Compare commits
746 Commits
2.0.0-alph
...
2.0.0-rc.1
Author | SHA1 | Date | |
---|---|---|---|
6eac4b68bc | |||
fcbfacb6d5 | |||
b600915953 | |||
905f38acb8 | |||
38f4c5f155 | |||
509f4ec611 | |||
27a7b51d99 | |||
a033f8335b | |||
b98c9e74e1 | |||
9f784dcc5a | |||
b625f2471a | |||
3aa2606ff1 | |||
89704e0f93 | |||
908a102a87 | |||
dd6e0cf1b5 | |||
52a6ba7ed9 | |||
43e0fa513b | |||
de978229b2 | |||
ba62fe974b | |||
3a40cb1a85 | |||
883e0c48b1 | |||
18b6a55764 | |||
00d3b6083c | |||
c386fc8379 | |||
43527172ed | |||
b88384eed8 | |||
107016ec12 | |||
d930ad1816 | |||
072446aed3 | |||
2e1f3f003d | |||
fdd8bd1a36 | |||
7db911fdd4 | |||
b6fd81169b | |||
3ae856ab8b | |||
ce5b37239e | |||
3e17c99f4e | |||
bb8976608d | |||
cd52318f48 | |||
2570b72158 | |||
6e79de794c | |||
c4be30d2e8 | |||
57240c85a5 | |||
a66cdb469f | |||
505da6c0a8 | |||
4fe0f1fa65 | |||
ec4ca0eace | |||
db95fd6ca9 | |||
ca13f1c024 | |||
277b1fc473 | |||
8836219b16 | |||
6f5e3f9390 | |||
a84c2d7fee | |||
f114d6c560 | |||
163d80adb7 | |||
9e05814212 | |||
ab56be46e1 | |||
6a0cbb8a57 | |||
4e2c68354e | |||
62a0809e81 | |||
76d6f5fa0d | |||
0f1b370117 | |||
e589f9949b | |||
0f774df811 | |||
351f24e8eb | |||
aecb60a604 | |||
4d691b61ee | |||
11955f9b13 | |||
5ff31f0713 | |||
deba804671 | |||
e5b87e55da | |||
d097784d57 | |||
15f6b27ae0 | |||
176e55927c | |||
365be6a309 | |||
713e6d4aff | |||
a8e277b067 | |||
3aa322a9c6 | |||
a02614beaa | |||
d2527b504a | |||
46cd868827 | |||
b1a9e445b3 | |||
5297c9d9cc | |||
ee7caceec7 | |||
a0b5964a63 | |||
cacdead96d | |||
96ae348648 | |||
78946fe9fa | |||
33e53c9a59 | |||
c493d88405 | |||
8bf6ef6544 | |||
ca40ef5ac7 | |||
7c0d4976b1 | |||
30de2db349 | |||
602641dffd | |||
560cc14d97 | |||
de56dd5f30 | |||
fa5bfe4b64 | |||
446657bdd5 | |||
79830f1c75 | |||
6e1fed42b7 | |||
d35c109cb9 | |||
fad3b6434c | |||
073ec0a7eb | |||
70b23ae2ca | |||
769835e53e | |||
ac55e1e27b | |||
0700c8a252 | |||
c79e657fcd | |||
d9648887b8 | |||
35cd0ded22 | |||
d2efac18ed | |||
ff2ae7a2e1 | |||
e1058a4d8a | |||
d327ac4b43 | |||
1ad2a02b11 | |||
1e8864c4a5 | |||
c209836fd0 | |||
7d1b6af073 | |||
140a878a3d | |||
b62bccf254 | |||
39eb34739a | |||
969b55326c | |||
2c8371654a | |||
5a897cf299 | |||
ef67a0c57f | |||
ef6163e652 | |||
f6985671dd | |||
90a1f7d5c4 | |||
3e114227f8 | |||
6103aa0a46 | |||
b7c6feff28 | |||
b48d907697 | |||
676ddfa065 | |||
c8d00dc191 | |||
0b6865d6c6 | |||
67d05eb65f | |||
152a117d5c | |||
d6626309fd | |||
c3daccd83b | |||
8db62151d2 | |||
bab81a9831 | |||
cc86fee1d1 | |||
386cc5dbb6 | |||
2f7045720a | |||
9889c21aaa | |||
e69cb40de3 | |||
12837e1c17 | |||
9092ac79d4 | |||
0a7d10ba55 | |||
efbd446d18 | |||
c06b0a2371 | |||
0c600cf6e3 | |||
41404057cf | |||
f4e6994634 | |||
b602bd8c83 | |||
30c43521d3 | |||
45f5df371d | |||
43e31c5abb | |||
13c8b13343 | |||
0fc9ec248e | |||
d094a85647 | |||
22c05b0834 | |||
8490921fb3 | |||
ecb9bb96f0 | |||
75463cd8df | |||
c6244d1470 | |||
22ae2d0976 | |||
88b0a239c4 | |||
7c9717bba8 | |||
7f297666ca | |||
d99823e2fd | |||
bb9fb21fac | |||
b64672b23c | |||
930f58718b | |||
2b34c88b69 | |||
45f09ba686 | |||
bb62905bef | |||
7bc9b19418 | |||
e9f7a00910 | |||
a5d6b6db8b | |||
fc496813e2 | |||
2abb414cfb | |||
0e56aaf189 | |||
3412aba46e | |||
347e71af7d | |||
d24df799d3 | |||
01e6b8c7ed | |||
60727c4d2b | |||
03627aa84d | |||
83b8f59297 | |||
c6f454f51d | |||
ccff17599a | |||
fb2773b8f3 | |||
5110121f6e | |||
27cf897239 | |||
5d33a12af4 | |||
08b295603c | |||
3b60503d2b | |||
3c2473bac6 | |||
f9426709ef | |||
e1e44a910e | |||
f371c9066d | |||
85c1927993 | |||
a596b887ff | |||
6cbf99086e | |||
26a3390549 | |||
9a1959f77a | |||
226e662cf1 | |||
7a1a1b80ed | |||
529988bc81 | |||
c17dc1c057 | |||
09a95a692e | |||
247964af62 | |||
5e2bc5c593 | |||
28e657d857 | |||
06ad112998 | |||
cfa1d17afe | |||
3ca6df87b8 | |||
e310bee9e2 | |||
4902244cce | |||
8db97b0b7a | |||
9be04f8d38 | |||
74e2bd7e3e | |||
52d3980d02 | |||
4e9809bcb2 | |||
bd8a4215dd | |||
d23b973e7a | |||
0dbf959548 | |||
20812f446f | |||
27a4d0ce11 | |||
9dec4c7485 | |||
90c87fa6ad | |||
291928feb1 | |||
c9c52fb353 | |||
0bcfcde63d | |||
1c20a62611 | |||
8430927e6b | |||
d2ca7d81c8 | |||
756121acc1 | |||
d7e1175df0 | |||
66cd84e0d5 | |||
506f4ce1e5 | |||
a0387d2835 | |||
9bdd5951d9 | |||
111afcdff1 | |||
85f3dc2fb5 | |||
430f367c2f | |||
d272f96e23 | |||
73a84a7098 | |||
17c8ec8a5d | |||
a1880c3576 | |||
91999e016e | |||
aa966f5de2 | |||
3e593b8221 | |||
440aca86a3 | |||
09f4d6f52d | |||
3f57fa6e0e | |||
ae876d1317 | |||
6de68e2f1f | |||
49527ab495 | |||
0898bca939 | |||
d940387beb | |||
27c45b05cf | |||
90f09d551f | |||
3739588e97 | |||
0730b753f2 | |||
cad693de0f | |||
bf911fc992 | |||
fb6d791ce9 | |||
0f8efce799 | |||
69c1405196 | |||
980491b08f | |||
72e24663ad | |||
363ed5140e | |||
b0f585ab08 | |||
8b67b07580 | |||
5c330ea492 | |||
06eaaf0ac5 | |||
9820271243 | |||
b6507e37ef | |||
c194f6695d | |||
967ae3e1b8 | |||
a5e6eaaebc | |||
d4e9b55fb6 | |||
048bd280dd | |||
8326ab3240 | |||
a7fe983be2 | |||
e1f8e54e34 | |||
2b165944ea | |||
ea11b3f1f8 | |||
3bd87147ab | |||
2f581ffc88 | |||
d61aaac400 | |||
310620fd12 | |||
f9fb72fb0e | |||
095db673c5 | |||
70d18b5b53 | |||
f1796d67f4 | |||
cb38d72ff4 | |||
b72bab49aa | |||
201475e8d8 | |||
c25b9fcf97 | |||
8755a8e188 | |||
127fbfd5a6 | |||
f33dda79e9 | |||
293fa5505b | |||
df1f78e302 | |||
43bb31c6c6 | |||
169869a195 | |||
b691da26af | |||
8e3e45097a | |||
aa43d2f87b | |||
128acbb6eb | |||
5824866a83 | |||
0d58b137a7 | |||
b5c769e1e4 | |||
7f22bd62ab | |||
83f0e7c975 | |||
adef68b4d6 | |||
14f0e9ada8 | |||
ef9e40e82b | |||
41e38e4330 | |||
2c7c3e3c69 | |||
756f5d884f | |||
45fd6f0a41 | |||
75ae4a9159 | |||
37d18d0112 | |||
773fe8f8c5 | |||
4da2b19ea0 | |||
9f3547e35d | |||
5a79358727 | |||
85bfbc13c1 | |||
1a01af9e68 | |||
dd95e901df | |||
9782d8c32e | |||
80764c6f71 | |||
d9e78e4fa8 | |||
10fedd0dfc | |||
315e73c47c | |||
912717ff31 | |||
b857fd1eeb | |||
6dce4f49c2 | |||
15e16148f4 | |||
ae49085481 | |||
11e8aa26f6 | |||
81beb1c788 | |||
6402d61f69 | |||
5a59e44765 | |||
7455b907d1 | |||
579b890446 | |||
19a08f3a43 | |||
a3d7629134 | |||
bc9644e86e | |||
a10c02cb41 | |||
9936e347ff | |||
7d44b8230e | |||
75343eb340 | |||
2548ce86db | |||
5586c29492 | |||
1174473e9c | |||
1d49b3e36b | |||
2830df4190 | |||
143cf89b5f | |||
69c1694900 | |||
01fe7f5fac | |||
9aedef208f | |||
39b6e0efba | |||
f60fa14767 | |||
d900f5c075 | |||
391a9edabb | |||
28a78117eb | |||
eeb594c010 | |||
0bb10d6bb6 | |||
59629a0801 | |||
b5e6319fa9 | |||
c9a3df970b | |||
f72f137261 | |||
ee3c580e88 | |||
05c185a7b1 | |||
b47f80ec76 | |||
ebd438ff5e | |||
331b9c1317 | |||
4a93f58b8b | |||
ebe531bf92 | |||
1779caf5f8 | |||
6ef2121e6a | |||
38cb526f60 | |||
f6a8d04c32 | |||
4b3b5d7c53 | |||
abff302e52 | |||
e1f6679c75 | |||
aaafdf03ce | |||
ee298baa1b | |||
d1abada5b7 | |||
ab36ea097b | |||
8bb66a5eb3 | |||
cfc1e56dd8 | |||
a1c3be21ec | |||
edad8e3f56 | |||
d4a4d81173 | |||
e7470d557d | |||
b634a25ae0 | |||
c1a0af514f | |||
c6afea61f1 | |||
ce10fe92b2 | |||
b81b1fb81c | |||
280b86ec55 | |||
2f5a2ba671 | |||
c45ec6f1be | |||
530470e0ce | |||
ce72ccf9e8 | |||
46d9c87ddc | |||
d736c31fea | |||
a7e9bc97f6 | |||
265703b950 | |||
ae275fa4e4 | |||
3478d5d450 | |||
e72dc16dbe | |||
40a043275d | |||
f161b5cc28 | |||
117d57e121 | |||
3dcce706fd | |||
efb89b83e1 | |||
3d96c2337f | |||
19cfb4eb12 | |||
3d715a2f7b | |||
c7261c295c | |||
1a26f8edd6 | |||
fc887774da | |||
7cbf88a691 | |||
1cb1c139cf | |||
1fd924f7d5 | |||
eb688f2c8e | |||
61cf499b0b | |||
f1f5b45361 | |||
50548fb565 | |||
8f47aa3530 | |||
df7885c9f5 | |||
0f10624b08 | |||
6f1ef33e32 | |||
231773ea76 | |||
e725542703 | |||
2337469753 | |||
55122cd57a | |||
7e0f02f96e | |||
e7ad03cba6 | |||
74be3d3fde | |||
a15ca23469 | |||
de77700da0 | |||
e73fee7156 | |||
72ab35bceb | |||
0f22dce036 | |||
c6036435f0 | |||
d86be245b8 | |||
a26053d3ff | |||
24d5b665e1 | |||
aa98fad338 | |||
9cb6dbbbab | |||
e21718faa9 | |||
b0f7d59e64 | |||
b86829f492 | |||
22929a1671 | |||
86c40f8474 | |||
16b521794c | |||
2a70f4e4c7 | |||
2f31c4c1c5 | |||
1435763383 | |||
05238df89b | |||
772d60d9fe | |||
24086bf0bb | |||
9b0e10e9a7 | |||
995a9e0cf8 | |||
b55f1764b5 | |||
5e9daed2e8 | |||
aa8c5aa2e2 | |||
f2c7946cca | |||
da1fcfd820 | |||
dbeff6f548 | |||
26e60d658a | |||
c2ceb7fba4 | |||
4bfe49cd42 | |||
cee2318110 | |||
cfef76f683 | |||
f56df65d48 | |||
3a40cd79f0 | |||
6acc99729c | |||
99e6500a2d | |||
5c782d6ba8 | |||
4e43d6f769 | |||
3529ee9973 | |||
29aa6a6c1c | |||
7918f3c1fc | |||
2f4e176054 | |||
d4565fdaf3 | |||
2a302aa73a | |||
31b819e9c2 | |||
27daeaff5e | |||
3e9b532409 | |||
c5aa6d17ef | |||
e480b0798e | |||
8a645d5e44 | |||
321193889f | |||
566d3ede04 | |||
8c36aa866a | |||
ed2dbf2db7 | |||
36a0e04604 | |||
8867afdaab | |||
a199772508 | |||
b008f542fa | |||
a78dcfa5f3 | |||
e1bf3d33f8 | |||
ae7d2ab515 | |||
c6adbf602c | |||
1f7a41c963 | |||
f4f614f3a9 | |||
94139c351f | |||
fc5b128b43 | |||
68a799af2e | |||
16d9c60a0e | |||
c0b5e7a672 | |||
6932b29acb | |||
c2a38c05aa | |||
8bea667a0b | |||
800c8f196f | |||
42231f5719 | |||
db87baeb98 | |||
c4c43f5a77 | |||
0ae77753f3 | |||
5f0baaac73 | |||
b5b6ece65a | |||
4282297c24 | |||
9c96b8affc | |||
132829e5e2 | |||
4a414420e9 | |||
fb6335ab60 | |||
89bd008445 | |||
caafb41eb5 | |||
31b81a7439 | |||
f7b1973358 | |||
32f01da49a | |||
59684c97b0 | |||
a32a0a3a97 | |||
96f5b0929d | |||
8e6cf7fca8 | |||
fdbe8741c9 | |||
775fb2c340 | |||
b60f594798 | |||
cc49790bdb | |||
a4bc19c530 | |||
f7985dbdb7 | |||
0bdcb5c1e0 | |||
a0d25db4a5 | |||
625474c4e2 | |||
1cd2a6328a | |||
d6bafe4fe3 | |||
6a2ef15355 | |||
a8ca560503 | |||
ad361808ec | |||
c47639f2b1 | |||
ba90a85f7b | |||
d3b569557f | |||
c9090ffa31 | |||
341bf39d23 | |||
3778ac26aa | |||
6cfc6f5bb2 | |||
47a3b4d56b | |||
c72ed991ad | |||
78bfdf78ea | |||
a24ee6add4 | |||
df3074fdfe | |||
f7424d5aeb | |||
a593ffa6f3 | |||
761c6d0df7 | |||
3e65d1458e | |||
a4b5cb8376 | |||
c785a1e474 | |||
3adc472f06 | |||
e7081b8b7c | |||
9b3a548f6f | |||
90b3502bb8 | |||
e19b31db29 | |||
bd015f14e8 | |||
ca7ba12fc6 | |||
ae05ec69c4 | |||
92dc3b91d8 | |||
8bd697b316 | |||
eda4c3eb4c | |||
4d0c2ed1f6 | |||
eda6a5d52a | |||
c1c54ed0f2 | |||
6b73d09ba1 | |||
ac85cbb28a | |||
b0cebdba6b | |||
933a9112da | |||
8c37b7e8f2 | |||
c8e909f8c9 | |||
69ae3634c7 | |||
95248f46a1 | |||
b3c7df1783 | |||
c56679e8e1 | |||
041c599511 | |||
6343f71be5 | |||
89f32f808f | |||
7ae23adaff | |||
a08f50badd | |||
0b6e75a85e | |||
4291758079 | |||
b44d36cf95 | |||
a038bb9ae3 | |||
9d28147acb | |||
d116861c8e | |||
9a70f1a1d9 | |||
8516473340 | |||
cab69f689f | |||
822e83ebb0 | |||
b2bc50dbd1 | |||
e748adda2e | |||
630d93150a | |||
76f1f9f1e3 | |||
c47d85b038 | |||
197cf09689 | |||
e67ebb7f70 | |||
3524946581 | |||
9276dad42c | |||
2a2f9a9a19 | |||
909e70bd61 | |||
8ac9719832 | |||
3eff7c6f59 | |||
17fbbfba91 | |||
b232dded77 | |||
f50affaf9c | |||
463754bf16 | |||
a45b27e7f9 | |||
af3ea16acb | |||
fc75220d63 | |||
00822c3415 | |||
3dca9d522a | |||
de996ec50b | |||
059e09c3be | |||
d5e4686e7e | |||
d55655f5a3 | |||
61b9468596 | |||
c6f52e3282 | |||
42ccff859c | |||
29600c0c87 | |||
5b63b6764f | |||
2835265916 | |||
e950dd6a2a | |||
321ed7d099 | |||
42b74f524a | |||
77b7caeceb | |||
b803ecf7e7 | |||
d3a79db48d | |||
e891baeea4 | |||
2aaef81b1b | |||
3191fd1440 | |||
80a5e47e61 | |||
5a04ffec3e | |||
1c779d8b9e | |||
a79fe057f9 | |||
b88a6d983f | |||
19396d14cc | |||
2983558e5e | |||
0f8e40bb42 | |||
006a96dd20 | |||
4deaf0bdd3 | |||
9917d7f8af | |||
1607ef8782 | |||
539c5633bf | |||
f83f4ace2d | |||
283962f810 | |||
892f9e19bc | |||
cacfb214b6 | |||
4e5cd1e558 | |||
43f42d9c6e | |||
a885f37dfa | |||
ed2c25eb2f | |||
b1b0593ddf | |||
200dc00dbb | |||
20c6eebb29 | |||
8c69497d8e | |||
44c648fc04 | |||
979162d324 | |||
2934b82113 | |||
92ddc62bed | |||
0cb32c2fef | |||
2ca5e38a78 | |||
b8e69a21a9 | |||
3fd898e91f | |||
a66cc50168 | |||
eb296756fb | |||
f1a9a537cb | |||
080469f8e6 | |||
7c13372721 | |||
778677ba75 | |||
f0d876a873 | |||
e9e2a4152e | |||
398f024b24 | |||
4a17e6906c | |||
8d3e5596dc | |||
df6d2d1e23 | |||
d26c338aa0 | |||
edcb34dc9f | |||
693d9dce5d | |||
194dc7da78 | |||
79399e1c51 | |||
fe1dd77d94 | |||
24dcd267b8 | |||
cf3ce171a5 | |||
06d076a6f2 | |||
3190c5941a | |||
30e25acb9f | |||
4975cb92ae | |||
c8d6ad2718 | |||
cde6726e25 | |||
b3d10af89a | |||
4724cc664e | |||
4e16feaf72 | |||
9e44dd85ad | |||
da9b46a071 | |||
b386d1134a | |||
1e740581ee | |||
f1741b10f2 | |||
7bce1477ef | |||
01ba94ba56 | |||
51cb7586e0 | |||
796eee1e6f | |||
c39828f0f2 | |||
28860d35b2 | |||
5f0ce30ee6 | |||
f4d937ad8d | |||
db096a5e22 | |||
bf484b19b3 | |||
56a254e6a5 | |||
793098bcce | |||
d6d759d722 | |||
61e8b60506 | |||
2f0744b089 | |||
ca73852746 | |||
72444c40a7 | |||
214148d58a | |||
cc8f1f9552 | |||
cbf788869d | |||
c1ae49d91e | |||
4432cf5438 | |||
869a392357 |
16
.github/ISSUE_TEMPLATE.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
**IMPORTANT**: This repository's issues are reserved for feature requests and bug reports. Do not submit support requests here, see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question.
|
||||
|
||||
|
||||
**Steps to reproduce and a minimal demo of the problem**
|
||||
|
||||
_Use https://plnkr.co or similar -- try this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5_
|
||||
|
||||
_What steps should we try in your demo to see the problem?_
|
||||
|
||||
**Current behavior**
|
||||
|
||||
|
||||
**Expected/desired behavior**
|
||||
|
||||
|
||||
**Other information**
|
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
* **Please check if the PR fulfills these requirements**
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit-message-format
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
|
||||
* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
||||
|
||||
|
||||
|
||||
* **What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
|
||||
|
||||
* **What is the new behavior (if this is a feature change)?**
|
||||
|
||||
|
||||
|
||||
* **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
||||
|
||||
|
||||
|
||||
* **Other information**:
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -21,7 +21,13 @@ tmp
|
||||
*.js.deps
|
||||
*.js.map
|
||||
|
||||
# Files created by the template compiler
|
||||
**/*.ngfactory.ts
|
||||
**/*.css.ts
|
||||
**/*.css.shim.ts
|
||||
|
||||
# Or type definitions we mirror from github
|
||||
# (NB: these lines are removed in publish-build-artifacts.sh)
|
||||
**/typings/**/*.d.ts
|
||||
**/typings/tsd.cached.json
|
||||
|
||||
@ -48,3 +54,6 @@ npm-debug.log
|
||||
|
||||
# built dart payload tests
|
||||
/modules_dart/payload/**/build
|
||||
|
||||
# rollup-test output
|
||||
/modules/rollup-test/dist/
|
||||
|
272
.travis.yml
272
.travis.yml
@ -1,132 +1,192 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '4.2.1'
|
||||
- '5.4.1'
|
||||
|
||||
addons:
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
# needed to install g++ that is used by npms's native modules
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3sync
|
||||
- g3_v2_0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- $HOME/.pub-cache
|
||||
- ./node_modules
|
||||
- ./.chrome/chromium
|
||||
# - $HOME/.pub-cache
|
||||
|
||||
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
|
||||
env:
|
||||
global:
|
||||
- KARMA_BROWSERS=DartiumWithWebPlatform
|
||||
- E2E_BROWSERS=Dartium
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- BROWSER_STACK_USERNAME=angularteam1
|
||||
- BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
- ARCH=linux-x64
|
||||
- DART_DEV_VERSION=latest
|
||||
- DART_STABLE_VERSION=latest
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
||||
# because those are not visible for pull requests, and those should also be reliable.
|
||||
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# (password is in Valentine)
|
||||
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - ARCH=linux-x64
|
||||
|
||||
matrix:
|
||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
- MODE=dart DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=browserstack DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=js DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=router DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=build_only DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=payload DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- CI_MODE=js
|
||||
- CI_MODE=lint
|
||||
- CI_MODE=e2e
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=browserstack DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
# TODO(alxhub): remove when dartdoc #1039 is in dev channel
|
||||
- env: "MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
#matrix:
|
||||
# allow_failures:
|
||||
# - env: "MODE=saucelabs_optional"
|
||||
# - env: "MODE=browserstack_optional"
|
||||
|
||||
addons:
|
||||
firefox: "38.0"
|
||||
|
||||
before_install:
|
||||
- node tools/analytics/build-analytics start ci job
|
||||
- node tools/analytics/build-analytics start ci before_install
|
||||
- echo ${TSDRC} > .tsdrc
|
||||
- export DISPLAY=:99.0
|
||||
- export GIT_SHA=$(git rev-parse HEAD)
|
||||
- ./scripts/ci/init_android.sh
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
- node tools/analytics/build-analytics success ci before_install
|
||||
|
||||
install:
|
||||
- node tools/analytics/build-analytics start ci install
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules || true
|
||||
# Install npm dependecies
|
||||
- npm install
|
||||
- node tools/analytics/build-analytics success ci install
|
||||
- ./scripts/ci-lite/install.sh
|
||||
|
||||
before_script:
|
||||
- node tools/analytics/build-analytics start ci before_script
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./scripts/ci/presubmit-queue-setup.sh
|
||||
- node tools/analytics/build-analytics success ci before_script
|
||||
|
||||
|
||||
script:
|
||||
- node tools/analytics/build-analytics start ci script
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
- node tools/analytics/build-analytics success ci script
|
||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||
|
||||
after_script:
|
||||
- node tools/analytics/build-analytics start ci after_script
|
||||
- ./scripts/ci/print-logs.sh
|
||||
- ./scripts/ci/after-script.sh
|
||||
- ./scripts/publish/publish-build-artifacts.sh
|
||||
- node tools/analytics/build-analytics success ci after_script
|
||||
- if [[ $TRAVIS_TEST_RESULT -eq 0 ]]; then node tools/analytics/build-analytics success ci job; else node tools/analytics/build-analytics error ci job; fi
|
||||
- ./scripts/ci-lite/cleanup.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# trigger Buildtime Trend Service to parse Travis CI log
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
||||
slack:
|
||||
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
||||
deploy:
|
||||
- provider: gcs
|
||||
# This is for project angular-github-babysitter
|
||||
access_key_id: GOOGIOQTDBEOPBUAWFZQ
|
||||
secret_access_key:
|
||||
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
|
||||
# this bucket has a lifecycle to delete after 90 days:
|
||||
# $ echo '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 90}}]}' > lifecycle.json
|
||||
# $ gsutil lifecycle set lifecycle.json gs://angular2-snapshots
|
||||
bucket: angular2-snapshots
|
||||
# don't delete generated files
|
||||
skip_cleanup: true
|
||||
# serve to public at https://storage.googleapis.com/angular2-snapshots/SHA/dist.tgz
|
||||
acl: public-read
|
||||
# upload the .tgz archive created in scripts/ci/build_and_test.sh
|
||||
local-dir: deploy
|
||||
# create a "subdirectory" for each commit
|
||||
upload-dir: $TRAVIS_COMMIT
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = build_only"
|
||||
#branches:
|
||||
# except:
|
||||
# - g3_v2_0
|
||||
#
|
||||
#cache:
|
||||
# directories:
|
||||
# - $HOME/.pub-cache
|
||||
# - $HOME/.chrome/chromium
|
||||
#
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
#
|
||||
#env:
|
||||
# global:
|
||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
# - CXX=g++-4.8
|
||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - SAUCE_USERNAME=angular-ci
|
||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
# - BROWSER_STACK_USERNAME=angularteam1
|
||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
# - ARCH=linux-x64
|
||||
# - DART_DEV_VERSION=latest
|
||||
# - DART_STABLE_VERSION=latest
|
||||
# - DART_CHANNEL=stable
|
||||
# - DART_VERSION=$DART_STABLE_VERSION
|
||||
# # Token for tsd to increase github rate limit
|
||||
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
||||
# # because those are not visible for pull requests, and those should also be reliable.
|
||||
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# # (password is in Valentine)
|
||||
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
||||
# # GITHUB_TOKEN_ANGULAR
|
||||
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
# matrix:
|
||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
# - MODE=dart
|
||||
# - MODE=dart DART_CHANNEL=dev
|
||||
# - MODE=saucelabs_required
|
||||
# - MODE=browserstack_required
|
||||
# - MODE=saucelabs_optional
|
||||
# - MODE=browserstack_optional
|
||||
# - MODE=dart_ddc
|
||||
# - MODE=js
|
||||
# - MODE=router
|
||||
# - MODE=build_only
|
||||
# - MODE=typescript_next
|
||||
# - MODE=lint
|
||||
#
|
||||
#matrix:
|
||||
# allow_failures:
|
||||
# - env: "MODE=saucelabs_optional"
|
||||
# - env: "MODE=browserstack_optional"
|
||||
#
|
||||
#addons:
|
||||
# firefox: "38.0"
|
||||
# apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
# packages:
|
||||
# - g++-4.8
|
||||
#
|
||||
#before_install:
|
||||
# - node tools/analytics/build-analytics start ci job
|
||||
# - node tools/analytics/build-analytics start ci before_install
|
||||
# - echo ${TSDRC} > .tsdrc
|
||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
||||
# - export DISPLAY=:99.0
|
||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
||||
# - ./scripts/ci/init_android.sh
|
||||
# - sh -e /etc/init.d/xvfb start
|
||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
# - node tools/analytics/build-analytics success ci before_install
|
||||
#
|
||||
#install:
|
||||
# - node tools/analytics/build-analytics start ci install
|
||||
# # Install version of npm that we are locked against
|
||||
# - npm install -g npm@3.5.3
|
||||
# # Install version of Chromium that we are locked against
|
||||
# - ./scripts/ci/install_chromium.sh
|
||||
# # Install version of Dart based on the matrix build variables
|
||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
# # Print the size of caches to ease debugging
|
||||
# - du -sh ./node_modules || true
|
||||
# # Install npm dependecies
|
||||
# # check-node-modules will exit(1) if we don't need to install
|
||||
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
||||
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
||||
# - node tools/analytics/build-analytics success ci install
|
||||
#
|
||||
#before_script:
|
||||
# - node tools/analytics/build-analytics start ci before_script
|
||||
# - mkdir -p $LOGS_DIR
|
||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
||||
# - node tools/analytics/build-analytics success ci before_script
|
||||
#
|
||||
#script:
|
||||
# - node tools/analytics/build-analytics start ci script
|
||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
||||
# - node tools/analytics/build-analytics success ci script
|
||||
#
|
||||
#after_script:
|
||||
# - node tools/analytics/build-analytics start ci after_script
|
||||
# - ./scripts/ci/print-logs.sh
|
||||
# - ./scripts/ci/after-script.sh
|
||||
# - ./scripts/publish/publish-build-artifacts.sh
|
||||
# - node tools/analytics/build-analytics success ci after_script
|
||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
||||
#
|
||||
#notifications:
|
||||
# webhooks:
|
||||
# urls:
|
||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
||||
# - https://buildtimetrend.herokuapp.com/travis
|
||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
||||
# on_success: always # options: [always|never|change] default: always
|
||||
# on_failure: always # options: [always|never|change] default: always
|
||||
# on_start: never # default: never
|
||||
# slack:
|
||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
1072
CHANGELOG.md
1072
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -11,8 +11,11 @@ Someone with committer access will do the rest.
|
||||
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
||||
Angular committers and also prevent breakages on master.
|
||||
|
||||
When a PR is ready to merge, a project member in the CoreTeamMember list (see below) can add the special label,
|
||||
`PR: merge`.
|
||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
||||
so the caretaker reviews the changes manually.
|
||||
|
||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
||||
is notified that the label was added by an authorized person,
|
||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
||||
@ -26,6 +29,6 @@ Finally, after merge `mary-poppins` removes the presubmit branch.
|
||||
|
||||
## Administration
|
||||
|
||||
The list of users who can trigger a merge by adding the label is stored in our appengine app datastore.
|
||||
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
|
||||
Edit the contents of the [CoreTeamMember Table](
|
||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
||||
|
@ -48,14 +48,18 @@ features, by not reporting duplicate issues. Providing the following informatio
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
|
||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps.
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
|
||||
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
@ -176,7 +180,8 @@ Must be one of the following:
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
* **build** Changes that affect the build system, CI configuration or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
* **chore**: Other changes that don't modify `src` or `test` files
|
||||
|
||||
### Scope
|
||||
|
44
DEVELOPER.md
44
DEVELOPER.md
@ -9,7 +9,7 @@ JS and Dart versions. It also explains the basic mechanics of using `git`, `node
|
||||
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
* [Build commands](#build-commands)
|
||||
* [Running Tests Locally](#running-tests-locally)
|
||||
* [Formatting](#clang-format)
|
||||
* [Code Style](#code-style)
|
||||
* [Project Information](#project-information)
|
||||
* [CI using Travis](#ci-using-travis)
|
||||
* [Transforming Dart code](#transforming-dart-code)
|
||||
@ -23,7 +23,16 @@ if you'd like to contribute to Angular.
|
||||
Before you can build and test Angular, you must install and configure the
|
||||
following products on your development machine:
|
||||
|
||||
* [Dart](https://www.dartlang.org) (version ` >=1.12.0 <2.0.0`), specifically the Dart-SDK and
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(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). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
||||
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
||||
@ -33,19 +42,6 @@ following products on your development machine:
|
||||
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).`
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org), (version `>=4.2.1 <5`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(version `>=2.14.7 <3.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
|
||||
bleeding edge functionality, built especially for developers (and early adopters).
|
||||
|
||||
* [Bower](http://bower.io/).
|
||||
|
||||
|
||||
## Getting the Sources
|
||||
@ -200,15 +196,15 @@ Then, in another terminal:
|
||||
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.(saucelabs|browserstack) --browsers=option1,option2,..,optionN`
|
||||
- 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.saucelabs --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
||||
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.saucelabs --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
||||
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
|
||||
@ -231,7 +227,9 @@ Angular specific command line options when running protractor:
|
||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
## Formatting with <a name="clang-format">clang-format</a>
|
||||
## 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
|
||||
@ -277,6 +275,14 @@ to some whitespace difference.
|
||||
* `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
|
||||
|
||||
```shell
|
||||
$ gulp lint
|
||||
```
|
||||
|
||||
## Generating the API documentation
|
||||
|
||||
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
|
||||
|
215
LICENSE
215
LICENSE
@ -1,202 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
The MIT License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2014-2016 Google, Inc. http://angular.io
|
||||
|
||||
1. Definitions.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
18
README.md
18
README.md
@ -1,31 +1,24 @@
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://badge.fury.io/js/angular2)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
|
||||
Angular
|
||||
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.
|
||||
|
||||
Angular 2 is currently in **Developer Preview**. We recommend using Angular 1.X for production
|
||||
applications:
|
||||
|
||||
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
|
||||
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
|
||||
Angular 2 is currently in **Beta**.
|
||||
|
||||
## Quickstart
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Setup & Install Angular 2
|
||||
|
||||
Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
|
||||
## Want to help?
|
||||
|
||||
@ -36,8 +29,7 @@ guidelines for [contributing][contributing] and then check out one of our issues
|
||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
[dart]: http://www.dartlang.org
|
||||
[dartium]: http://www.dartlang.org/tools/dartium
|
||||
[download]: http://angular.io/download/
|
||||
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
|
||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
|
@ -21,7 +21,7 @@ By default the debugging tools are disabled.
|
||||
Enable the debugging tools as follows:
|
||||
|
||||
```dart
|
||||
import 'package:angular2/tools.dart';
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
|
||||
main() async {
|
||||
var appRef = await bootstrap(Application);
|
||||
|
@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
|
||||
kinds of computation. Example:
|
||||
|
||||
```typescript
|
||||
@View({
|
||||
@Component({
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
})
|
||||
class FancyButton {
|
||||
|
@ -1,3 +1,33 @@
|
||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
|
||||
// If the target is set to null, then the browser is not run anywhere during CI.
|
||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// FirefoxBeta should be required:true
|
||||
// https://github.com/angular/angular/issues/7560
|
||||
'FirefoxBeta': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: false}},
|
||||
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'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: true}, 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}},
|
||||
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
// TODO(mlaval): iOS9 deactivated as not reliable, reactivate after https://github.com/angular/angular/issues/5408
|
||||
'iOS9': { unitTest: {target: null, required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
var customLaunchers = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
@ -8,7 +38,7 @@ var customLaunchers = {
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '46'
|
||||
version: '50'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -23,7 +53,7 @@ var customLaunchers = {
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '42'
|
||||
version: '45'
|
||||
},
|
||||
'SL_FIREFOXBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -47,7 +77,7 @@ var customLaunchers = {
|
||||
platform: 'OS X 10.10',
|
||||
version: '8'
|
||||
},
|
||||
'SL_SAFARI9.0': {
|
||||
'SL_SAFARI9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11',
|
||||
@ -119,7 +149,7 @@ var customLaunchers = {
|
||||
platform: 'Linux',
|
||||
version: '4.4'
|
||||
},
|
||||
'SL_ANDROID5.1': {
|
||||
'SL_ANDROID5': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
@ -239,21 +269,18 @@ var customLaunchers = {
|
||||
}
|
||||
};
|
||||
|
||||
// iOS9 deactivated as not reliable in both providers
|
||||
// TODO(mlaval): reactivate after https://github.com/angular/angular/issues/5408
|
||||
|
||||
var sauceAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9.0'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1'],
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9.0'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||
'CI': ['SL_CHROME',' SL_FIREFOX', 'SL_CHROMEDEV', 'SL_FIREFOXBETA', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE',
|
||||
'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1']
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false)
|
||||
};
|
||||
|
||||
var browserstackAliases = {
|
||||
@ -264,16 +291,23 @@ var browserstackAliases = {
|
||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'CI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_IOS7', 'BS_IOS8', 'BS_WINDOWSPHONE']
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
customLaunchers: customLaunchers,
|
||||
sauceAliases: sauceAliases,
|
||||
browserstackAliases: browserstackAliases
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
|
||||
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => {
|
||||
return target + '_' + item.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
112
build.sh
Executable file
112
build.sh
Executable file
@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/ng2tc -p ${TSCONFIG} ====="
|
||||
rm -rf ./dist/all/
|
||||
mkdir ./dist/all/
|
||||
|
||||
# prepare all files for e2e tests
|
||||
cp -r ./modules/playground ./dist/all/
|
||||
cp -r ./modules/playground/favicon.ico ./dist/
|
||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||
mkdir ./dist/all/playground/vendor
|
||||
cd ./dist/all/playground/vendor
|
||||
ln -s ../../../../node_modules/es6-shim/es6-shim.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs/bundles/Rx.js .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
cd -
|
||||
|
||||
# compile ts code
|
||||
$(npm bin)/ng2tc -p ${TSCONFIG}
|
||||
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
|
||||
for PACKAGE in \
|
||||
core \
|
||||
compiler \
|
||||
common \
|
||||
platform-browser \
|
||||
platform-browser-dynamic \
|
||||
platform-server \
|
||||
http \
|
||||
router \
|
||||
router-deprecated \
|
||||
upgrade
|
||||
do
|
||||
SRCDIR=./modules/@angular/${PACKAGE}
|
||||
DESTDIR=./dist/packages-dist/${PACKAGE}
|
||||
UMDES6PATH=${DESTDIR}/esm/${PACKAGE}.umd.js
|
||||
UMDES5PATH=${DESTDIR}/${PACKAGE}.umd.js
|
||||
|
||||
|
||||
if [[ ${PACKAGE} == "router-deprecated" ]]; then
|
||||
echo "====== COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json
|
||||
else
|
||||
echo "====== COMPILING: \$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es5.json
|
||||
fi
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
|
||||
|
||||
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'
|
||||
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'
|
||||
fi
|
||||
|
||||
|
||||
if [[ ${PACKAGE} == "router-deprecated" ]]; then
|
||||
echo "====== (esm)COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json
|
||||
else
|
||||
echo "====== (esm)COMPILING: \$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$(npm bin)/ng2tc -p ${SRCDIR}/tsconfig-es2015.json
|
||||
fi
|
||||
|
||||
|
||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||
(
|
||||
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"
|
||||
|
||||
# workaround for https://github.com/rollup/rollup/issues/626
|
||||
if [[ ${TRAVIS} ]]; then
|
||||
sed -i "s/ class exports\./ class /g" ${DESTDIR}/esm/${PACKAGE}.umd.js
|
||||
else
|
||||
sed -i '' "s/ class exports\./ class /g" ${DESTDIR}/esm/${PACKAGE}.umd.js
|
||||
fi
|
||||
|
||||
$(npm bin)/tsc \
|
||||
--out ${UMDES5PATH} \
|
||||
--target es5 \
|
||||
--allowJs \
|
||||
${UMDES6PATH} \
|
||||
modules/\@angular/manual_typings/globals.d.ts \
|
||||
modules/\@angular/typings/es6-collections/es6-collections.d.ts \
|
||||
modules/\@angular/typings/es6-promise/es6-promise.d.ts
|
||||
rm ${UMDES6PATH}
|
||||
|
||||
cat ./modules/@angular/license-banner.txt > ${UMDES5PATH}.tmp
|
||||
cat ${UMDES5PATH} >> ${UMDES5PATH}.tmp
|
||||
mv ${UMDES5PATH}.tmp ${UMDES5PATH}
|
||||
|
||||
done
|
21
circle.yml
Normal file
21
circle.yml
Normal file
@ -0,0 +1,21 @@
|
||||
machine:
|
||||
node:
|
||||
version: 5.4.1
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
override:
|
||||
- npm install:
|
||||
environment:
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# This is not hidden using https://circleci.com/docs/fork-pr-builds#details
|
||||
# because those are not visible for pull requests, and those should also be reliable.
|
||||
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# (password is in Valentine)
|
||||
TSD_GITHUB_TOKEN: ef474500309daea53d5991b3079159a29520a40b
|
||||
|
||||
test:
|
||||
override:
|
||||
- npm run build
|
796
gulpfile.js
796
gulpfile.js
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
// This module provides a customFileHandler for karma
|
||||
// that serves files with urls like /packages_<timestamp>/...
|
||||
// with maximum cache.
|
||||
// We are using these urls when we spawn isolates
|
||||
// so that the isolates don't reload files every time.
|
||||
|
||||
var common = require('karma/lib/middleware/common');
|
||||
var fs = require('fs');
|
||||
|
||||
var DART_EVAL_PATH_RE = /.*\/packages_\d+\/(.*)$/;
|
||||
|
||||
module.exports = createFactory;
|
||||
|
||||
function createFactory(proxyPaths) {
|
||||
return {
|
||||
'framework:dart-evalcache': ['factory', dartEvalCacheFactory]
|
||||
};
|
||||
|
||||
function dartEvalCacheFactory(emitter, logger, customFileHandlers) {
|
||||
var filesPromise = new common.PromiseContainer();
|
||||
emitter.on('file_list_modified', function(files) {
|
||||
filesPromise.set(Promise.resolve(files));
|
||||
});
|
||||
|
||||
var serveFile = common.createServeFile(fs);
|
||||
var log = logger.create('dart-evalcache');
|
||||
|
||||
customFileHandlers.push({
|
||||
urlRegex: DART_EVAL_PATH_RE,
|
||||
handler: handler
|
||||
});
|
||||
|
||||
// See source_files handler
|
||||
function handler(request, response, fa, fb, basePath) {
|
||||
return filesPromise.then(function(files) {
|
||||
try {
|
||||
var requestedFilePath = mapUrlToFile(request.url, proxyPaths, basePath, log);
|
||||
// TODO(vojta): change served to be a map rather then an array
|
||||
var file = findByPath(files.served, requestedFilePath);
|
||||
if (file) {
|
||||
serveFile(file.contentPath || file.path, response, function() {
|
||||
common.setHeavyCacheHeaders(response);
|
||||
}, file.content);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end('Not found');
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e.stack);
|
||||
response.writeHead(500);
|
||||
response.end('Error', e.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mapUrlToFile(url, proxyPaths, basePath, log) {
|
||||
var originalUrl = url;
|
||||
url = url.indexOf('?') > -1 ? url.substring(0, url.indexOf('?')) : url;
|
||||
var match = DART_EVAL_PATH_RE.exec(url);
|
||||
var packagePath = match[1];
|
||||
var result = null;
|
||||
var lastProxyFromLength = 0;
|
||||
Object.keys(proxyPaths).forEach(function(proxyFrom) {
|
||||
if (startsWith(packagePath, proxyFrom) && proxyFrom.length > lastProxyFromLength) {
|
||||
lastProxyFromLength = proxyFrom.length;
|
||||
result = proxyPaths[proxyFrom] + packagePath.substring(proxyFrom.length);
|
||||
}
|
||||
});
|
||||
return basePath + '/' + result;
|
||||
}
|
||||
|
||||
function startsWith(string, subString) {
|
||||
return string.length >= subString.length && string.slice(0, subString.length) === subString;
|
||||
}
|
||||
|
||||
function findByPath(files, path) {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (files[i].path === path) {
|
||||
return files[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
|
||||
var packageSources = {
|
||||
// Dependencies installed with `pub install`.
|
||||
'unittest': 'packages/unittest',
|
||||
'guinness': 'packages/guinness',
|
||||
'matcher': 'packages/matcher',
|
||||
'stack_trace': 'packages/stack_trace',
|
||||
'collection': 'packages/collection',
|
||||
'path': 'packages/path',
|
||||
'observe': 'packages/observe',
|
||||
'quiver': 'packages/quiver',
|
||||
'intl': 'packages/intl',
|
||||
'smoke': 'packages/smoke',
|
||||
'logging': 'packages/logging',
|
||||
'utf': 'packages/utf',
|
||||
|
||||
// Local dependencies, transpiled from the source.
|
||||
'angular2': 'dist/dart/angular2/lib',
|
||||
'angular2/test/': 'dist/dart/angular2/test/',
|
||||
'http': 'dist/dart/http/lib',
|
||||
'angular2_material': 'dist/dart/angular2_material/lib',
|
||||
'benchpress': 'dist/dart/benchpress/lib',
|
||||
'examples': 'dist/dart/examples/lib'
|
||||
};
|
||||
|
||||
var proxyPaths = {};
|
||||
Object.keys(packageSources).map(function(packageName) {
|
||||
var filePath = packageSources[packageName];
|
||||
proxyPaths['/packages/'+packageName] = '/base/'+filePath;
|
||||
});
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
frameworks: ['dart-unittest', 'dart-evalcache'],
|
||||
|
||||
files: [
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-init.dart', included: true},
|
||||
// Unit test files needs to be included.
|
||||
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: false},
|
||||
|
||||
// Karma-dart via the dart-unittest framework generates
|
||||
// `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'dist/dart/**', included: false, watched: false},
|
||||
|
||||
// Dependencies, installed with `pub install`.
|
||||
{pattern: 'packages/**/*.dart', included: false, watched: false},
|
||||
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-main.dart', included: true},
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false},
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/dart/**/packages/**',
|
||||
'modules/angular1_router/**'
|
||||
],
|
||||
|
||||
karmaDartImports: {
|
||||
guinness: 'package:guinness/guinness_html.dart'
|
||||
},
|
||||
|
||||
// Map packages to the correct urls where Karma serves them.
|
||||
proxies: proxyPaths,
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
browsers: ['DartiumWithWebPlatform'],
|
||||
|
||||
port: 9877,
|
||||
|
||||
plugins: [
|
||||
require('karma-dart'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-sauce-launcher'),
|
||||
require('./karma-dart-evalcache')(packageSources)
|
||||
]
|
||||
});
|
||||
};
|
@ -11,28 +11,37 @@ module.exports = function(config) {
|
||||
files: [
|
||||
// Sources and specs.
|
||||
// Loaded through the System loader, in `test-main.js`.
|
||||
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
|
||||
{pattern: 'dist/all/@angular/**/*.js', included: false, watched: true},
|
||||
{pattern: 'dist/all/angular2/**/*.js', included: false, watched: true},
|
||||
|
||||
'node_modules/es6-shim/es6-shim.js',
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
|
||||
// zone-microtask must be included first as it contains a Promise monkey patch
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
'node_modules/zone.js/dist/async-test.js',
|
||||
'node_modules/zone.js/dist/fake-async-test.js',
|
||||
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'modules/angular2/src/testing/shims_for_IE.js',
|
||||
'shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
|
||||
{pattern: 'dist/all/empty.*', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser-dynamic/test/browser/static_assets/**', included: false, watched: false}
|
||||
],
|
||||
|
||||
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/js/dev/es5/angular2/examples/**', 'dist/angular1_router.js'],
|
||||
exclude: [
|
||||
'dist/all/@angular/**/e2e_test/**',
|
||||
'dist/all/@angular/examples/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js'
|
||||
],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
|
||||
@ -53,6 +62,7 @@ module.exports = function(config) {
|
||||
reporters: ['internal-angular'],
|
||||
sauceLabs: {
|
||||
testName: 'Angular2',
|
||||
retryLimit: 3,
|
||||
startConnect: false,
|
||||
recordVideo: false,
|
||||
recordScreenshots: false,
|
||||
@ -67,19 +77,23 @@ module.exports = function(config) {
|
||||
browserStack: {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 1,
|
||||
retryLimit: 3,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout : 60000,
|
||||
browserDisconnectTolerance : 3,
|
||||
browserNoActivityTimeout : 60000,
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.MODE === 'saucelabs') {
|
||||
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
@ -89,7 +103,7 @@ module.exports = function(config) {
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
|
||||
if (process.env.MODE === 'browserstack') {
|
||||
if (process.env.CI_MODE.startsWith('browserstack')) {
|
||||
config.browserStack.build = buildId;
|
||||
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
}
|
||||
|
1
modules/@angular/common/common.dart
Normal file
1
modules/@angular/common/common.dart
Normal file
@ -0,0 +1 @@
|
||||
export 'index.dart';
|
5
modules/@angular/common/index.ts
Normal file
5
modules/@angular/common/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from './src/pipes';
|
||||
export * from './src/directives';
|
||||
export * from './src/forms';
|
||||
export * from './src/common_directives';
|
||||
export * from './src/location';
|
13
modules/@angular/common/package.json
Normal file
13
modules/@angular/common/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@angular/common",
|
||||
"version": "$$ANGULAR_VERSION$$",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"jsnext:main": "esm/index.js",
|
||||
"typings": "index.d.ts",
|
||||
"author": "angular",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/core": "$$ANGULAR_VERSION$$"
|
||||
}
|
||||
}
|
17
modules/@angular/common/rollup.config.js
Normal file
17
modules/@angular/common/rollup.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/common/esm/index.js',
|
||||
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
|
||||
format: 'umd',
|
||||
moduleName: 'ng.common',
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Subject': 'Rx',
|
||||
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
|
||||
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
|
||||
'rxjs/Observable': 'Rx'
|
||||
},
|
||||
plugins: [
|
||||
// nodeResolve({ jsnext: true, main: true }),
|
||||
]
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import {CONST_EXPR, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
import {FORM_DIRECTIVES} from './forms';
|
||||
import {CORE_DIRECTIVES} from './directives';
|
||||
|
||||
@ -9,7 +8,7 @@ import {CORE_DIRECTIVES} from './directives';
|
||||
* NgModel).
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@Component` or `@View` decorators.
|
||||
* property of the `@Component` decorator.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
@ -17,7 +16,7 @@ import {CORE_DIRECTIVES} from './directives';
|
||||
*
|
||||
* ```typescript
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm} from
|
||||
* 'angular2/angular2';
|
||||
* '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
@ -33,7 +32,7 @@ import {CORE_DIRECTIVES} from './directives';
|
||||
* one could import all the common directives at once:
|
||||
*
|
||||
* ```typescript
|
||||
* import {COMMON_DIRECTIVES} from 'angular2/angular2';
|
||||
* import {COMMON_DIRECTIVES} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
@ -46,4 +45,4 @@ import {CORE_DIRECTIVES} from './directives';
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const COMMON_DIRECTIVES: Type[][] = CONST_EXPR([CORE_DIRECTIVES, FORM_DIRECTIVES]);
|
||||
export const COMMON_DIRECTIVES: Type[][] = /*@ts2dart_const*/[CORE_DIRECTIVES, FORM_DIRECTIVES];
|
@ -6,7 +6,9 @@
|
||||
export {NgClass} from './directives/ng_class';
|
||||
export {NgFor} from './directives/ng_for';
|
||||
export {NgIf} from './directives/ng_if';
|
||||
export {NgTemplateOutlet} from './directives/ng_template_outlet';
|
||||
export {NgStyle} from './directives/ng_style';
|
||||
export {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './directives/ng_switch';
|
||||
export {NgPlural, NgPluralCase, NgLocalization} from './directives/ng_plural';
|
||||
export * from './directives/observable_list_diff';
|
||||
export {CORE_DIRECTIVES} from './directives/core_directives';
|
||||
export {CORE_DIRECTIVES} from './directives/core_directives';
|
@ -1,23 +1,25 @@
|
||||
import {CONST_EXPR, Type} from 'angular2/src/facade/lang';
|
||||
import {Type} from '../../src/facade/lang';
|
||||
import {NgClass} from './ng_class';
|
||||
import {NgFor} from './ng_for';
|
||||
import {NgIf} from './ng_if';
|
||||
import {NgTemplateOutlet} from './ng_template_outlet';
|
||||
import {NgStyle} from './ng_style';
|
||||
import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
|
||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@View` annotation.
|
||||
* property of the `@Component` annotation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
|
||||
*
|
||||
* Instead of writing:
|
||||
*
|
||||
* ```typescript
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault} from 'angular2/angular2';
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
@ -32,7 +34,7 @@ import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
|
||||
* one could import all the core directives at once:
|
||||
*
|
||||
* ```typescript
|
||||
* import {CORE_DIRECTIVES} from 'angular2/angular2';
|
||||
* import {CORE_DIRECTIVES} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
@ -45,5 +47,15 @@ import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const CORE_DIRECTIVES: Type[] =
|
||||
CONST_EXPR([NgClass, NgFor, NgIf, NgStyle, NgSwitch, NgSwitchWhen, NgSwitchDefault]);
|
||||
export const CORE_DIRECTIVES: Type[] = /*@ts2dart_const*/[
|
||||
NgClass,
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgTemplateOutlet,
|
||||
NgStyle,
|
||||
NgSwitch,
|
||||
NgSwitchWhen,
|
||||
NgSwitchDefault,
|
||||
NgPlural,
|
||||
NgPluralCase
|
||||
];
|
@ -1,16 +1,18 @@
|
||||
import {isPresent, isString, StringWrapper, isBlank, isArray} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
DoCheck,
|
||||
OnDestroy,
|
||||
Directive,
|
||||
ElementRef,
|
||||
IterableDiffer,
|
||||
IterableDiffers,
|
||||
KeyValueDiffer,
|
||||
KeyValueDiffers,
|
||||
Renderer
|
||||
} from 'angular2/core';
|
||||
import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||
Renderer,
|
||||
IterableDiffer,
|
||||
KeyValueDiffer,
|
||||
CollectionChangeRecord,
|
||||
KeyValueChangeRecord
|
||||
} from '@angular/core';
|
||||
import {isPresent, isString, isArray} from '../../src/facade/lang';
|
||||
import {StringMapWrapper, isListLikeIterable} from '../../src/facade/collection';
|
||||
|
||||
/**
|
||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
||||
@ -31,13 +33,14 @@ import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collecti
|
||||
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* import {Component, NgClass} from 'angular2/angular2';
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgClass} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'toggle-button',
|
||||
* inputs: ['isDisabled'],
|
||||
* template: `
|
||||
* <div class="button" [ng-class]="{active: isOn, disabled: isDisabled}"
|
||||
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
|
||||
* (click)="toggle(!isOn)">
|
||||
* Click me!
|
||||
* </div>`,
|
||||
@ -70,68 +73,70 @@ import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collecti
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Directive({selector: '[ng-class]', inputs: ['rawClass: ng-class', 'initialClasses: class']})
|
||||
@Directive({selector: '[ngClass]', inputs: ['rawClass: ngClass', 'initialClasses: class']})
|
||||
export class NgClass implements DoCheck, OnDestroy {
|
||||
private _differ: any;
|
||||
private _mode: string;
|
||||
private _initialClasses = [];
|
||||
private _rawClass;
|
||||
private _iterableDiffer: IterableDiffer;
|
||||
private _keyValueDiffer: KeyValueDiffer;
|
||||
private _initialClasses: string[] = [];
|
||||
private _rawClass: string[] | Set<string>;
|
||||
|
||||
constructor(private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
set initialClasses(v) {
|
||||
set initialClasses(v: string) {
|
||||
this._applyInitialClasses(true);
|
||||
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
|
||||
this._applyInitialClasses(false);
|
||||
this._applyClasses(this._rawClass, false);
|
||||
}
|
||||
|
||||
set rawClass(v) {
|
||||
set rawClass(v: string | string[] | Set<string>| {[key: string]: any}) {
|
||||
this._cleanupClasses(this._rawClass);
|
||||
|
||||
if (isString(v)) {
|
||||
v = v.split(' ');
|
||||
v = (<string>v).split(' ');
|
||||
}
|
||||
|
||||
this._rawClass = v;
|
||||
this._rawClass = <string[] | Set<string>>v;
|
||||
this._iterableDiffer = null;
|
||||
this._keyValueDiffer = null;
|
||||
if (isPresent(v)) {
|
||||
if (isListLikeIterable(v)) {
|
||||
this._differ = this._iterableDiffers.find(v).create(null);
|
||||
this._mode = 'iterable';
|
||||
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
|
||||
} else {
|
||||
this._differ = this._keyValueDiffers.find(v).create(null);
|
||||
this._mode = 'keyValue';
|
||||
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
|
||||
}
|
||||
} else {
|
||||
this._differ = null;
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._rawClass);
|
||||
if (isPresent(this._iterableDiffer)) {
|
||||
var changes = this._iterableDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
if (this._mode == 'iterable') {
|
||||
this._applyIterableChanges(changes);
|
||||
} else {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
this._applyIterableChanges(changes);
|
||||
}
|
||||
}
|
||||
if (isPresent(this._keyValueDiffer)) {
|
||||
var changes = this._keyValueDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this._cleanupClasses(this._rawClass); }
|
||||
|
||||
private _cleanupClasses(rawClassVal): void {
|
||||
private _cleanupClasses(rawClassVal: string[] | Set<string>| {[key: string]: any}): void {
|
||||
this._applyClasses(rawClassVal, true);
|
||||
this._applyInitialClasses(false);
|
||||
}
|
||||
|
||||
private _applyKeyValueChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record) => {
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
||||
if (record.previousValue) {
|
||||
this._toggleClass(record.key, false);
|
||||
}
|
||||
@ -139,15 +144,17 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
}
|
||||
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
|
||||
changes.forEachAddedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, false); });
|
||||
}
|
||||
|
||||
private _applyInitialClasses(isCleanup: boolean) {
|
||||
this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
|
||||
}
|
||||
|
||||
private _applyClasses(rawClassVal: string[] | Set<string>| {[key: string]: string},
|
||||
private _applyClasses(rawClassVal: string[] | Set<string>| {[key: string]: any},
|
||||
isCleanup: boolean) {
|
||||
if (isPresent(rawClassVal)) {
|
||||
if (isArray(rawClassVal)) {
|
||||
@ -155,23 +162,24 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
} else if (rawClassVal instanceof Set) {
|
||||
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
||||
} else {
|
||||
StringMapWrapper.forEach(<{[k: string]: string}>rawClassVal, (expVal, className) => {
|
||||
if (expVal) this._toggleClass(className, !isCleanup);
|
||||
});
|
||||
StringMapWrapper.forEach(<{[k: string]: any}>rawClassVal,
|
||||
(expVal: any, className: string) => {
|
||||
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleClass(className: string, enabled): void {
|
||||
private _toggleClass(className: string, enabled: boolean): void {
|
||||
className = className.trim();
|
||||
if (className.length > 0) {
|
||||
if (className.indexOf(' ') > -1) {
|
||||
var classes = className.split(/\s+/g);
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
this._renderer.setElementClass(this._ngEl, classes[i], enabled);
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
|
||||
}
|
||||
} else {
|
||||
this._renderer.setElementClass(this._ngEl, className, enabled);
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,26 +6,44 @@ import {
|
||||
IterableDiffers,
|
||||
ViewContainerRef,
|
||||
TemplateRef,
|
||||
ViewRef
|
||||
} from 'angular2/core';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
EmbeddedViewRef,
|
||||
TrackByFn,
|
||||
DefaultIterableDiffer,
|
||||
CollectionChangeRecord
|
||||
} from '@angular/core';
|
||||
import {isPresent, isBlank, getTypeNameForDebugging} from '../../src/facade/lang';
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
|
||||
export class NgForRow {
|
||||
constructor(public $implicit: any, public index: number, public count: number) {}
|
||||
|
||||
get first(): boolean { return this.index === 0; }
|
||||
|
||||
get last(): boolean { return this.index === this.count - 1; }
|
||||
|
||||
get even(): boolean { return this.index % 2 === 0; }
|
||||
|
||||
get odd(): boolean { return !this.even; }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
|
||||
* each instantiated template inherits from the outer context with the given loop variable set
|
||||
* to the current item from the iterable.
|
||||
*
|
||||
* # Local Variables
|
||||
* ### Local Variables
|
||||
*
|
||||
* `NgFor` provides several exported values that can be aliased to local variables:
|
||||
*
|
||||
* * `index` will be set to the current loop iteration for each template context.
|
||||
* * `first` will be set to a boolean value indicating whether the item is the first one in the
|
||||
* iteration.
|
||||
* * `last` will be set to a boolean value indicating whether the item is the last one in the
|
||||
* iteration.
|
||||
* * `even` will be set to a boolean value indicating whether this item has an even index.
|
||||
* * `odd` will be set to a boolean value indicating whether this item has an odd index.
|
||||
*
|
||||
* # Change Propagation
|
||||
* ### Change Propagation
|
||||
*
|
||||
* When the contents of the iterator changes, `NgFor` makes the corresponding changes to the DOM:
|
||||
*
|
||||
@ -48,39 +66,48 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
* elements were deleted and all new elements inserted). This is an expensive operation and should
|
||||
* be avoided if possible.
|
||||
*
|
||||
* # Syntax
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<li *ng-for="#item of items; #i = index">...</li>`
|
||||
* - `<li template="ng-for #item of items; #i = index">...</li>`
|
||||
* - `<template ng-for #item [ng-for-of]="items" #i="index"><li>...</li></template>`
|
||||
* - `<li *ngFor="let item of items; let i = index">...</li>`
|
||||
* - `<li template="ngFor let item of items; let i = index">...</li>`
|
||||
* - `<template ngFor let-item [ngForOf]="items" let-i="index"><li>...</li></template>`
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
|
||||
* example.
|
||||
*/
|
||||
@Directive({selector: '[ng-for][ng-for-of]', inputs: ['ngForOf', 'ngForTemplate']})
|
||||
@Directive({selector: '[ngFor][ngForOf]', inputs: ['ngForTrackBy', 'ngForOf', 'ngForTemplate']})
|
||||
export class NgFor implements DoCheck {
|
||||
/** @internal */
|
||||
_ngForOf: any;
|
||||
/** @internal */
|
||||
_ngForTrackBy: TrackByFn;
|
||||
private _differ: IterableDiffer;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef,
|
||||
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)) {
|
||||
this._differ = this._iterableDiffers.find(value).create(this._cdr);
|
||||
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.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set ngForTemplate(value: TemplateRef) {
|
||||
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
||||
if (isPresent(value)) {
|
||||
this._templateRef = value;
|
||||
}
|
||||
}
|
||||
|
||||
set ngForTrackBy(value: TrackByFn) { this._ngForTrackBy = value; }
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._ngForOf);
|
||||
@ -88,19 +115,19 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
}
|
||||
|
||||
private _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 = [];
|
||||
changes.forEachRemovedItem((removedRecord) =>
|
||||
var recordViewTuples: RecordViewTuple[] = [];
|
||||
changes.forEachRemovedItem((removedRecord: CollectionChangeRecord) =>
|
||||
recordViewTuples.push(new RecordViewTuple(removedRecord, null)));
|
||||
|
||||
changes.forEachMovedItem((movedRecord) =>
|
||||
changes.forEachMovedItem((movedRecord: CollectionChangeRecord) =>
|
||||
recordViewTuples.push(new RecordViewTuple(movedRecord, null)));
|
||||
|
||||
var insertTuples = this._bulkRemove(recordViewTuples);
|
||||
|
||||
changes.forEachAddedItem((addedRecord) =>
|
||||
changes.forEachAddedItem((addedRecord: CollectionChangeRecord) =>
|
||||
insertTuples.push(new RecordViewTuple(addedRecord, null)));
|
||||
|
||||
this._bulkInsert(insertTuples);
|
||||
@ -110,25 +137,31 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
this._viewContainer.get(i).setLocal('last', i === ilen - 1);
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record) => {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
||||
private _perViewChange(view, record) {
|
||||
view.setLocal('\$implicit', record.item);
|
||||
view.setLocal('index', record.currentIndex);
|
||||
view.setLocal('even', (record.currentIndex % 2 == 0));
|
||||
view.setLocal('odd', (record.currentIndex % 2 == 1));
|
||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
|
||||
view.context.$implicit = record.item;
|
||||
}
|
||||
|
||||
private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
|
||||
tuples.sort((a, b) => a.record.previousIndex - b.record.previousIndex);
|
||||
var movedTuples = [];
|
||||
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 = this._viewContainer.detach(tuple.record.previousIndex);
|
||||
tuple.view =
|
||||
<EmbeddedViewRef<NgForRow>>this._viewContainer.detach(tuple.record.previousIndex);
|
||||
movedTuples.push(tuple);
|
||||
} else {
|
||||
this._viewContainer.remove(tuple.record.previousIndex);
|
||||
@ -144,8 +177,8 @@ export class NgFor implements DoCheck {
|
||||
if (isPresent(tuple.view)) {
|
||||
this._viewContainer.insert(tuple.view, tuple.record.currentIndex);
|
||||
} else {
|
||||
tuple.view =
|
||||
this._viewContainer.createEmbeddedView(this._templateRef, tuple.record.currentIndex);
|
||||
tuple.view = this._viewContainer.createEmbeddedView(
|
||||
this._templateRef, new NgForRow(null, null, null), tuple.record.currentIndex);
|
||||
}
|
||||
}
|
||||
return tuples;
|
||||
@ -153,9 +186,9 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
view: ViewRef;
|
||||
view: EmbeddedViewRef<NgForRow>;
|
||||
record: any;
|
||||
constructor(record, view) {
|
||||
constructor(record: any, view: EmbeddedViewRef<NgForRow>) {
|
||||
this.record = record;
|
||||
this.view = view;
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
import {Directive, ViewContainerRef, TemplateRef} from 'angular2/core';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {Directive, ViewContainerRef, TemplateRef} from '@angular/core';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
|
||||
/**
|
||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||
*
|
||||
* If the expression assigned to `ng-if` evaluates to a false value then the element
|
||||
* If the expression assigned to `ngIf` evaluates to a false value then the element
|
||||
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* <div *ng-if="errorCount > 0" class="error">
|
||||
* <div *ngIf="errorCount > 0" class="error">
|
||||
* <!-- Error message displayed when the errorCount property on the current context is greater
|
||||
* than 0. -->
|
||||
* {{errorCount}} errors detected
|
||||
@ -19,17 +19,18 @@ import {isBlank} from 'angular2/src/facade/lang';
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div *ng-if="condition">...</div>`
|
||||
* - `<div template="ng-if condition">...</div>`
|
||||
* - `<template [ng-if]="condition"><div>...</div></template>`
|
||||
* - `<div *ngIf="condition">...</div>`
|
||||
* - `<div template="ngIf condition">...</div>`
|
||||
* - `<template [ngIf]="condition"><div>...</div></template>`
|
||||
*/
|
||||
@Directive({selector: '[ng-if]', inputs: ['ngIf']})
|
||||
@Directive({selector: '[ngIf]', inputs: ['ngIf']})
|
||||
export class NgIf {
|
||||
private _prevCondition: boolean = null;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
|
||||
}
|
||||
|
||||
set ngIf(newCondition /* boolean */) {
|
||||
set ngIf(newCondition: any /* boolean */) {
|
||||
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
|
||||
this._prevCondition = true;
|
||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
148
modules/@angular/common/src/directives/ng_plural.ts
Normal file
148
modules/@angular/common/src/directives/ng_plural.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import {
|
||||
Directive,
|
||||
ViewContainerRef,
|
||||
TemplateRef,
|
||||
ContentChildren,
|
||||
QueryList,
|
||||
Attribute,
|
||||
AfterContentInit,
|
||||
Input
|
||||
} from '@angular/core';
|
||||
import {isPresent, NumberWrapper} from '../../src/facade/lang';
|
||||
import {Map} from '../../src/facade/collection';
|
||||
|
||||
import {SwitchView} from './ng_switch';
|
||||
|
||||
const _CATEGORY_DEFAULT = 'other';
|
||||
|
||||
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
|
||||
|
||||
/**
|
||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
||||
*
|
||||
* To use this directive, you must provide an extension of `NgLocalization` that maps values to
|
||||
* category names. You then define a container element that sets the `[ngPlural]` attribute to a
|
||||
* switch expression.
|
||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
||||
* expression.
|
||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||
* matches the switch expression exactly.
|
||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
||||
* 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) {
|
||||
* if(value < 5) {
|
||||
* return 'few';
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* providers: [provide(NgLocalization, {useClass: MyLocalization})]
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngPlural]="value">
|
||||
* <template ngPluralCase="=0">there is nothing</template>
|
||||
* <template ngPluralCase="=1">there is one</template>
|
||||
* <template ngPluralCase="few">there are a few</template>
|
||||
* <template ngPluralCase="other">there is some number</template>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [NgPlural, NgPluralCase]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: '[ngPlural]'})
|
||||
export class NgPlural implements AfterContentInit {
|
||||
private _switchValue: number;
|
||||
private _activeView: SwitchView;
|
||||
private _caseViews = new Map<any, SwitchView>();
|
||||
@ContentChildren(NgPluralCase) cases: QueryList<NgPluralCase> = null;
|
||||
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
@Input()
|
||||
set ngPlural(value: number) {
|
||||
this._switchValue = value;
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.cases.forEach((pluralCase: NgPluralCase): void => {
|
||||
this._caseViews.set(this._formatValue(pluralCase), pluralCase._view);
|
||||
});
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
var view: SwitchView = this._caseViews.get(this._switchValue);
|
||||
if (!isPresent(view)) view = this._getCategoryView(this._switchValue);
|
||||
|
||||
this._activateView(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_clearViews() {
|
||||
if (isPresent(this._activeView)) this._activeView.destroy();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateView(view: SwitchView) {
|
||||
if (!isPresent(view)) return;
|
||||
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); }
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import {KeyValueChangeRecord} from '@angular/core';
|
||||
import {
|
||||
DoCheck,
|
||||
KeyValueDiffer,
|
||||
@ -5,30 +6,31 @@ import {
|
||||
ElementRef,
|
||||
Directive,
|
||||
Renderer
|
||||
} from 'angular2/core';
|
||||
import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
|
||||
} from '@angular/core';
|
||||
import {isPresent, isBlank} from '../../src/facade/lang';
|
||||
|
||||
/**
|
||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
||||
*
|
||||
* An expression assigned to the `ng-style` property must evaluate to an object and the
|
||||
* An expression assigned to the `ngStyle` property must evaluate to an object and the
|
||||
* corresponding element styles are updated based on changes to this object. Style names to update
|
||||
* are taken from the object's keys, and values - from the corresponding object's values.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div [ng-style]="{'font-style': style}"></div>`
|
||||
* - `<div [ng-style]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
||||
* - `<div [ngStyle]="{'font-style': style}"></div>`
|
||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* import {Component, NgStyle} from 'angular2/angular2';
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgStyle} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'ng-style-example',
|
||||
* selector: 'ngStyle-example',
|
||||
* template: `
|
||||
* <h1 [ng-style]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
||||
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
||||
* Change style of this text!
|
||||
* </h1>
|
||||
*
|
||||
@ -58,17 +60,17 @@ import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
|
||||
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
|
||||
* based on the `style` property's value changes.
|
||||
*/
|
||||
@Directive({selector: '[ng-style]', inputs: ['rawStyle: ng-style']})
|
||||
@Directive({selector: '[ngStyle]', inputs: ['rawStyle: ngStyle']})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_rawStyle;
|
||||
_rawStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
|
||||
constructor(private _differs: KeyValueDiffers, private _ngEl: ElementRef,
|
||||
private _renderer: Renderer) {}
|
||||
|
||||
set rawStyle(v) {
|
||||
set rawStyle(v: {[key: string]: string}) {
|
||||
this._rawStyle = v;
|
||||
if (isBlank(this._differ) && isPresent(v)) {
|
||||
this._differ = this._differs.find(this._rawStyle).create(null);
|
||||
@ -85,12 +87,15 @@ export class NgStyle implements DoCheck {
|
||||
}
|
||||
|
||||
private _applyChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record) => { 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, name, val);
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, val);
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
import {Directive, Host, ViewContainerRef, TemplateRef} from 'angular2/core';
|
||||
import {isPresent, isBlank, normalizeBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, Map} from 'angular2/src/facade/collection';
|
||||
import {Directive, Host, ViewContainerRef, TemplateRef} from '@angular/core';
|
||||
import {isPresent, isBlank, normalizeBlank} from '../../src/facade/lang';
|
||||
import {ListWrapper, Map} from '../../src/facade/collection';
|
||||
|
||||
const _WHEN_DEFAULT = CONST_EXPR(new Object());
|
||||
const _WHEN_DEFAULT = /*@ts2dart_const*/ new Object();
|
||||
|
||||
class SwitchView {
|
||||
constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
|
||||
export class SwitchView {
|
||||
constructor(private _viewContainerRef: ViewContainerRef,
|
||||
private _templateRef: TemplateRef<Object>) {}
|
||||
|
||||
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||
|
||||
@ -21,38 +22,38 @@ class SwitchView {
|
||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
||||
* obtained from the evaluated switch expression. In other words, you define a container element
|
||||
* (where you place the directive with a switch expression on the
|
||||
* **`[ng-switch]="..."` attribute**), define any inner elements inside of the directive and
|
||||
* place a `[ng-switch-when]` attribute per element.
|
||||
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
|
||||
* place a `[ngSwitchWhen]` attribute per element.
|
||||
*
|
||||
* The `ng-switch-when` property is used to inform `NgSwitch` which element to display when the
|
||||
* expression is evaluated. If a matching expression is not found via a `ng-switch-when` property
|
||||
* then an element with the `ng-switch-default` attribute is displayed.
|
||||
* The `ngSwitchWhen` property is used to inform `NgSwitch` which element to display when the
|
||||
* expression is evaluated. If a matching expression is not found via a `ngSwitchWhen` property
|
||||
* then an element with the `ngSwitchDefault` attribute is displayed.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({selector: 'app'})
|
||||
* @View({
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ng-switch]="value">
|
||||
* <p *ng-switch-when="'init'">increment to start</p>
|
||||
* <p *ng-switch-when="0">0, increment again</p>
|
||||
* <p *ng-switch-when="1">1, increment again</p>
|
||||
* <p *ng-switch-when="2">2, stop incrementing</p>
|
||||
* <p *ng-switch-default>> 2, STOP!</p>
|
||||
* <div [ngSwitch]="value">
|
||||
* <p *ngSwitchWhen="'init'">increment to start</p>
|
||||
* <p *ngSwitchWhen="0">0, increment again</p>
|
||||
* <p *ngSwitchWhen="1">1, increment again</p>
|
||||
* <p *ngSwitchWhen="2">2, stop incrementing</p>
|
||||
* <p *ngSwitchDefault>> 2, STOP!</p>
|
||||
* </div>
|
||||
*
|
||||
* <!-- alternate syntax -->
|
||||
*
|
||||
* <p [ng-switch]="value">
|
||||
* <template ng-switch-when="init">increment to start</template>
|
||||
* <template [ng-switch-when]="0">0, increment again</template>
|
||||
* <template [ng-switch-when]="1">1, increment again</template>
|
||||
* <template [ng-switch-when]="2">2, stop incrementing</template>
|
||||
* <template ng-switch-default>> 2, STOP!</template>
|
||||
* <p [ngSwitch]="value">
|
||||
* <template ngSwitchWhen="init">increment to start</template>
|
||||
* <template [ngSwitchWhen]="0">0, increment again</template>
|
||||
* <template [ngSwitchWhen]="1">1, increment again</template>
|
||||
* <template [ngSwitchWhen]="2">2, stop incrementing</template>
|
||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
||||
* </p>
|
||||
* `,
|
||||
* directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault]
|
||||
@ -68,14 +69,14 @@ class SwitchView {
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
@Directive({selector: '[ng-switch]', inputs: ['ngSwitch']})
|
||||
@Directive({selector: '[ngSwitch]', inputs: ['ngSwitch']})
|
||||
export class NgSwitch {
|
||||
private _switchValue: any;
|
||||
private _useDefault: boolean = false;
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
|
||||
set ngSwitch(value) {
|
||||
set ngSwitch(value: any) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViews();
|
||||
|
||||
@ -92,7 +93,7 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_onWhenValueChanged(oldWhen, newWhen, view: SwitchView): void {
|
||||
_onWhenValueChanged(oldWhen: any, newWhen: any, view: SwitchView): void {
|
||||
this._deregisterView(oldWhen, view);
|
||||
this._registerView(newWhen, view);
|
||||
|
||||
@ -136,7 +137,7 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_registerView(value, view: SwitchView): void {
|
||||
_registerView(value: any, view: SwitchView): void {
|
||||
var views = this._valueViews.get(value);
|
||||
if (isBlank(views)) {
|
||||
views = [];
|
||||
@ -146,7 +147,7 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_deregisterView(value, view: SwitchView): void {
|
||||
_deregisterView(value: any, view: SwitchView): void {
|
||||
// `_WHEN_DEFAULT` is used a marker for non-registered whens
|
||||
if (value === _WHEN_DEFAULT) return;
|
||||
var views = this._valueViews.get(value);
|
||||
@ -159,14 +160,14 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the sub-tree when the `ng-switch-when` expression evaluates to the same value as the
|
||||
* Insert the sub-tree when the `ngSwitchWhen` expression evaluates to the same value as the
|
||||
* enclosing switch expression.
|
||||
*
|
||||
* If multiple match expression match the switch expression value, all of them are displayed.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*/
|
||||
@Directive({selector: '[ng-switch-when]', inputs: ['ngSwitchWhen']})
|
||||
@Directive({selector: '[ngSwitchWhen]', inputs: ['ngSwitchWhen']})
|
||||
export class NgSwitchWhen {
|
||||
// `_WHEN_DEFAULT` is used as a marker for a not yet initialized value
|
||||
/** @internal */
|
||||
@ -175,13 +176,13 @@ export class NgSwitchWhen {
|
||||
_view: SwitchView;
|
||||
private _switch: NgSwitch;
|
||||
|
||||
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
|
||||
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
this._switch = ngSwitch;
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
set ngSwitchWhen(value) {
|
||||
set ngSwitchWhen(value: any) {
|
||||
this._switch._onWhenValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
@ -193,9 +194,9 @@ export class NgSwitchWhen {
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*/
|
||||
@Directive({selector: '[ng-switch-default]'})
|
||||
@Directive({selector: '[ngSwitchDefault]'})
|
||||
export class NgSwitchDefault {
|
||||
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
|
||||
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() sswitch: NgSwitch) {
|
||||
sswitch._registerView(_WHEN_DEFAULT, new SwitchView(viewContainer, templateRef));
|
||||
}
|
26
modules/@angular/common/src/directives/ng_template_outlet.ts
Normal file
26
modules/@angular/common/src/directives/ng_template_outlet.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {Directive, Input, ViewContainerRef, ViewRef, TemplateRef} from '@angular/core';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
|
||||
/**
|
||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
||||
*
|
||||
* ### Syntax
|
||||
* - `<template [ngTemplateOutlet]="templateRefExpression"></template>`
|
||||
*/
|
||||
@Directive({selector: '[ngTemplateOutlet]'})
|
||||
export class NgTemplateOutlet {
|
||||
private _insertedViewRef: ViewRef;
|
||||
|
||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
@Input()
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) {
|
||||
if (isPresent(this._insertedViewRef)) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._insertedViewRef));
|
||||
}
|
||||
|
||||
if (isPresent(templateRef)) {
|
||||
this._insertedViewRef = this._viewContainerRef.createEmbeddedView(templateRef);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ class ObservableListDiff extends DefaultIterableDiffer {
|
||||
}
|
||||
}
|
||||
|
||||
dynamic diff(ObservableList collection) {
|
||||
DefaultIterableDiffer diff(ObservableList collection) {
|
||||
if (collection is! ObservableList) {
|
||||
throw "Cannot change the type of a collection";
|
||||
}
|
||||
@ -57,7 +57,7 @@ class ObservableListDiff extends DefaultIterableDiffer {
|
||||
class ObservableListDiffFactory implements IterableDifferFactory {
|
||||
const ObservableListDiffFactory();
|
||||
bool supports(obj) => obj is ObservableList;
|
||||
IterableDiffer create(ChangeDetectorRef cdRef) {
|
||||
IterableDiffer create(ChangeDetectorRef cdRef, [Function trackByFn]) {
|
||||
return new ObservableListDiff(cdRef);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
// TS does not have Observables
|
||||
|
||||
// I need to be here to make TypeScript think this is a module.
|
||||
import {} from '../../src/facade/lang';
|
||||
|
||||
/**
|
||||
* This module exists in Dart, but not in Typescript. This exported symbol
|
||||
* is only here to help Typescript think this is a module.
|
||||
*/
|
||||
export var workaround_empty_observable_list_diff: any;
|
1
modules/@angular/common/src/facade
Symbolic link
1
modules/@angular/common/src/facade
Symbolic link
@ -0,0 +1 @@
|
||||
../../facade/src
|
@ -7,9 +7,8 @@
|
||||
* to read information
|
||||
* from the form DOM elements.
|
||||
*
|
||||
* This module is not included in the `angular2` module; you must import the forms module
|
||||
* Forms providers are not included in default providers; you must import these providers
|
||||
* explicitly.
|
||||
*
|
||||
*/
|
||||
export {AbstractControl, Control, ControlGroup, ControlArray} from './forms/model';
|
||||
|
||||
@ -31,12 +30,34 @@ export {
|
||||
NgSelectOption,
|
||||
SelectControlValueAccessor
|
||||
} from './forms/directives/select_control_value_accessor';
|
||||
export {FORM_DIRECTIVES} from './forms/directives';
|
||||
export {FORM_DIRECTIVES, RadioButtonState} from './forms/directives';
|
||||
export {NG_VALIDATORS, NG_ASYNC_VALIDATORS, Validators} from './forms/validators';
|
||||
export {
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator,
|
||||
Validator
|
||||
} from './forms/directives/validators';
|
||||
export {FormBuilder, FORM_PROVIDERS, FORM_BINDINGS} from './forms/form_builder';
|
||||
export {FormBuilder} from './forms/form_builder';
|
||||
import {FormBuilder} from './forms/form_builder';
|
||||
import {RadioControlRegistry} from './forms/directives/radio_control_value_accessor';
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Shorthand set of providers used for building Angular forms.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* bootstrap(MyApp, [FORM_PROVIDERS]);
|
||||
* ```
|
||||
*/
|
||||
export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioControlRegistry];
|
||||
|
||||
/**
|
||||
* See {@link FORM_PROVIDERS} instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export const FORM_BINDINGS = /*@ts2dart_const*/ FORM_PROVIDERS;
|
@ -1,4 +1,4 @@
|
||||
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Type} from '@angular/core';
|
||||
import {NgControlName} from './directives/ng_control_name';
|
||||
import {NgFormControl} from './directives/ng_form_control';
|
||||
import {NgModel} from './directives/ng_model';
|
||||
@ -8,12 +8,18 @@ import {NgForm} from './directives/ng_form';
|
||||
import {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||
import {NumberValueAccessor} from './directives/number_value_accessor';
|
||||
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
|
||||
import {NgControlStatus} from './directives/ng_control_status';
|
||||
import {
|
||||
SelectControlValueAccessor,
|
||||
NgSelectOption
|
||||
} from './directives/select_control_value_accessor';
|
||||
import {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
|
||||
import {
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator
|
||||
} from './directives/validators';
|
||||
|
||||
export {NgControlName} from './directives/ng_control_name';
|
||||
export {NgFormControl} from './directives/ng_form_control';
|
||||
@ -23,19 +29,28 @@ export {NgFormModel} from './directives/ng_form_model';
|
||||
export {NgForm} from './directives/ng_form';
|
||||
export {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||
export {
|
||||
RadioControlValueAccessor,
|
||||
RadioButtonState
|
||||
} from './directives/radio_control_value_accessor';
|
||||
export {NumberValueAccessor} from './directives/number_value_accessor';
|
||||
export {NgControlStatus} from './directives/ng_control_status';
|
||||
export {
|
||||
SelectControlValueAccessor,
|
||||
NgSelectOption
|
||||
} from './directives/select_control_value_accessor';
|
||||
export {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
|
||||
export {
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator
|
||||
} from './directives/validators';
|
||||
export {NgControl} from './directives/ng_control';
|
||||
export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||
|
||||
/**
|
||||
*
|
||||
* A list of all the form directives used as part of a `@View` annotation.
|
||||
* A list of all the form directives used as part of a `@Component` annotation.
|
||||
*
|
||||
* This is a shorthand for importing them each individually.
|
||||
*
|
||||
@ -49,7 +64,7 @@ export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||
* class MyApp {}
|
||||
* ```
|
||||
*/
|
||||
export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
|
||||
export const FORM_DIRECTIVES: Type[] = /*@ts2dart_const*/[
|
||||
NgControlName,
|
||||
NgControlGroup,
|
||||
|
||||
@ -63,9 +78,11 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
|
||||
NumberValueAccessor,
|
||||
CheckboxControlValueAccessor,
|
||||
SelectControlValueAccessor,
|
||||
RadioControlValueAccessor,
|
||||
NgControlStatus,
|
||||
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator
|
||||
]);
|
||||
MaxLengthValidator,
|
||||
PatternValidator
|
||||
];
|
@ -1,6 +1,6 @@
|
||||
import {AbstractControl} from '../model';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {isPresent} from '../../../src/facade/lang';
|
||||
import {unimplemented} from '../../../src/facade/exceptions';
|
||||
|
||||
/**
|
||||
* Base class for control directives.
|
@ -1,33 +1,34 @@
|
||||
import {Directive, Renderer, ElementRef, Self, forwardRef, Provider} from 'angular2/core';
|
||||
|
||||
import {Directive, Renderer, ElementRef, Self, forwardRef, Provider} from '@angular/core';
|
||||
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
const CHECKBOX_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => CheckboxControlValueAccessor), multi: true}));
|
||||
export const CHECKBOX_VALUE_ACCESSOR: any = /*@ts2dart_const*/ {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => CheckboxControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a checkbox input element.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* <input type="checkbox" ng-control="rememberLogin">
|
||||
* <input type="checkbox" ngControl="rememberLogin">
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input[type=checkbox][ng-control],input[type=checkbox][ng-form-control],input[type=checkbox][ng-model]',
|
||||
'input[type=checkbox][ngControl],input[type=checkbox][ngFormControl],input[type=checkbox][ngModel]',
|
||||
host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
|
||||
bindings: [CHECKBOX_VALUE_ACCESSOR]
|
||||
providers: [CHECKBOX_VALUE_ACCESSOR]
|
||||
})
|
||||
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._renderer.setElementProperty(this._elementRef, 'checked', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value);
|
||||
}
|
||||
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
@ -1,5 +1,4 @@
|
||||
import {OpaqueToken} from 'angular2/core';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
/**
|
||||
* A bridge between a control and a native element.
|
||||
@ -26,4 +25,10 @@ export interface ControlValueAccessor {
|
||||
registerOnTouched(fn: any): void;
|
||||
}
|
||||
|
||||
export const NG_VALUE_ACCESSOR: OpaqueToken = CONST_EXPR(new OpaqueToken("NgValueAccessor"));
|
||||
/**
|
||||
* Used to provide a {@link ControlValueAccessor} for form controls.
|
||||
*
|
||||
* See {@link DefaultValueAccessor} for how to implement one.
|
||||
*/
|
||||
export const NG_VALUE_ACCESSOR: OpaqueToken =
|
||||
/*@ts2dart_const*/ new OpaqueToken("NgValueAccessor");
|
@ -1,9 +1,13 @@
|
||||
import {Directive, ElementRef, Renderer, Self, forwardRef, Provider} from 'angular2/core';
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
import {isBlank} from '../../../src/facade/lang';
|
||||
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||
import {isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DefaultValueAccessor), multi: true}));
|
||||
export const DEFAULT_VALUE_ACCESSOR: any = /*@ts2dart_const*/
|
||||
/* @ts2dart_Provider */ {
|
||||
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
|
||||
@ -11,27 +15,27 @@ const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* <input type="text" ng-control="searchQuery">
|
||||
* <input type="text" ngControl="searchQuery">
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input:not([type=checkbox])[ng-control],textarea[ng-control],input:not([type=checkbox])[ng-form-control],textarea[ng-form-control],input:not([type=checkbox])[ng-model],textarea[ng-model],[ng-default-control]',
|
||||
'input:not([type=checkbox])[ngControl],textarea[ngControl],input:not([type=checkbox])[ngFormControl],textarea[ngFormControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]',
|
||||
// TODO: vsavkin replace the above selector with the one below it once
|
||||
// https://github.com/angular/angular/issues/3011 is implemented
|
||||
// selector: '[ng-control],[ng-model],[ng-form-control]',
|
||||
// selector: '[ngControl],[ngModel],[ngFormControl]',
|
||||
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
bindings: [DEFAULT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
var normalizedValue = isBlank(value) ? '' : value;
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', normalizedValue);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
@ -42,4 +42,4 @@ export interface Form {
|
||||
* Update the model for a particular control with a new value.
|
||||
*/
|
||||
updateModel(dir: NgControl, value: any): void;
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import {unimplemented} from '../../../src/facade/exceptions';
|
||||
|
||||
import {ControlValueAccessor} from './control_value_accessor';
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
/**
|
||||
* A base class that all control directive extend.
|
||||
@ -12,8 +14,8 @@ export abstract class NgControl extends AbstractControlDirective {
|
||||
name: string = null;
|
||||
valueAccessor: ControlValueAccessor = null;
|
||||
|
||||
get validator(): Function { return unimplemented(); }
|
||||
get asyncValidator(): Function { return unimplemented(); }
|
||||
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
|
||||
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
|
||||
|
||||
abstract viewToModelUpdate(newValue: any): void;
|
||||
}
|
@ -9,17 +9,19 @@ import {
|
||||
forwardRef,
|
||||
Provider,
|
||||
Self
|
||||
} from 'angular2/core';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
} from '@angular/core';
|
||||
import {ControlContainer} from './control_container';
|
||||
import {controlPath, composeValidators, composeAsyncValidators} from './shared';
|
||||
import {ControlGroup} from '../model';
|
||||
import {Form} from './form_interface';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
const controlGroupProvider =
|
||||
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgControlGroup)}));
|
||||
export const controlGroupProvider: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgControlGroup)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and binds a control group to a DOM element.
|
||||
@ -32,29 +34,26 @@ const controlGroupProvider =
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <div>
|
||||
* <h2>Angular2 Control & ControlGroup Example</h2>
|
||||
* <h2>Angular Control & ControlGroup Example</h2>
|
||||
* <form #f="ngForm">
|
||||
* <div ng-control-group="name" #cg-name="ngForm">
|
||||
* <div ngControlGroup="name" #cg-name="form">
|
||||
* <h3>Enter your name:</h3>
|
||||
* <p>First: <input ng-control="first" required></p>
|
||||
* <p>Middle: <input ng-control="middle"></p>
|
||||
* <p>Last: <input ng-control="last" required></p>
|
||||
* <p>First: <input ngControl="first" required></p>
|
||||
* <p>Middle: <input ngControl="middle"></p>
|
||||
* <p>Last: <input ngControl="last" required></p>
|
||||
* </div>
|
||||
* <h3>Name value:</h3>
|
||||
* <pre>{{valueOf(cgName)}}</pre>
|
||||
* <p>Name is {{cgName?.control?.valid ? "valid" : "invalid"}}</p>
|
||||
* <h3>What's your favorite food?</h3>
|
||||
* <p><input ng-control="food"></p>
|
||||
* <p><input ngControl="food"></p>
|
||||
* <h3>Form value</h3>
|
||||
* <pre>{{valueOf(f)}}</pre>
|
||||
* </form>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [FORM_DIRECTIVES]
|
||||
* `
|
||||
* })
|
||||
* export class App {
|
||||
* valueOf(cg: NgControlGroup): string {
|
||||
@ -70,9 +69,9 @@ const controlGroupProvider =
|
||||
* this group can be accessed separately from the overall form.
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ng-control-group]',
|
||||
selector: '[ngControlGroup]',
|
||||
providers: [controlGroupProvider],
|
||||
inputs: ['name: ng-control-group'],
|
||||
inputs: ['name: ngControlGroup'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgControlGroup extends ControlContainer implements OnInit,
|
||||
@ -106,7 +105,7 @@ export class NgControlGroup extends ControlContainer implements OnInit,
|
||||
*/
|
||||
get formDirective(): Form { return this._parent.formDirective; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
@ -14,8 +11,9 @@ import {
|
||||
Inject,
|
||||
Optional,
|
||||
Self
|
||||
} from 'angular2/core';
|
||||
} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper} from '../../../src/facade/async';
|
||||
import {ControlContainer} from './control_container';
|
||||
import {NgControl} from './ng_control';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
@ -27,11 +25,15 @@ import {
|
||||
selectValueAccessor
|
||||
} from './shared';
|
||||
import {Control} from '../model';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
|
||||
const controlNameBinding =
|
||||
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgControlName)}));
|
||||
export const controlNameBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgControlName)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and binds a control with a specified name to a DOM element.
|
||||
@ -50,10 +52,10 @@ const controlNameBinding =
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `
|
||||
* <form #f="ngForm" (submit)='onLogIn(f.value)'>
|
||||
* Login <input type='text' ng-control='login' #l="ngForm">
|
||||
* <div *ng-if="!l.valid">Login is invalid</div>
|
||||
* Login <input type='text' ngControl='login' #l="form">
|
||||
* <div *ngIf="!l.valid">Login is invalid</div>
|
||||
*
|
||||
* Password <input type='password' ng-control='password'>
|
||||
* Password <input type='password' ngControl='password'>
|
||||
* <button type='submit'>Log in!</button>
|
||||
* </form>
|
||||
* `})
|
||||
@ -64,7 +66,7 @@ const controlNameBinding =
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We can also use ng-model to bind a domain model to the form.
|
||||
* We can also use ngModel to bind a domain model to the form.
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
@ -72,9 +74,9 @@ const controlNameBinding =
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `
|
||||
* <form (submit)='onLogIn()'>
|
||||
* Login <input type='text' ng-control='login' [(ng-model)]="credentials.login">
|
||||
* Password <input type='password' ng-control='password'
|
||||
* [(ng-model)]="credentials.password">
|
||||
* Login <input type='text' ngControl='login' [(ngModel)]="credentials.login">
|
||||
* Password <input type='password' ngControl='password'
|
||||
* [(ngModel)]="credentials.password">
|
||||
* <button type='submit'>Log in!</button>
|
||||
* </form>
|
||||
* `})
|
||||
@ -89,7 +91,7 @@ const controlNameBinding =
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ng-control]',
|
||||
selector: '[ngControl]',
|
||||
bindings: [controlNameBinding],
|
||||
inputs: ['name: ngControl', 'model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
@ -136,9 +138,9 @@ export class NgControlName extends NgControl implements OnChanges,
|
||||
|
||||
get formDirective(): any { return this._parent.formDirective; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
get control(): Control { return this.formDirective.getControl(this); }
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
import {Directive, Self} from 'angular2/core';
|
||||
import {Directive, Self} from '@angular/core';
|
||||
import {NgControl} from './ng_control';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {isPresent} from '../../../src/facade/lang';
|
||||
|
||||
/**
|
||||
* Directive automatically applied to Angular forms that sets CSS classes
|
||||
* based on control status (valid/invalid/dirty/etc).
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ng-control],[ng-model],[ng-form-control]',
|
||||
selector: '[ngControl],[ngModel],[ngFormControl]',
|
||||
host: {
|
||||
'[class.ng-untouched]': 'ngClassUntouched',
|
||||
'[class.ng-touched]': 'ngClassTouched',
|
@ -1,22 +1,22 @@
|
||||
import {Directive, forwardRef, Provider, Optional, Inject, Self} from '@angular/core';
|
||||
import {
|
||||
PromiseWrapper,
|
||||
ObservableWrapper,
|
||||
EventEmitter,
|
||||
PromiseCompleter
|
||||
} from 'angular2/src/facade/async';
|
||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Directive, forwardRef, Provider, Optional, Inject, Self} from 'angular2/core';
|
||||
} from '../../../src/facade/async';
|
||||
import {ListWrapper} from '../../../src/facade/collection';
|
||||
import {isPresent} from '../../../src/facade/lang';
|
||||
import {NgControl} from './ng_control';
|
||||
import {Form} from './form_interface';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {ControlContainer} from './control_container';
|
||||
import {AbstractControl, ControlGroup, Control} from '../model';
|
||||
import {setUpControl, setUpControlGroup, composeValidators, composeAsyncValidators} from './shared';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
|
||||
const formDirectiveProvider =
|
||||
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgForm)}));
|
||||
export const formDirectiveProvider: any =
|
||||
/*@ts2dart_const*/ {provide: ControlContainer, useExisting: forwardRef(() => NgForm)};
|
||||
|
||||
/**
|
||||
* If `NgForm` is bound in a component, `<form>` elements in that component will be
|
||||
@ -36,7 +36,7 @@ const formDirectiveProvider =
|
||||
*
|
||||
* ### Submission
|
||||
*
|
||||
* The `ng-submit` event signals when the user triggers a form submission.
|
||||
* The `ngSubmit` event signals when the user triggers a form submission.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ltdgYj4P0iY64AR71EpL?p=preview))
|
||||
*
|
||||
@ -47,16 +47,16 @@ const formDirectiveProvider =
|
||||
* <div>
|
||||
* <p>Submit the form to see the data object Angular builds</p>
|
||||
* <h2>NgForm demo</h2>
|
||||
* <form #f="ngForm" (ng-submit)="onSubmit(f.value)">
|
||||
* <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">
|
||||
* <h3>Control group: credentials</h3>
|
||||
* <div ng-control-group="credentials">
|
||||
* <p>Login: <input type="text" ng-control="login"></p>
|
||||
* <p>Password: <input type="password" ng-control="password"></p>
|
||||
* <div ngControlGroup="credentials">
|
||||
* <p>Login: <input type="text" ngControl="login"></p>
|
||||
* <p>Password: <input type="password" ngControl="password"></p>
|
||||
* </div>
|
||||
* <h3>Control group: person</h3>
|
||||
* <div ng-control-group="person">
|
||||
* <p>First name: <input type="text" ng-control="firstName"></p>
|
||||
* <p>Last name: <input type="text" ng-control="lastName"></p>
|
||||
* <div ngControlGroup="person">
|
||||
* <p>First name: <input type="text" ngControl="firstName"></p>
|
||||
* <p>Last name: <input type="text" ngControl="lastName"></p>
|
||||
* </div>
|
||||
* <button type="submit">Submit Form</button>
|
||||
* <p>Form data submitted:</p>
|
||||
@ -78,7 +78,7 @@ const formDirectiveProvider =
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'form:not([ng-no-form]):not([ng-form-model]),ng-form,[ng-form]',
|
||||
selector: 'form:not([ngNoForm]):not([ngFormModel]),ngForm,[ngForm]',
|
||||
bindings: [formDirectiveProvider],
|
||||
host: {
|
||||
'(submit)': 'onSubmit()',
|
@ -1,6 +1,3 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
@ -11,10 +8,14 @@ import {
|
||||
Inject,
|
||||
Optional,
|
||||
Self
|
||||
} from 'angular2/core';
|
||||
} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper} from '../../../src/facade/collection';
|
||||
import {EventEmitter, ObservableWrapper} from '../../../src/facade/async';
|
||||
|
||||
import {NgControl} from './ng_control';
|
||||
import {Control} from '../model';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {
|
||||
setUpControl,
|
||||
@ -23,9 +24,13 @@ import {
|
||||
isPropertyUpdated,
|
||||
selectValueAccessor
|
||||
} from './shared';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
const formControlBinding =
|
||||
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgFormControl)}));
|
||||
export const formControlBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgFormControl)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds an existing {@link Control} to a DOM element.
|
||||
@ -44,7 +49,7 @@ const formControlBinding =
|
||||
* <h2>NgFormControl Example</h2>
|
||||
* <form>
|
||||
* <p>Element with existing control: <input type="text"
|
||||
* [ng-form-control]="loginControl"></p>
|
||||
* [ngFormControl]="loginControl"></p>
|
||||
* <p>Value of existing control: {{loginControl.value}}</p>
|
||||
* </form>
|
||||
* </div>
|
||||
@ -56,9 +61,9 @@ const formControlBinding =
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ###ng-model
|
||||
* ### ngModel
|
||||
*
|
||||
* We can also use `ng-model` to bind a domain model to the form.
|
||||
* We can also use `ngModel` to bind a domain model to the form.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yHMLuHO7DNgT8XvtjTDH?p=preview))
|
||||
*
|
||||
@ -66,7 +71,7 @@ const formControlBinding =
|
||||
* @Component({
|
||||
* selector: "login-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: "<input type='text' [ng-form-control]='loginControl' [(ng-model)]='login'>"
|
||||
* template: "<input type='text' [ngFormControl]='loginControl' [(ngModel)]='login'>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginControl: Control = new Control('');
|
||||
@ -75,7 +80,7 @@ const formControlBinding =
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ng-form-control]',
|
||||
selector: '[ngFormControl]',
|
||||
bindings: [formControlBinding],
|
||||
inputs: ['form: ngFormControl', 'model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
@ -110,9 +115,9 @@ export class NgFormControl extends NgControl implements OnChanges {
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
get control(): Control { return this.form; }
|
||||
|
@ -1,6 +1,3 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
|
||||
import {
|
||||
SimpleChange,
|
||||
OnChanges,
|
||||
@ -10,7 +7,11 @@ import {
|
||||
Inject,
|
||||
Optional,
|
||||
Self
|
||||
} from 'angular2/core';
|
||||
} from '@angular/core';
|
||||
import {isBlank} from '../../../src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from '../../../src/facade/collection';
|
||||
import {BaseException} from '../../../src/facade/exceptions';
|
||||
import {ObservableWrapper, EventEmitter} from '../../../src/facade/async';
|
||||
import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {ControlContainer} from './control_container';
|
||||
@ -19,8 +20,11 @@ import {Control, ControlGroup} from '../model';
|
||||
import {setUpControl, setUpControlGroup, composeValidators, composeAsyncValidators} from './shared';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
|
||||
const formDirectiveProvider =
|
||||
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgFormModel)}));
|
||||
export const formDirectiveProvider: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgFormModel)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds an existing control group to a DOM element.
|
||||
@ -36,9 +40,9 @@ const formDirectiveProvider =
|
||||
* template: `
|
||||
* <div>
|
||||
* <h2>NgFormModel Example</h2>
|
||||
* <form [ng-form-model]="loginForm">
|
||||
* <p>Login: <input type="text" ng-control="login"></p>
|
||||
* <p>Password: <input type="password" ng-control="password"></p>
|
||||
* <form [ngFormModel]="loginForm">
|
||||
* <p>Login: <input type="text" ngControl="login"></p>
|
||||
* <p>Password: <input type="password" ngControl="password"></p>
|
||||
* </form>
|
||||
* <p>Value:</p>
|
||||
* <pre>{{value}}</pre>
|
||||
@ -62,17 +66,17 @@ const formDirectiveProvider =
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We can also use ng-model to bind a domain model to the form.
|
||||
* We can also use ngModel to bind a domain model to the form.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: "login-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `
|
||||
* <form [ng-form-model]='loginForm'>
|
||||
* Login <input type='text' ng-control='login' [(ng-model)]='credentials.login'>
|
||||
* Password <input type='password' ng-control='password'
|
||||
* [(ng-model)]='credentials.password'>
|
||||
* <form [ngFormModel]='loginForm'>
|
||||
* Login <input type='text' ngControl='login' [(ngModel)]='credentials.login'>
|
||||
* Password <input type='password' ngControl='password'
|
||||
* [(ngModel)]='credentials.password'>
|
||||
* <button (click)="onLogin()">Login</button>
|
||||
* </form>`
|
||||
* })
|
||||
@ -95,9 +99,9 @@ const formDirectiveProvider =
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ng-form-model]',
|
||||
selector: '[ngFormModel]',
|
||||
bindings: [formDirectiveProvider],
|
||||
inputs: ['form: ng-form-model'],
|
||||
inputs: ['form: ngFormModel'],
|
||||
host: {'(submit)': 'onSubmit()'},
|
||||
outputs: ['ngSubmit'],
|
||||
exportAs: 'ngForm'
|
||||
@ -114,6 +118,7 @@ export class NgFormModel extends ControlContainer implements Form,
|
||||
}
|
||||
|
||||
ngOnChanges(changes: {[key: string]: SimpleChange}): void {
|
||||
this._checkFormPresent();
|
||||
if (StringMapWrapper.contains(changes, "form")) {
|
||||
var sync = composeValidators(this._validators);
|
||||
this.form.validator = Validators.compose([this.form.validator, sync]);
|
||||
@ -173,4 +178,11 @@ export class NgFormModel extends ControlContainer implements Form,
|
||||
dir.valueAccessor.writeValue(ctrl.value);
|
||||
});
|
||||
}
|
||||
|
||||
private _checkFormPresent() {
|
||||
if (isBlank(this.form)) {
|
||||
throw new BaseException(
|
||||
`ngFormModel expects a form. Please pass one in. Example: <form [ngFormModel]="myCoolForm">`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +1,18 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
Query,
|
||||
Directive,
|
||||
forwardRef,
|
||||
Provider,
|
||||
Inject,
|
||||
Optional,
|
||||
Self
|
||||
} from 'angular2/core';
|
||||
} from '@angular/core';
|
||||
import {EventEmitter, ObservableWrapper} from '../../../src/facade/async';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {Control} from '../model';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {
|
||||
setUpControl,
|
||||
isPropertyUpdated,
|
||||
@ -22,17 +20,21 @@ import {
|
||||
composeValidators,
|
||||
composeAsyncValidators
|
||||
} from './shared';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
const formControlBinding =
|
||||
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgModel)}));
|
||||
export const formControlBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgModel)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds a domain model to a form control.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* `ng-model` binds an existing domain model to a form control. For a
|
||||
* two-way binding, use `[(ng-model)]` to ensure the model updates in
|
||||
* `ngModel` binds an existing domain model to a form control. For a
|
||||
* two-way binding, use `[(ngModel)]` to ensure the model updates in
|
||||
* both directions.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/R3UX5qDaUqFO2VYR0UzH?p=preview))
|
||||
@ -40,7 +42,7 @@ const formControlBinding =
|
||||
* @Component({
|
||||
* selector: "search-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `<input type='text' [(ng-model)]="searchQuery">`
|
||||
* template: `<input type='text' [(ngModel)]="searchQuery">`
|
||||
* })
|
||||
* class SearchComp {
|
||||
* searchQuery: string;
|
||||
@ -48,7 +50,7 @@ const formControlBinding =
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ng-model]:not([ng-control]):not([ng-form-control])',
|
||||
selector: '[ngModel]:not([ngControl]):not([ngFormControl])',
|
||||
bindings: [formControlBinding],
|
||||
inputs: ['model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
@ -88,9 +90,9 @@ export class NgModel extends NgControl implements OnChanges {
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
@ -10,3 +10,11 @@ Function normalizeValidator(dynamic validator){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Function normalizeAsyncValidator(dynamic validator){
|
||||
if (validator is Validator) {
|
||||
return (c) => validator.validate(c);
|
||||
} else {
|
||||
return validator;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import {AbstractControl} from '../model';
|
||||
import {Validator, ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
||||
} else {
|
||||
return <ValidatorFn>validator;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => Promise.resolve((<Validator>validator).validate(c));
|
||||
} else {
|
||||
return <AsyncValidatorFn>validator;
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
import {Directive, ElementRef, Renderer, Self, forwardRef, Provider} from 'angular2/core';
|
||||
import {Directive, ElementRef, Renderer, Self, forwardRef, Provider} from '@angular/core';
|
||||
import {NumberWrapper} from '../../../src/facade/lang';
|
||||
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||
import {isBlank, CONST_EXPR, NumberWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => NumberValueAccessor), multi: true}));
|
||||
export const NUMBER_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => NumberValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a number value and listening to changes that is used by the
|
||||
@ -11,12 +14,12 @@ const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* <input type="number" [(ng-model)]="age">
|
||||
* <input type="number" [(ngModel)]="age">
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input[type=number][ng-control],input[type=number][ng-form-control],input[type=number][ng-model]',
|
||||
'input[type=number][ngControl],input[type=number][ngFormControl],input[type=number][ngModel]',
|
||||
host: {
|
||||
'(change)': 'onChange($event.target.value)',
|
||||
'(input)': 'onChange($event.target.value)',
|
||||
@ -25,17 +28,17 @@ const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
bindings: [NUMBER_VALUE_ACCESSOR]
|
||||
})
|
||||
export class NumberValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: number): void {
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number) => void): void {
|
||||
this.onChange = (value) => { fn(NumberWrapper.parseFloat(value)); };
|
||||
this.onChange = (value) => { fn(value == '' ? null : NumberWrapper.parseFloat(value)); };
|
||||
}
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
Renderer,
|
||||
forwardRef,
|
||||
Provider,
|
||||
Input,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Injector,
|
||||
Injectable
|
||||
} from '@angular/core';
|
||||
import {isPresent} from '../../../src/facade/lang';
|
||||
import {ListWrapper} from '../../../src/facade/collection';
|
||||
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
|
||||
export const RADIO_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => RadioControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal class used by Angular to uncheck radio buttons with the matching name.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RadioControlRegistry {
|
||||
private _accessors: any[] = [];
|
||||
|
||||
add(control: NgControl, accessor: RadioControlValueAccessor) {
|
||||
this._accessors.push([control, accessor]);
|
||||
}
|
||||
|
||||
remove(accessor: RadioControlValueAccessor) {
|
||||
var indexToRemove = -1;
|
||||
for (var i = 0; i < this._accessors.length; ++i) {
|
||||
if (this._accessors[i][1] === accessor) {
|
||||
indexToRemove = i;
|
||||
}
|
||||
}
|
||||
ListWrapper.removeAt(this._accessors, indexToRemove);
|
||||
}
|
||||
|
||||
select(accessor: RadioControlValueAccessor) {
|
||||
this._accessors.forEach((c) => {
|
||||
if (c[0].control.root === accessor._control.control.root && c[1] !== accessor) {
|
||||
c[1].fireUncheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value provided by the forms API for radio buttons.
|
||||
*/
|
||||
export class RadioButtonState {
|
||||
constructor(public checked: boolean, public value: string) {}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The accessor for writing a radio control value and listening to changes that is used by the
|
||||
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* @Component({
|
||||
* template: `
|
||||
* <input type="radio" name="food" [(ngModel)]="foodChicken">
|
||||
* <input type="radio" name="food" [(ngModel)]="foodFish">
|
||||
* `
|
||||
* })
|
||||
* class FoodCmp {
|
||||
* foodChicken = new RadioButtonState(true, "chicken");
|
||||
* foodFish = new RadioButtonState(false, "fish");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input[type=radio][ngControl],input[type=radio][ngFormControl],input[type=radio][ngModel]',
|
||||
host: {'(change)': 'onChange()', '(blur)': 'onTouched()'},
|
||||
providers: [RADIO_VALUE_ACCESSOR]
|
||||
})
|
||||
export class RadioControlValueAccessor implements ControlValueAccessor,
|
||||
OnDestroy, OnInit {
|
||||
/** @internal */
|
||||
_state: RadioButtonState;
|
||||
/** @internal */
|
||||
_control: NgControl;
|
||||
@Input() name: string;
|
||||
/** @internal */
|
||||
_fn: Function;
|
||||
onChange = () => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef,
|
||||
private _registry: RadioControlRegistry, private _injector: Injector) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._control = this._injector.get(NgControl);
|
||||
this._registry.add(this._control, this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this._registry.remove(this); }
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._state = value;
|
||||
if (isPresent(value) && value.checked) {
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => {}): void {
|
||||
this._fn = fn;
|
||||
this.onChange = () => {
|
||||
fn(new RadioButtonState(true, this._state.value));
|
||||
this._registry.select(this);
|
||||
};
|
||||
}
|
||||
|
||||
fireUncheck(): void { this._fn(new RadioButtonState(false, this._state.value)); }
|
||||
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
import {
|
||||
Directive,
|
||||
Renderer,
|
||||
forwardRef,
|
||||
Provider,
|
||||
ElementRef,
|
||||
Input,
|
||||
Host,
|
||||
OnDestroy,
|
||||
Optional
|
||||
} from '@angular/core';
|
||||
import {
|
||||
StringWrapper,
|
||||
isPrimitive,
|
||||
isPresent,
|
||||
isBlank,
|
||||
looseIdentical
|
||||
} from '../../../src/facade/lang';
|
||||
import {MapWrapper} from '../../../src/facade/collection';
|
||||
|
||||
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||
|
||||
export const SELECT_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
function _buildValueString(id: string, value: any): string {
|
||||
if (isBlank(id)) return `${value}`;
|
||||
if (!isPrimitive(value)) value = "Object";
|
||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
||||
}
|
||||
|
||||
function _extractId(valueString: string): string {
|
||||
return valueString.split(":")[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*
|
||||
* Note: We have to listen to the 'change' event because 'input' events aren't fired
|
||||
* for selects in Firefox and IE:
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1024350
|
||||
* https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/
|
||||
*
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
|
||||
host: {'(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
value: any;
|
||||
/** @internal */
|
||||
_optionMap: Map<string, any> = new Map<string, any>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
var valueString = _buildValueString(this._getOptionId(value), value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (valueString: string) => { fn(this._getOptionValue(valueString)); };
|
||||
}
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/** @internal */
|
||||
_registerOption(): string { return (this._idCounter++).toString(); }
|
||||
|
||||
/** @internal */
|
||||
_getOptionId(value: any): string {
|
||||
for (let id of MapWrapper.keys(this._optionMap)) {
|
||||
if (looseIdentical(this._optionMap.get(id), value)) return id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionValue(valueString: string): any {
|
||||
let value = this._optionMap.get(_extractId(valueString));
|
||||
return isPresent(value) ? value : valueString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <select ngControl="city">
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectOption implements OnDestroy {
|
||||
id: string;
|
||||
|
||||
constructor(private _element: ElementRef, private _renderer: Renderer,
|
||||
@Optional() @Host() private _select: SelectControlValueAccessor) {
|
||||
if (isPresent(this._select)) this.id = this._select._registerOption();
|
||||
}
|
||||
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
this._select._optionMap.set(this.id, value);
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
this._setElementValue(value);
|
||||
if (isPresent(this._select)) this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setElementValue(value: string): void {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (isPresent(this._select)) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent, looseIdentical} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper, StringMapWrapper} from '../../../src/facade/collection';
|
||||
import {isBlank, isPresent, looseIdentical, hasConstructor} from '../../../src/facade/lang';
|
||||
import {BaseException} from '../../../src/facade/exceptions';
|
||||
|
||||
import {ControlContainer} from './control_container';
|
||||
import {NgControl} from './ng_control';
|
||||
@ -13,7 +13,9 @@ import {DefaultValueAccessor} from './default_value_accessor';
|
||||
import {NumberValueAccessor} from './number_value_accessor';
|
||||
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
||||
import {SelectControlValueAccessor} from './select_control_value_accessor';
|
||||
import {normalizeValidator} from './normalize_validator';
|
||||
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
||||
import {normalizeValidator, normalizeAsyncValidator} from './normalize_validator';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
|
||||
export function controlPath(name: string, parent: ControlContainer): string[] {
|
||||
@ -31,14 +33,14 @@ export function setUpControl(control: Control, dir: NgControl): void {
|
||||
dir.valueAccessor.writeValue(control.value);
|
||||
|
||||
// view -> model
|
||||
dir.valueAccessor.registerOnChange(newValue => {
|
||||
dir.valueAccessor.registerOnChange((newValue: any) => {
|
||||
dir.viewToModelUpdate(newValue);
|
||||
control.updateValue(newValue, {emitModelToViewChange: false});
|
||||
control.markAsDirty();
|
||||
});
|
||||
|
||||
// model -> view
|
||||
control.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
|
||||
control.registerOnChange((newValue: any) => dir.valueAccessor.writeValue(newValue));
|
||||
|
||||
// touched
|
||||
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
|
||||
@ -55,13 +57,14 @@ function _throwError(dir: AbstractControlDirective, message: string): void {
|
||||
throw new BaseException(`${message} '${path}'`);
|
||||
}
|
||||
|
||||
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): Function {
|
||||
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
|
||||
return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null;
|
||||
}
|
||||
|
||||
export function composeAsyncValidators(
|
||||
validators: /* Array<Validator|Function> */ any[]): Function {
|
||||
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeValidator)) : null;
|
||||
validators: /* Array<Validator|Function> */ any[]): AsyncValidatorFn {
|
||||
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
|
||||
null;
|
||||
}
|
||||
|
||||
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
|
||||
@ -77,16 +80,17 @@ export function selectValueAccessor(dir: NgControl,
|
||||
valueAccessors: ControlValueAccessor[]): ControlValueAccessor {
|
||||
if (isBlank(valueAccessors)) return null;
|
||||
|
||||
var defaultAccessor;
|
||||
var builtinAccessor;
|
||||
var customAccessor;
|
||||
|
||||
valueAccessors.forEach(v => {
|
||||
if (v instanceof DefaultValueAccessor) {
|
||||
var defaultAccessor: ControlValueAccessor;
|
||||
var builtinAccessor: ControlValueAccessor;
|
||||
var customAccessor: ControlValueAccessor;
|
||||
valueAccessors.forEach((v: ControlValueAccessor) => {
|
||||
if (hasConstructor(v, DefaultValueAccessor)) {
|
||||
defaultAccessor = v;
|
||||
|
||||
} else if (v instanceof CheckboxControlValueAccessor || v instanceof NumberValueAccessor ||
|
||||
v instanceof SelectControlValueAccessor) {
|
||||
} else if (hasConstructor(v, CheckboxControlValueAccessor) ||
|
||||
hasConstructor(v, NumberValueAccessor) ||
|
||||
hasConstructor(v, SelectControlValueAccessor) ||
|
||||
hasConstructor(v, RadioControlValueAccessor)) {
|
||||
if (isPresent(builtinAccessor))
|
||||
_throwError(dir, "More than one built-in value accessor matches");
|
||||
builtinAccessor = v;
|
150
modules/@angular/common/src/forms/directives/validators.ts
Normal file
150
modules/@angular/common/src/forms/directives/validators.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import {forwardRef, Attribute, Directive} from '@angular/core';
|
||||
import {NumberWrapper} from '../../facade/lang';
|
||||
import {Validators, NG_VALIDATORS} from '../validators';
|
||||
import {AbstractControl} from '../model';
|
||||
import * as modelModule from '../model';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An interface that can be implemented by classes that can act as validators.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* ```typescript
|
||||
* @Directive({
|
||||
* selector: '[custom-validator]',
|
||||
* providers: [provide(NG_VALIDATORS, {useExisting: CustomValidatorDirective, multi: true})]
|
||||
* })
|
||||
* class CustomValidatorDirective implements Validator {
|
||||
* validate(c: Control): {[key: string]: any} {
|
||||
* return {"custom": true};
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface Validator { validate(c: modelModule.AbstractControl): {[key: string]: any}; }
|
||||
|
||||
const REQUIRED = /*@ts2dart_const*/ Validators.required;
|
||||
|
||||
export const REQUIRED_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALIDATORS,
|
||||
useValue: REQUIRED,
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A Directive that adds the `required` validator to any controls marked with the
|
||||
* `required` attribute, via the {@link NG_VALIDATORS} binding.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input ngControl="fullName" required>
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[required][ngControl],[required][ngFormControl],[required][ngModel]',
|
||||
providers: [REQUIRED_VALIDATOR]
|
||||
})
|
||||
export class RequiredValidator {
|
||||
}
|
||||
|
||||
export interface ValidatorFn { (c: AbstractControl): {[key: string]: any}; }
|
||||
export interface AsyncValidatorFn {
|
||||
(c: AbstractControl): any /*Promise<{[key: string]: any}>|Observable<{[key: string]: any}>*/;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provivder which adds {@link MinLengthValidator} to {@link NG_VALIDATORS}.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* {@example common/forms/ts/validators/validators.ts region='min'}
|
||||
*/
|
||||
export const MIN_LENGTH_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MinLengthValidator),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A directive which installs the {@link MinLengthValidator} for any `ngControl`,
|
||||
* `ngFormControl`, or control with `ngModel` that also has a `minlength` attribute.
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[minlength][ngControl],[minlength][ngFormControl],[minlength][ngModel]',
|
||||
providers: [MIN_LENGTH_VALIDATOR]
|
||||
})
|
||||
export class MinLengthValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute("minlength") minLength: string) {
|
||||
this._validator = Validators.minLength(NumberWrapper.parseInt(minLength, 10));
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider which adds {@link MaxLengthValidator} to {@link NG_VALIDATORS}.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* {@example common/forms/ts/validators/validators.ts region='max'}
|
||||
*/
|
||||
export const MAX_LENGTH_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MaxLengthValidator),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A directive which installs the {@link MaxLengthValidator} for any `ngControl, `ngFormControl`,
|
||||
* or control with `ngModel` that also has a `maxlength` attribute.
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[maxlength][ngControl],[maxlength][ngFormControl],[maxlength][ngModel]',
|
||||
providers: [MAX_LENGTH_VALIDATOR]
|
||||
})
|
||||
export class MaxLengthValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute("maxlength") maxLength: string) {
|
||||
this._validator = Validators.maxLength(NumberWrapper.parseInt(maxLength, 10));
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A Directive that adds the `pattern` validator to any controls marked with the
|
||||
* `pattern` attribute, via the {@link NG_VALIDATORS} binding. Uses attribute value
|
||||
* as the regex to validate Control value against. Follows pattern attribute
|
||||
* semantics; i.e. regex must match entire Control value.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input [ngControl]="fullName" pattern="[a-zA-Z ]*">
|
||||
* ```
|
||||
*/
|
||||
export const PATTERN_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => PatternValidator),
|
||||
multi: true
|
||||
};
|
||||
@Directive({
|
||||
selector: '[pattern][ngControl],[pattern][ngFormControl],[pattern][ngModel]',
|
||||
providers: [PATTERN_VALIDATOR]
|
||||
})
|
||||
export class PatternValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute("pattern") pattern: string) {
|
||||
this._validator = Validators.pattern(pattern);
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isArray, CONST_EXPR, Type} from 'angular2/src/facade/lang';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {isPresent, isArray} from '../../src/facade/lang';
|
||||
import * as modelModule from './model';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
|
||||
|
||||
|
||||
/**
|
||||
@ -14,11 +15,11 @@ import * as modelModule from './model';
|
||||
* selector: 'my-app',
|
||||
* viewBindings: [FORM_BINDINGS]
|
||||
* template: `
|
||||
* <form [ng-form-model]="loginForm">
|
||||
* <p>Login <input ng-control="login"></p>
|
||||
* <div ng-control-group="passwordRetry">
|
||||
* <p>Password <input type="password" ng-control="password"></p>
|
||||
* <p>Confirm password <input type="password" ng-control="passwordConfirmation"></p>
|
||||
* <form [ngFormModel]="loginForm">
|
||||
* <p>Login <input ngControl="login"></p>
|
||||
* <div ngControlGroup="passwordRetry">
|
||||
* <p>Password <input type="password" ngControl="password"></p>
|
||||
* <p>Confirm password <input type="password" ngControl="passwordConfirmation"></p>
|
||||
* </div>
|
||||
* </form>
|
||||
* <h3>Form value:</h3>
|
||||
@ -56,16 +57,18 @@ export class FormBuilder {
|
||||
group(controlsConfig: {[key: string]: any},
|
||||
extra: {[key: string]: any} = null): modelModule.ControlGroup {
|
||||
var controls = this._reduceControls(controlsConfig);
|
||||
var optionals = isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null;
|
||||
var validator = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
|
||||
var asyncValidator = isPresent(extra) ? StringMapWrapper.get(extra, "asyncValidator") : null;
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* Construct a new {@link Control} with the given `value`,`validator`, and `asyncValidator`.
|
||||
*/
|
||||
control(value: Object, validator: Function = null,
|
||||
asyncValidator: Function = null): modelModule.Control {
|
||||
control(value: Object, validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null): modelModule.Control {
|
||||
return new modelModule.Control(value, validator, asyncValidator);
|
||||
}
|
||||
|
||||
@ -73,16 +76,17 @@ export class FormBuilder {
|
||||
* Construct an array of {@link Control}s from the given `controlsConfig` array of
|
||||
* configuration, with the given optional `validator` and `asyncValidator`.
|
||||
*/
|
||||
array(controlsConfig: any[], validator: Function = null,
|
||||
asyncValidator: Function = null): modelModule.ControlArray {
|
||||
array(controlsConfig: any[], validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null): modelModule.ControlArray {
|
||||
var controls = controlsConfig.map(c => this._createControl(c));
|
||||
return new modelModule.ControlArray(controls, validator, asyncValidator);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reduceControls(controlsConfig: any): {[key: string]: modelModule.AbstractControl} {
|
||||
_reduceControls(controlsConfig: {[k: string]:
|
||||
any}): {[key: string]: modelModule.AbstractControl} {
|
||||
var controls: {[key: string]: modelModule.AbstractControl} = {};
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig, controlName) => {
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig: any, controlName: string) => {
|
||||
controls[controlName] = this._createControl(controlConfig);
|
||||
});
|
||||
return controls;
|
||||
@ -97,8 +101,8 @@ export class FormBuilder {
|
||||
|
||||
} else if (isArray(controlConfig)) {
|
||||
var value = controlConfig[0];
|
||||
var validator = controlConfig.length > 1 ? controlConfig[1] : null;
|
||||
var asyncValidator = controlConfig.length > 2 ? controlConfig[2] : null;
|
||||
var validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
|
||||
var asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
|
||||
return this.control(value, validator, asyncValidator);
|
||||
|
||||
} else {
|
||||
@ -106,19 +110,3 @@ export class FormBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand set of providers used for building Angular forms.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* bootstrap(MyApp, [FORM_PROVIDERS]);
|
||||
* ```
|
||||
*/
|
||||
export const FORM_PROVIDERS: Type[] = CONST_EXPR([FormBuilder]);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const FORM_BINDINGS = FORM_PROVIDERS;
|
@ -1,7 +1,8 @@
|
||||
import {StringWrapper, isPresent, isBlank, normalizeBool} from 'angular2/src/facade/lang';
|
||||
import {Observable, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, normalizeBool} from '../../src/facade/lang';
|
||||
import {Observable, EventEmitter, ObservableWrapper} from '../../src/facade/async';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {StringMapWrapper, ListWrapper} from '../../src/facade/collection';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
|
||||
|
||||
/**
|
||||
* Indicates that a Control is valid, i.e. that no errors exist in the input value.
|
||||
@ -14,7 +15,7 @@ export const VALID = "VALID";
|
||||
export const INVALID = "INVALID";
|
||||
|
||||
/**
|
||||
* Indicates that a Control is pending, i.e. that async validation is occuring and
|
||||
* Indicates that a Control is pending, i.e. that async validation is occurring and
|
||||
* errors are not yet available for the input value.
|
||||
*/
|
||||
export const PENDING = "PENDING";
|
||||
@ -62,9 +63,9 @@ export abstract class AbstractControl {
|
||||
private _pristine: boolean = true;
|
||||
private _touched: boolean = false;
|
||||
private _parent: ControlGroup | ControlArray;
|
||||
private _asyncValidationSubscription;
|
||||
private _asyncValidationSubscription: any;
|
||||
|
||||
constructor(public validator: Function, public asyncValidator: Function) {}
|
||||
constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {}
|
||||
|
||||
get value(): any { return this._value; }
|
||||
|
||||
@ -137,15 +138,17 @@ export abstract class AbstractControl {
|
||||
}
|
||||
}
|
||||
|
||||
private _runValidator() { return isPresent(this.validator) ? this.validator(this) : null; }
|
||||
private _runValidator(): {[key: string]: any} {
|
||||
return isPresent(this.validator) ? this.validator(this) : null;
|
||||
}
|
||||
|
||||
private _runAsyncValidator(emitEvent: boolean): void {
|
||||
if (isPresent(this.asyncValidator)) {
|
||||
this._status = PENDING;
|
||||
this._cancelExistingSubscription();
|
||||
var obs = toObservable(this.asyncValidator(this));
|
||||
this._asyncValidationSubscription =
|
||||
ObservableWrapper.subscribe(obs, res => this.setErrors(res, {emitEvent: emitEvent}));
|
||||
this._asyncValidationSubscription = ObservableWrapper.subscribe(
|
||||
obs, (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,6 +211,16 @@ export abstract class AbstractControl {
|
||||
return isPresent(this.getError(errorCode, path));
|
||||
}
|
||||
|
||||
get root(): AbstractControl {
|
||||
let x: AbstractControl = this;
|
||||
|
||||
while (isPresent(x._parent)) {
|
||||
x = x._parent;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_updateControlsErrors(): void {
|
||||
this._status = this._calculateStatus();
|
||||
@ -258,7 +271,8 @@ export class Control extends AbstractControl {
|
||||
/** @internal */
|
||||
_onChange: Function;
|
||||
|
||||
constructor(value: any = null, validator: Function = null, asyncValidator: Function = null) {
|
||||
constructor(value: any = null, validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null) {
|
||||
super(validator, asyncValidator);
|
||||
this._value = value;
|
||||
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
|
||||
@ -307,9 +321,10 @@ export class Control extends AbstractControl {
|
||||
/**
|
||||
* Defines a part of a form, of fixed length, that can contain other controls.
|
||||
*
|
||||
* A `ControlGroup` aggregates the values and errors of each {@link Control} in the group. Thus, if
|
||||
* one of the controls in a group is invalid, the entire group is invalid. Similarly, if a control
|
||||
* changes its value, the entire group changes as well.
|
||||
* A `ControlGroup` aggregates the values of each {@link Control} in the group.
|
||||
* The status of a `ControlGroup` depends on the status of its children.
|
||||
* If one of the controls in a group is invalid, the entire group is invalid.
|
||||
* Similarly, if a control changes its value, the entire group changes as well.
|
||||
*
|
||||
* `ControlGroup` is one of the three fundamental building blocks used to define forms in Angular,
|
||||
* along with {@link Control} and {@link ControlArray}. {@link ControlArray} can also contain other
|
||||
@ -321,8 +336,8 @@ export class ControlGroup extends AbstractControl {
|
||||
private _optionals: {[key: string]: boolean};
|
||||
|
||||
constructor(public controls: {[key: string]: AbstractControl},
|
||||
optionals: {[key: string]: boolean} = null, validator: Function = null,
|
||||
asyncValidator: Function = null) {
|
||||
optionals: {[key: string]: boolean} = null, validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null) {
|
||||
super(validator, asyncValidator);
|
||||
this._optionals = isPresent(optionals) ? optionals : {};
|
||||
this._initObservables();
|
||||
@ -369,7 +384,8 @@ export class ControlGroup extends AbstractControl {
|
||||
|
||||
/** @internal */
|
||||
_setParentForControls() {
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => { control.setParent(this); });
|
||||
StringMapWrapper.forEach(
|
||||
this.controls, (control: AbstractControl, name: string) => { control.setParent(this); });
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -378,7 +394,7 @@ export class ControlGroup extends AbstractControl {
|
||||
/** @internal */
|
||||
_anyControlsHaveStatus(status: string): boolean {
|
||||
var res = false;
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
|
||||
res = res || (this.contains(name) && control.status == status);
|
||||
});
|
||||
return res;
|
||||
@ -386,16 +402,17 @@ export class ControlGroup extends AbstractControl {
|
||||
|
||||
/** @internal */
|
||||
_reduceValue() {
|
||||
return this._reduceChildren({}, (acc, control, name) => {
|
||||
acc[name] = control.value;
|
||||
return acc;
|
||||
});
|
||||
return this._reduceChildren(
|
||||
{}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
|
||||
acc[name] = control.value;
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reduceChildren(initValue: any, fn: Function) {
|
||||
var res = initValue;
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
|
||||
if (this._included(name)) {
|
||||
res = fn(res, control, name);
|
||||
}
|
||||
@ -413,9 +430,10 @@ export class ControlGroup extends AbstractControl {
|
||||
/**
|
||||
* Defines a part of a form, of variable length, that can contain other controls.
|
||||
*
|
||||
* A `ControlArray` aggregates the values and errors of each {@link Control} in the group. Thus, if
|
||||
* one of the controls in a group is invalid, the entire group is invalid. Similarly, if a control
|
||||
* changes its value, the entire group changes as well.
|
||||
* A `ControlArray` aggregates the values of each {@link Control} in the group.
|
||||
* The status of a `ControlArray` depends on the status of its children.
|
||||
* If one of the controls in a group is invalid, the entire array is invalid.
|
||||
* Similarly, if a control changes its value, the entire array changes as well.
|
||||
*
|
||||
* `ControlArray` is one of the three fundamental building blocks used to define forms in Angular,
|
||||
* along with {@link Control} and {@link ControlGroup}. {@link ControlGroup} can also contain
|
||||
@ -432,8 +450,8 @@ export class ControlGroup extends AbstractControl {
|
||||
* ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
|
||||
*/
|
||||
export class ControlArray extends AbstractControl {
|
||||
constructor(public controls: AbstractControl[], validator: Function = null,
|
||||
asyncValidator: Function = null) {
|
||||
constructor(public controls: AbstractControl[], validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null) {
|
||||
super(validator, asyncValidator);
|
||||
this._initObservables();
|
||||
this._setParentForControls();
|
144
modules/@angular/common/src/forms/validators.ts
Normal file
144
modules/@angular/common/src/forms/validators.ts
Normal file
@ -0,0 +1,144 @@
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
import {isBlank, isPresent, isString} from '../../src/facade/lang';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {ObservableWrapper} from '../../src/facade/async';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import * as modelModule from './model';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
|
||||
|
||||
/**
|
||||
* Providers for validators to be used for {@link Control}s in a form.
|
||||
*
|
||||
* Provide this using `multi: true` to add validators.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/forms/ts/ng_validators/ng_validators.ts region='ng_validators'}
|
||||
*/
|
||||
export const NG_VALIDATORS: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken("NgValidators");
|
||||
|
||||
/**
|
||||
* Providers for asynchronous validators to be used for {@link Control}s
|
||||
* in a form.
|
||||
*
|
||||
* Provide this using `multi: true` to add validators.
|
||||
*
|
||||
* See {@link NG_VALIDATORS} for more details.
|
||||
*/
|
||||
export const NG_ASYNC_VALIDATORS: OpaqueToken =
|
||||
/*@ts2dart_const*/ new OpaqueToken("NgAsyncValidators");
|
||||
|
||||
/**
|
||||
* Provides a set of validators used by form controls.
|
||||
*
|
||||
* A validator is a function that processes a {@link Control} or collection of
|
||||
* controls and returns a map of errors. A null map means that validation has passed.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* var loginControl = new Control("", Validators.required)
|
||||
* ```
|
||||
*/
|
||||
export class Validators {
|
||||
/**
|
||||
* Validator that requires controls to have a non-empty value.
|
||||
*/
|
||||
static required(control: modelModule.AbstractControl): {[key: string]: boolean} {
|
||||
return isBlank(control.value) || (isString(control.value) && control.value == "") ?
|
||||
{"required": true} :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator that requires controls to have a value of a minimum length.
|
||||
*/
|
||||
static minLength(minLength: number): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
var v: string = control.value;
|
||||
return v.length < minLength ?
|
||||
{"minlength": {"requiredLength": minLength, "actualLength": v.length}} :
|
||||
null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator that requires controls to have a value of a maximum length.
|
||||
*/
|
||||
static maxLength(maxLength: number): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
var v: string = control.value;
|
||||
return v.length > maxLength ?
|
||||
{"maxlength": {"requiredLength": maxLength, "actualLength": v.length}} :
|
||||
null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator that requires a control to match a regex to its value.
|
||||
*/
|
||||
static pattern(pattern: string): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
let regex = new RegExp(`^${pattern}$`);
|
||||
let v: string = control.value;
|
||||
return regex.test(v) ? null :
|
||||
{"pattern": {"requiredPattern": `^${pattern}$`, "actualValue": v}};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op validator.
|
||||
*/
|
||||
static nullValidator(c: modelModule.AbstractControl): {[key: string]: boolean} { return null; }
|
||||
|
||||
/**
|
||||
* Compose multiple validators into a single function that returns the union
|
||||
* of the individual error maps.
|
||||
*/
|
||||
static compose(validators: ValidatorFn[]): ValidatorFn {
|
||||
if (isBlank(validators)) return null;
|
||||
var presentValidators = validators.filter(isPresent);
|
||||
if (presentValidators.length == 0) return null;
|
||||
|
||||
return function(control: modelModule.AbstractControl) {
|
||||
return _mergeErrors(_executeValidators(control, presentValidators));
|
||||
};
|
||||
}
|
||||
|
||||
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn {
|
||||
if (isBlank(validators)) return null;
|
||||
var presentValidators = validators.filter(isPresent);
|
||||
if (presentValidators.length == 0) return null;
|
||||
|
||||
return function(control: modelModule.AbstractControl) {
|
||||
let promises = _executeAsyncValidators(control, presentValidators).map(_convertToPromise);
|
||||
return PromiseWrapper.all(promises).then(_mergeErrors);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _convertToPromise(obj: any): any {
|
||||
return PromiseWrapper.isPromise(obj) ? obj : ObservableWrapper.toPromise(obj);
|
||||
}
|
||||
|
||||
function _executeValidators(control: modelModule.AbstractControl,
|
||||
validators: ValidatorFn[]): any[] {
|
||||
return validators.map(v => v(control));
|
||||
}
|
||||
|
||||
function _executeAsyncValidators(control: modelModule.AbstractControl,
|
||||
validators: AsyncValidatorFn[]): any[] {
|
||||
return validators.map(v => v(control));
|
||||
}
|
||||
|
||||
function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} {
|
||||
var res: {[key: string]: any} =
|
||||
arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => {
|
||||
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
|
||||
}, {});
|
||||
return StringMapWrapper.isEmpty(res) ? null : res;
|
||||
}
|
5
modules/@angular/common/src/location.ts
Normal file
5
modules/@angular/common/src/location.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from './location/platform_location';
|
||||
export * from './location/location_strategy';
|
||||
export * from './location/hash_location_strategy';
|
||||
export * from './location/path_location_strategy';
|
||||
export * from './location/location';
|
@ -1,13 +1,10 @@
|
||||
import {Injectable, Inject, Optional} from 'angular2/core';
|
||||
import {
|
||||
LocationStrategy,
|
||||
joinWithSlash,
|
||||
APP_BASE_HREF,
|
||||
normalizeQueryParams
|
||||
} from './location_strategy';
|
||||
import {EventListener} from 'angular2/src/facade/browser';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {PlatformLocation} from './platform_location';
|
||||
import {Injectable, Inject, Optional} from '@angular/core';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
|
||||
import {LocationStrategy, APP_BASE_HREF} from './location_strategy';
|
||||
import {Location} from './location';
|
||||
import {UrlChangeListener, PlatformLocation} from './platform_location';
|
||||
|
||||
|
||||
/**
|
||||
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||
@ -21,15 +18,17 @@ import {PlatformLocation} from './platform_location';
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component, provide} from 'angular2/angular2';
|
||||
* import {Component, provide} from '@angular/core';
|
||||
* import {
|
||||
* ROUTER_DIRECTIVES,
|
||||
* ROUTER_PROVIDERS,
|
||||
* RouteConfig,
|
||||
* Location,
|
||||
* LocationStrategy,
|
||||
* HashLocationStrategy
|
||||
* } from 'angular2/router';
|
||||
* } from '@angular/common';
|
||||
* import {
|
||||
* ROUTER_DIRECTIVES,
|
||||
* ROUTER_PROVIDERS,
|
||||
* RouteConfig
|
||||
* } from '@angular/router';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
@ -58,7 +57,7 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
onPopState(fn: EventListener): void {
|
||||
onPopState(fn: UrlChangeListener): void {
|
||||
this._platformLocation.onPopState(fn);
|
||||
this._platformLocation.onHashChange(fn);
|
||||
}
|
||||
@ -69,21 +68,21 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
// 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) +
|
||||
normalizeQueryParams(this._platformLocation.search);
|
||||
return (path.length > 0 ? path.substring(1) : path);
|
||||
}
|
||||
|
||||
prepareExternalUrl(internal: string): string {
|
||||
var url = joinWithSlash(this._baseHref, internal);
|
||||
var url = Location.joinWithSlash(this._baseHref, internal);
|
||||
return url.length > 0 ? ('#' + url) : url;
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams));
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
@ -91,7 +90,7 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams));
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import {Injectable, Inject} from '@angular/core';
|
||||
import {EventEmitter, ObservableWrapper} from '../../src/facade/async';
|
||||
|
||||
import {LocationStrategy} from './location_strategy';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {Injectable, Inject} from 'angular2/core';
|
||||
|
||||
/**
|
||||
* `Location` is a service that applications can use to interact with a browser's URL.
|
||||
@ -21,13 +22,13 @@ import {Injectable, Inject} from 'angular2/core';
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from 'angular2/angular2';
|
||||
* import {Component} from '@angular/core';
|
||||
* import {Location} from '@angular/common';
|
||||
* import {
|
||||
* ROUTER_DIRECTIVES,
|
||||
* ROUTER_PROVIDERS,
|
||||
* RouteConfig,
|
||||
* Location
|
||||
* } from 'angular2/router';
|
||||
* RouteConfig
|
||||
* } from '@angular/router';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
@ -51,7 +52,7 @@ export class Location {
|
||||
|
||||
constructor(public platformStrategy: LocationStrategy) {
|
||||
var browserBaseHref = this.platformStrategy.getBaseHref();
|
||||
this._baseHref = stripTrailingSlash(stripIndexHtml(browserBaseHref));
|
||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||
this.platformStrategy.onPopState((ev) => {
|
||||
ObservableWrapper.callEmit(this._subject, {'url': this.path(), 'pop': true, 'type': ev.type});
|
||||
});
|
||||
@ -67,7 +68,7 @@ export class Location {
|
||||
* trailing slashes
|
||||
*/
|
||||
normalize(url: string): string {
|
||||
return stripTrailingSlash(_stripBaseHref(this._baseHref, stripIndexHtml(url)));
|
||||
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,6 +118,50 @@ export class Location {
|
||||
onReturn: () => void = null): Object {
|
||||
return ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string of url parameters, prepend with '?' if needed, otherwise return parameters as
|
||||
* is.
|
||||
*/
|
||||
public static normalizeQueryParams(params: string): string {
|
||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given 2 parts of a url, join them with a slash if needed.
|
||||
*/
|
||||
public static joinWithSlash(start: string, end: string): string {
|
||||
if (start.length == 0) {
|
||||
return end;
|
||||
}
|
||||
if (end.length == 0) {
|
||||
return start;
|
||||
}
|
||||
var slashes = 0;
|
||||
if (start.endsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
if (end.startsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
if (slashes == 2) {
|
||||
return start + end.substring(1);
|
||||
}
|
||||
if (slashes == 1) {
|
||||
return start + end;
|
||||
}
|
||||
return start + '/' + end;
|
||||
}
|
||||
|
||||
/**
|
||||
* If url has a trailing slash, remove it, otherwise return url as is.
|
||||
*/
|
||||
public static stripTrailingSlash(url: string): string {
|
||||
if (/\/$/g.test(url)) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
function _stripBaseHref(baseHref: string, url: string): string {
|
||||
@ -126,17 +171,10 @@ function _stripBaseHref(baseHref: string, url: string): string {
|
||||
return url;
|
||||
}
|
||||
|
||||
function stripIndexHtml(url: string): string {
|
||||
function _stripIndexHtml(url: string): string {
|
||||
if (/\/index.html$/g.test(url)) {
|
||||
// '/index.html'.length == 11
|
||||
return url.substring(0, url.length - 11);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function stripTrailingSlash(url: string): string {
|
||||
if (/\/$/g.test(url)) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {OpaqueToken} from 'angular2/core';
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
import {UrlChangeListener} from './platform_location';
|
||||
|
||||
/**
|
||||
* `LocationStrategy` is responsible for representing and reading route state
|
||||
* from the browser's URL. Angular provides two strategies:
|
||||
* {@link HashLocationStrategy} (default) and {@link PathLocationStrategy}.
|
||||
* {@link HashLocationStrategy} and {@link PathLocationStrategy} (default).
|
||||
*
|
||||
* This is used under the hood of the {@link Location} service.
|
||||
*
|
||||
@ -24,7 +24,7 @@ export abstract class LocationStrategy {
|
||||
abstract replaceState(state: any, title: string, url: string, queryParams: string): void;
|
||||
abstract forward(): void;
|
||||
abstract back(): void;
|
||||
abstract onPopState(fn: (_: any) => any): void;
|
||||
abstract onPopState(fn: UrlChangeListener): void;
|
||||
abstract getBaseHref(): string;
|
||||
}
|
||||
|
||||
@ -40,8 +40,9 @@ export abstract class LocationStrategy {
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from 'angular2/angular2';
|
||||
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
|
||||
* import {Component} from '@angular/core';
|
||||
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from '@angular/router';
|
||||
* import {APP_BASE_HREF} from '@angular/common';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
@ -53,36 +54,8 @@ export abstract class LocationStrategy {
|
||||
*
|
||||
* bootstrap(AppCmp, [
|
||||
* ROUTER_PROVIDERS,
|
||||
* PathLocationStrategy,
|
||||
* provide(APP_BASE_HREF, {useValue: '/my/app'})
|
||||
* ]);
|
||||
* ```
|
||||
*/
|
||||
export const APP_BASE_HREF: OpaqueToken = CONST_EXPR(new OpaqueToken('appBaseHref'));
|
||||
|
||||
export function normalizeQueryParams(params: string): string {
|
||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||
}
|
||||
|
||||
export function joinWithSlash(start: string, end: string): string {
|
||||
if (start.length == 0) {
|
||||
return end;
|
||||
}
|
||||
if (end.length == 0) {
|
||||
return start;
|
||||
}
|
||||
var slashes = 0;
|
||||
if (start.endsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
if (end.startsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
if (slashes == 2) {
|
||||
return start + end.substring(1);
|
||||
}
|
||||
if (slashes == 1) {
|
||||
return start + end;
|
||||
}
|
||||
return start + '/' + end;
|
||||
}
|
||||
export const APP_BASE_HREF: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('appBaseHref');
|
@ -1,14 +1,10 @@
|
||||
import {Injectable, Inject, Optional} from 'angular2/core';
|
||||
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
LocationStrategy,
|
||||
APP_BASE_HREF,
|
||||
normalizeQueryParams,
|
||||
joinWithSlash
|
||||
} from './location_strategy';
|
||||
import {PlatformLocation} from './platform_location';
|
||||
import {Injectable, Inject, Optional} from '@angular/core';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
|
||||
import {PlatformLocation, UrlChangeListener} from './platform_location';
|
||||
import {LocationStrategy, APP_BASE_HREF} from './location_strategy';
|
||||
import {Location} from './location';
|
||||
|
||||
/**
|
||||
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||
@ -30,14 +26,17 @@ import {PlatformLocation} from './platform_location';
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component, provide} from 'angular2/angular2';
|
||||
* import {Component, provide} from '@angular/core';
|
||||
* import {bootstrap} from '@angular/platform-browser/browser';
|
||||
* import {
|
||||
* Location,
|
||||
* APP_BASE_HREF
|
||||
* } from '@angular/common';
|
||||
* import {
|
||||
* ROUTER_DIRECTIVES,
|
||||
* ROUTER_PROVIDERS,
|
||||
* RouteConfig,
|
||||
* Location
|
||||
* } from 'angular2/router';
|
||||
* RouteConfig
|
||||
* } from '@angular/router';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
@ -75,26 +74,29 @@ export class PathLocationStrategy extends LocationStrategy {
|
||||
this._baseHref = href;
|
||||
}
|
||||
|
||||
onPopState(fn: EventListener): void {
|
||||
onPopState(fn: UrlChangeListener): void {
|
||||
this._platformLocation.onPopState(fn);
|
||||
this._platformLocation.onHashChange(fn);
|
||||
}
|
||||
|
||||
getBaseHref(): string { return this._baseHref; }
|
||||
|
||||
prepareExternalUrl(internal: string): string { return joinWithSlash(this._baseHref, internal); }
|
||||
prepareExternalUrl(internal: string): string {
|
||||
return Location.joinWithSlash(this._baseHref, internal);
|
||||
}
|
||||
|
||||
path(): string {
|
||||
return this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search);
|
||||
return this._platformLocation.pathname +
|
||||
Location.normalizeQueryParams(this._platformLocation.search);
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||
var externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
||||
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
this._platformLocation.pushState(state, title, externalUrl);
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, url: string, queryParams: string) {
|
||||
var externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
||||
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
this._platformLocation.replaceState(state, title, externalUrl);
|
||||
}
|
||||
|
48
modules/@angular/common/src/location/platform_location.ts
Normal file
48
modules/@angular/common/src/location/platform_location.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* This class should not be used directly by an application developer. Instead, use
|
||||
* {@link Location}.
|
||||
*
|
||||
* `PlatformLocation` encapsulates all calls to DOM apis, which allows the Router to be platform
|
||||
* agnostic.
|
||||
* This means that we can have different implementation of `PlatformLocation` for the different
|
||||
* platforms
|
||||
* that angular supports. For example, the default `PlatformLocation` is {@link
|
||||
* BrowserPlatformLocation},
|
||||
* however when you run your app in a WebWorker you use {@link WebWorkerPlatformLocation}.
|
||||
*
|
||||
* The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy}
|
||||
* when
|
||||
* they need to interact with the DOM apis like pushState, popState, etc...
|
||||
*
|
||||
* {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly
|
||||
* by
|
||||
* the {@link Router} in order to navigate between routes. Since all interactions between {@link
|
||||
* Router} /
|
||||
* {@link Location} / {@link LocationStrategy} and DOM apis flow through the `PlatformLocation`
|
||||
* class
|
||||
* they are all platform independent.
|
||||
*/
|
||||
export abstract class PlatformLocation {
|
||||
abstract getBaseHrefFromDOM(): string;
|
||||
abstract onPopState(fn: UrlChangeListener): void;
|
||||
abstract onHashChange(fn: UrlChangeListener): void;
|
||||
|
||||
/* abstract */ get pathname(): string { return null; }
|
||||
/* abstract */ get search(): string { return null; }
|
||||
/* abstract */ get hash(): string { return null; }
|
||||
|
||||
abstract replaceState(state: any, title: string, url: string): void;
|
||||
|
||||
abstract pushState(state: any, title: string, url: string): void;
|
||||
|
||||
abstract forward(): void;
|
||||
|
||||
abstract back(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A serializable version of the event from onPopState or onHashChange
|
||||
*/
|
||||
export interface UrlChangeEvent { type: string; }
|
||||
|
||||
export interface UrlChangeListener { (e: UrlChangeEvent): any; }
|
17
modules/@angular/common/src/pipes.ts
Normal file
17
modules/@angular/common/src/pipes.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This module provides a set of common Pipes.
|
||||
*/
|
||||
|
||||
export {AsyncPipe} from './pipes/async_pipe';
|
||||
export {DatePipe} from './pipes/date_pipe';
|
||||
export {JsonPipe} from './pipes/json_pipe';
|
||||
export {SlicePipe} from './pipes/slice_pipe';
|
||||
export {LowerCasePipe} from './pipes/lowercase_pipe';
|
||||
export {NumberPipe, DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
|
||||
export {UpperCasePipe} from './pipes/uppercase_pipe';
|
||||
export {ReplacePipe} from './pipes/replace_pipe';
|
||||
export {I18nPluralPipe} from './pipes/i18n_plural_pipe';
|
||||
export {I18nSelectPipe} from './pipes/i18n_select_pipe';
|
||||
export {COMMON_PIPES} from './pipes/common_pipes';
|
@ -1,13 +1,7 @@
|
||||
import {isBlank, isPresent, isPromise, CONST} from 'angular2/src/facade/lang';
|
||||
import {Promise, ObservableWrapper, Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||
import {
|
||||
Pipe,
|
||||
Injectable,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
PipeTransform,
|
||||
WrappedValue
|
||||
} from 'angular2/core';
|
||||
import {Pipe, Injectable, ChangeDetectorRef, OnDestroy, WrappedValue} from '@angular/core';
|
||||
|
||||
import {isBlank, isPresent, isPromise} from '../../src/facade/lang';
|
||||
import {ObservableWrapper, Observable, EventEmitter} from '../../src/facade/async';
|
||||
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
@ -22,7 +16,7 @@ class ObservableStrategy {
|
||||
}
|
||||
|
||||
class PromiseStrategy {
|
||||
createSubscription(async: any, updateLatestValue: any): any {
|
||||
createSubscription(async: Promise<any>, updateLatestValue: (v: any) => any): any {
|
||||
return async.then(updateLatestValue);
|
||||
}
|
||||
|
||||
@ -33,7 +27,7 @@ class PromiseStrategy {
|
||||
|
||||
var _promiseStrategy = new PromiseStrategy();
|
||||
var _observableStrategy = new ObservableStrategy();
|
||||
|
||||
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
|
||||
|
||||
/**
|
||||
* The `async` pipe subscribes to an Observable or Promise and returns the latest value it has
|
||||
@ -55,7 +49,7 @@ var _observableStrategy = new ObservableStrategy();
|
||||
*/
|
||||
@Pipe({name: 'async', pure: false})
|
||||
@Injectable()
|
||||
export class AsyncPipe implements PipeTransform, OnDestroy {
|
||||
export class AsyncPipe implements OnDestroy {
|
||||
/** @internal */
|
||||
_latestValue: Object = null;
|
||||
/** @internal */
|
||||
@ -76,12 +70,13 @@ export class AsyncPipe implements PipeTransform, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
transform(obj: Observable<any>| Promise<any>| EventEmitter<any>, args?: any[]): any {
|
||||
transform(obj: Observable<any>| Promise<any>| EventEmitter<any>): any {
|
||||
if (isBlank(this._obj)) {
|
||||
if (isPresent(obj)) {
|
||||
this._subscribe(obj);
|
||||
}
|
||||
return null;
|
||||
this._latestReturnedValue = this._latestValue;
|
||||
return this._latestValue;
|
||||
}
|
||||
|
||||
if (obj !== this._obj) {
|
||||
@ -101,8 +96,8 @@ export class AsyncPipe implements PipeTransform, OnDestroy {
|
||||
_subscribe(obj: Observable<any>| Promise<any>| EventEmitter<any>): void {
|
||||
this._obj = obj;
|
||||
this._strategy = this._selectStrategy(obj);
|
||||
this._subscription =
|
||||
this._strategy.createSubscription(obj, value => this._updateLatestValue(obj, value));
|
||||
this._subscription = this._strategy.createSubscription(
|
||||
obj, (value: Object) => this._updateLatestValue(obj, value));
|
||||
}
|
||||
|
||||
/** @internal */
|
@ -10,9 +10,18 @@ import {JsonPipe} from './json_pipe';
|
||||
import {SlicePipe} from './slice_pipe';
|
||||
import {DatePipe} from './date_pipe';
|
||||
import {DecimalPipe, PercentPipe, CurrencyPipe} from './number_pipe';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {ReplacePipe} from './replace_pipe';
|
||||
import {I18nPluralPipe} from './i18n_plural_pipe';
|
||||
import {I18nSelectPipe} from './i18n_select_pipe';
|
||||
|
||||
export const COMMON_PIPES = CONST_EXPR([
|
||||
/**
|
||||
* A collection of Angular core pipes that are likely to be used in each and every
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in pipes in the `pipes`
|
||||
* property of the `@Component` decorator.
|
||||
*/
|
||||
export const COMMON_PIPES = /*@ts2dart_const*/[
|
||||
AsyncPipe,
|
||||
UpperCasePipe,
|
||||
LowerCasePipe,
|
||||
@ -21,5 +30,8 @@ export const COMMON_PIPES = CONST_EXPR([
|
||||
DecimalPipe,
|
||||
PercentPipe,
|
||||
CurrencyPipe,
|
||||
DatePipe
|
||||
]);
|
||||
DatePipe,
|
||||
ReplacePipe,
|
||||
I18nPluralPipe,
|
||||
I18nSelectPipe
|
||||
];
|
@ -1,16 +1,12 @@
|
||||
import {PipeTransform, Pipe, Injectable} from '@angular/core';
|
||||
import {
|
||||
isDate,
|
||||
isNumber,
|
||||
isPresent,
|
||||
Date,
|
||||
DateWrapper,
|
||||
CONST,
|
||||
isBlank,
|
||||
FunctionWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {DateFormatter} from 'angular2/src/facade/intl';
|
||||
import {PipeTransform, WrappedValue, Pipe, Injectable} from 'angular2/core';
|
||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
} from '../../src/facade/lang';
|
||||
import {DateFormatter} from '../../src/facade/intl';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
@ -84,7 +80,6 @@ var defaultLocale: string = 'en-US';
|
||||
*
|
||||
* {@example core/pipes/ts/date_pipe/date_pipe_example.ts region='DatePipe'}
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'date', pure: true})
|
||||
@Injectable()
|
||||
export class DatePipe implements PipeTransform {
|
||||
@ -101,14 +96,13 @@ export class DatePipe implements PipeTransform {
|
||||
};
|
||||
|
||||
|
||||
transform(value: any, args: any[]): string {
|
||||
transform(value: any, pattern: string = 'mediumDate'): string {
|
||||
if (isBlank(value)) return null;
|
||||
|
||||
if (!this.supports(value)) {
|
||||
throw new InvalidPipeArgumentException(DatePipe, value);
|
||||
}
|
||||
|
||||
var pattern: string = isPresent(args) && args.length > 0 ? args[0] : 'mediumDate';
|
||||
if (isNumber(value)) {
|
||||
value = DateWrapper.fromMillis(value);
|
||||
}
|
54
modules/@angular/common/src/pipes/i18n_plural_pipe.ts
Normal file
54
modules/@angular/common/src/pipes/i18n_plural_pipe.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import {Injectable, PipeTransform, Pipe} from '@angular/core';
|
||||
import {isStringMap, StringWrapper, isPresent, RegExpWrapper} from '../../src/facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
var interpolationExp: RegExp = RegExpWrapper.create('#');
|
||||
|
||||
/**
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* <div>
|
||||
* {{ messages.length | i18nPlural: messageMapping }}
|
||||
* </div>
|
||||
*
|
||||
* class MyApp {
|
||||
* messages: any[];
|
||||
* messageMapping: any = {
|
||||
* '=0': 'No messages.',
|
||||
* '=1': 'One message.',
|
||||
* 'other': '# messages.'
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
@Pipe({name: 'i18nPlural', pure: true})
|
||||
@Injectable()
|
||||
export class I18nPluralPipe implements PipeTransform {
|
||||
transform(value: number, pluralMap: {[count: string]: string}): string {
|
||||
var key: string;
|
||||
var valueStr: string;
|
||||
|
||||
if (!isStringMap(pluralMap)) {
|
||||
throw new InvalidPipeArgumentException(I18nPluralPipe, pluralMap);
|
||||
}
|
||||
|
||||
key = value === 0 || value === 1 ? `=${value}` : 'other';
|
||||
valueStr = isPresent(value) ? value.toString() : '';
|
||||
|
||||
return StringWrapper.replaceAll(pluralMap[key], interpolationExp, valueStr);
|
||||
}
|
||||
}
|
45
modules/@angular/common/src/pipes/i18n_select_pipe.ts
Normal file
45
modules/@angular/common/src/pipes/i18n_select_pipe.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import {Injectable, PipeTransform, Pipe} from '@angular/core';
|
||||
import {isStringMap} from '../../src/facade/lang';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
*
|
||||
* Generic selector that displays the string that matches the current value.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | i18nSelect:mapping
|
||||
*
|
||||
* where `mapping` is an object that indicates the text that should be displayed
|
||||
* for different values of the provided `expression`.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* <div>
|
||||
* {{ gender | i18nSelect: inviteMap }}
|
||||
* </div>
|
||||
*
|
||||
* class MyApp {
|
||||
* gender: string = 'male';
|
||||
* inviteMap: any = {
|
||||
* 'male': 'Invite her.',
|
||||
* 'female': 'Invite him.',
|
||||
* 'other': 'Invite them.'
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Pipe({name: 'i18nSelect', pure: true})
|
||||
@Injectable()
|
||||
export class I18nSelectPipe implements PipeTransform {
|
||||
transform(value: string, mapping: {[key: string]: string}): string {
|
||||
if (!isStringMap(mapping)) {
|
||||
throw new InvalidPipeArgumentException(I18nSelectPipe, mapping);
|
||||
}
|
||||
|
||||
return StringMapWrapper.contains(mapping, value) ? mapping[value] : mapping['other'];
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import {CONST, Type, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {Type, stringify} from '../../src/facade/lang';
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
|
||||
export class InvalidPipeArgumentException extends BaseException {
|
||||
constructor(type: Type, value: Object) {
|
@ -1,5 +1,6 @@
|
||||
import {isBlank, isPresent, Json, CONST} from 'angular2/src/facade/lang';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from 'angular2/core';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from '@angular/core';
|
||||
import {Json} from '../../src/facade/lang';
|
||||
|
||||
|
||||
/**
|
||||
* Transforms any input value using `JSON.stringify`. Useful for debugging.
|
||||
@ -7,9 +8,9 @@ import {Injectable, PipeTransform, WrappedValue, Pipe} from 'angular2/core';
|
||||
* ### Example
|
||||
* {@example core/pipes/ts/json_pipe/json_pipe_example.ts region='JsonPipe'}
|
||||
*/
|
||||
@CONST()
|
||||
/* @ts2dart_const */
|
||||
@Pipe({name: 'json', pure: false})
|
||||
@Injectable()
|
||||
export class JsonPipe implements PipeTransform {
|
||||
transform(value: any, args: any[] = null): string { return Json.stringify(value); }
|
||||
transform(value: any): string { return Json.stringify(value); }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import {isString, CONST, isBlank} from 'angular2/src/facade/lang';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from 'angular2/core';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from '@angular/core';
|
||||
import {isString, isBlank} from '../../src/facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
@ -9,11 +9,11 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
*
|
||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
||||
*/
|
||||
@CONST()
|
||||
/* @ts2dart_const */
|
||||
@Pipe({name: 'lowercase'})
|
||||
@Injectable()
|
||||
export class LowerCasePipe implements PipeTransform {
|
||||
transform(value: string, args: any[] = null): string {
|
||||
transform(value: string): string {
|
||||
if (isBlank(value)) return value;
|
||||
if (!isString(value)) {
|
||||
throw new InvalidPipeArgumentException(LowerCasePipe, value);
|
@ -1,17 +1,15 @@
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from '@angular/core';
|
||||
|
||||
import {
|
||||
isNumber,
|
||||
isPresent,
|
||||
isBlank,
|
||||
StringWrapper,
|
||||
NumberWrapper,
|
||||
RegExpWrapper,
|
||||
CONST,
|
||||
FunctionWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {NumberFormatter, NumberFormatStyle} from 'angular2/src/facade/intl';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from 'angular2/core';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
} from '../../src/facade/lang';
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
import {NumberFormatter, NumberFormatStyle} from '../../src/facade/intl';
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
@ -21,7 +19,6 @@ var _re = RegExpWrapper.create('^(\\d+)?\\.((\\d+)(\\-(\\d+))?)?$');
|
||||
/**
|
||||
* Internal base class for numeric pipes.
|
||||
*/
|
||||
@CONST()
|
||||
@Injectable()
|
||||
export class NumberPipe {
|
||||
/** @internal */
|
||||
@ -83,12 +80,10 @@ export class NumberPipe {
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='NumberPipe'}
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'number'})
|
||||
@Injectable()
|
||||
export class DecimalPipe extends NumberPipe implements PipeTransform {
|
||||
transform(value: any, args: any[]): string {
|
||||
var digits: string = ListWrapper.first(args);
|
||||
transform(value: any, digits: string = null): string {
|
||||
return NumberPipe._format(value, NumberFormatStyle.Decimal, digits);
|
||||
}
|
||||
}
|
||||
@ -109,12 +104,10 @@ export class DecimalPipe extends NumberPipe implements PipeTransform {
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='PercentPipe'}
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'percent'})
|
||||
@Injectable()
|
||||
export class PercentPipe extends NumberPipe implements PipeTransform {
|
||||
transform(value: any, args: any[]): string {
|
||||
var digits: string = ListWrapper.first(args);
|
||||
transform(value: any, digits: string = null): string {
|
||||
return NumberPipe._format(value, NumberFormatStyle.Percent, digits);
|
||||
}
|
||||
}
|
||||
@ -139,14 +132,11 @@ export class PercentPipe extends NumberPipe implements PipeTransform {
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='CurrencyPipe'}
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'currency'})
|
||||
@Injectable()
|
||||
export class CurrencyPipe extends NumberPipe implements PipeTransform {
|
||||
transform(value: any, args: any[]): string {
|
||||
var currencyCode: string = isPresent(args) && args.length > 0 ? args[0] : 'USD';
|
||||
var symbolDisplay: boolean = isPresent(args) && args.length > 1 ? args[1] : false;
|
||||
var digits: string = isPresent(args) && args.length > 2 ? args[2] : null;
|
||||
transform(value: any, currencyCode: string = 'USD', symbolDisplay: boolean = false,
|
||||
digits: string = null): string {
|
||||
return NumberPipe._format(value, NumberFormatStyle.Currency, digits, currencyCode,
|
||||
symbolDisplay);
|
||||
}
|
85
modules/@angular/common/src/pipes/replace_pipe.ts
Normal file
85
modules/@angular/common/src/pipes/replace_pipe.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import {Injectable, PipeTransform, Pipe} from '@angular/core';
|
||||
|
||||
import {
|
||||
isBlank,
|
||||
isString,
|
||||
isNumber,
|
||||
isFunction,
|
||||
RegExpWrapper,
|
||||
StringWrapper
|
||||
} from '../../src/facade/lang';
|
||||
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
* Creates a new String with some or all of the matches of a pattern replaced by
|
||||
* a replacement.
|
||||
*
|
||||
* The pattern to be matched is specified by the 'pattern' parameter.
|
||||
*
|
||||
* The replacement to be set is specified by the 'replacement' parameter.
|
||||
*
|
||||
* An optional 'flags' parameter can be set.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | replace:pattern:replacement
|
||||
*
|
||||
* All behavior is based on the expected behavior of the JavaScript API
|
||||
* String.prototype.replace() function.
|
||||
*
|
||||
* Where the input expression is a [String] or [Number] (to be treated as a string),
|
||||
* the `pattern` is a [String] or [RegExp],
|
||||
* the 'replacement' is a [String] or [Function].
|
||||
*
|
||||
* --Note--: The 'pattern' parameter will be converted to a RegExp instance. Make sure to escape the
|
||||
* string properly if you are matching for regular expression special characters like parenthesis,
|
||||
* brackets etc.
|
||||
*/
|
||||
|
||||
@Pipe({name: 'replace'})
|
||||
@Injectable()
|
||||
export class ReplacePipe implements PipeTransform {
|
||||
transform(value: any, pattern: string | RegExp, replacement: Function | string): any {
|
||||
if (isBlank(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (!this._supportedInput(value)) {
|
||||
throw new InvalidPipeArgumentException(ReplacePipe, value);
|
||||
}
|
||||
|
||||
var input = value.toString();
|
||||
|
||||
if (!this._supportedPattern(pattern)) {
|
||||
throw new InvalidPipeArgumentException(ReplacePipe, pattern);
|
||||
}
|
||||
if (!this._supportedReplacement(replacement)) {
|
||||
throw new InvalidPipeArgumentException(ReplacePipe, replacement);
|
||||
}
|
||||
// template fails with literal RegExp e.g /pattern/igm
|
||||
// var rgx = pattern instanceof RegExp ? pattern : RegExpWrapper.create(pattern);
|
||||
|
||||
if (isFunction(replacement)) {
|
||||
var rgxPattern = isString(pattern) ? RegExpWrapper.create(<string>pattern) : <RegExp>pattern;
|
||||
|
||||
return StringWrapper.replaceAllMapped(input, rgxPattern, <Function>replacement);
|
||||
}
|
||||
if (pattern instanceof RegExp) {
|
||||
// use the replaceAll variant
|
||||
return StringWrapper.replaceAll(input, pattern, <string>replacement);
|
||||
}
|
||||
|
||||
return StringWrapper.replace(input, <string>pattern, <string>replacement);
|
||||
}
|
||||
|
||||
private _supportedInput(input: any): boolean { return isString(input) || isNumber(input); }
|
||||
|
||||
private _supportedPattern(pattern: any): boolean {
|
||||
return isString(pattern) || pattern instanceof RegExp;
|
||||
}
|
||||
|
||||
private _supportedReplacement(replacement: any): boolean {
|
||||
return isString(replacement) || isFunction(replacement);
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
import {isBlank, isString, isArray, StringWrapper, CONST} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from 'angular2/core';
|
||||
import {Injectable, PipeTransform, WrappedValue, Pipe} from '@angular/core';
|
||||
|
||||
import {isBlank, isString, isArray, StringWrapper} from '../../src/facade/lang';
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
@ -42,7 +44,7 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
*
|
||||
* ## List Example
|
||||
*
|
||||
* This `ng-for` example:
|
||||
* This `ngFor` example:
|
||||
*
|
||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_list'}
|
||||
*
|
||||
@ -59,16 +61,11 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
@Pipe({name: 'slice', pure: false})
|
||||
@Injectable()
|
||||
export class SlicePipe implements PipeTransform {
|
||||
transform(value: any, args: any[] = null): any {
|
||||
if (isBlank(args) || args.length == 0) {
|
||||
throw new BaseException('Slice pipe requires one argument');
|
||||
}
|
||||
transform(value: any, start: number, end: number = null): any {
|
||||
if (!this.supports(value)) {
|
||||
throw new InvalidPipeArgumentException(SlicePipe, value);
|
||||
}
|
||||
if (isBlank(value)) return value;
|
||||
var start: number = args[0];
|
||||
var end: number = args.length > 1 ? args[1] : null;
|
||||
if (isString(value)) {
|
||||
return StringWrapper.slice(value, start, end);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import {isString, CONST, isBlank} from 'angular2/src/facade/lang';
|
||||
import {PipeTransform, WrappedValue, Injectable, Pipe} from 'angular2/core';
|
||||
import {PipeTransform, WrappedValue, Injectable, Pipe} from '@angular/core';
|
||||
import {isString, isBlank} from '../../src/facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
@ -9,11 +9,10 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
*
|
||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'uppercase'})
|
||||
@Injectable()
|
||||
export class UpperCasePipe implements PipeTransform {
|
||||
transform(value: string, args: any[] = null): string {
|
||||
transform(value: string): string {
|
||||
if (isBlank(value)) return value;
|
||||
if (!isString(value)) {
|
||||
throw new InvalidPipeArgumentException(UpperCasePipe, value);
|
@ -1,60 +1,51 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
||||
import {Component, View, provide} from 'angular2/core';
|
||||
import {NgFor} from 'angular2/common';
|
||||
import {NgClass} from 'angular2/src/common/directives/ng_class';
|
||||
import {APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/linker/view_pool';
|
||||
} 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';
|
||||
|
||||
function detectChangesAndCheck(fixture: ComponentFixture, classes: string, elIndex: number = 0) {
|
||||
function detectChangesAndCheck(fixture: ComponentFixture<any>, classes: string) {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.componentViewChildren[elIndex].nativeElement.className)
|
||||
.toEqual(classes);
|
||||
expect(fixture.debugElement.children[0].nativeElement.className).toEqual(classes);
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS class list', () => {
|
||||
|
||||
describe('viewpool support', () => {
|
||||
beforeEachProviders(() => { return [provide(APP_VIEW_POOL_CAPACITY, {useValue: 100})]; });
|
||||
it('should clean up when the directive is destroyed',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div *ngFor="let item of items" [ngClass]="item"></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [['0']];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = [['1']];
|
||||
|
||||
it('should clean up when the directive is destroyed',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div *ng-for="var item of items" [ng-class]="item"></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [['0']];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = [['1']];
|
||||
detectChangesAndCheck(fixture, '1');
|
||||
|
||||
detectChangesAndCheck(fixture, '1', 1);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
describe('expressions evaluating to objects', () => {
|
||||
|
||||
it('should add classes specified in an object literal',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="{foo: true, bar: false}"></div>';
|
||||
var template = '<div [ngClass]="{foo: true, bar: false}"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -67,7 +58,7 @@ export function main() {
|
||||
|
||||
it('should add classes specified in an object literal without change in class names',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-class]="{'foo-bar': true, 'fooBar': true}"></div>`;
|
||||
var template = `<div [ngClass]="{'foo-bar': true, 'fooBar': true}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -79,7 +70,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes in object literal values',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="{foo: condition, bar: !condition}"></div>';
|
||||
var template = '<div [ngClass]="{foo: condition, bar: !condition}"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -95,7 +86,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression object',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="objExpr"></div>';
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -117,7 +108,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on reference changes to the expression object',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="objExpr"></div>';
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -136,7 +127,7 @@ export function main() {
|
||||
|
||||
it('should remove active classes when expression evaluates to null',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="objExpr"></div>';
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -156,7 +147,7 @@ export function main() {
|
||||
|
||||
it('should allow multiple classes per expression',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="objExpr"></div>';
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -181,7 +172,7 @@ export function main() {
|
||||
|
||||
it('should split by one or more spaces between classes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="objExpr"></div>';
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -200,7 +191,7 @@ export function main() {
|
||||
|
||||
it('should add classes specified in a list literal',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-class]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`;
|
||||
var template = `<div [ngClass]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -212,7 +203,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="arrExpr"></div>';
|
||||
var template = '<div [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -235,7 +226,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes when a reference changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="arrExpr"></div>';
|
||||
var template = '<div [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -251,7 +242,7 @@ export function main() {
|
||||
|
||||
it('should take initial classes into account when a reference changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div class="foo" [ng-class]="arrExpr"></div>';
|
||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -267,7 +258,7 @@ export function main() {
|
||||
|
||||
it('should ignore empty or blank class names',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div class="foo" [ng-class]="arrExpr"></div>';
|
||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -282,7 +273,7 @@ export function main() {
|
||||
|
||||
it('should trim blanks from class names',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div class="foo" [ng-class]="arrExpr"></div>';
|
||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -298,7 +289,7 @@ export function main() {
|
||||
|
||||
it('should allow multiple classes per item in arrays',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="arrExpr"></div>';
|
||||
var template = '<div [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -320,7 +311,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes if the set instance changed',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="setExpr"></div>';
|
||||
var template = '<div [ngClass]="setExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -343,7 +334,7 @@ export function main() {
|
||||
|
||||
it('should add classes specified in a string literal',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-class]="'foo bar foo-bar fooBar'"></div>`;
|
||||
var template = `<div [ngClass]="'foo bar foo-bar fooBar'"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -355,7 +346,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="strExpr"></div>';
|
||||
var template = '<div [ngClass]="strExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -375,7 +366,7 @@ export function main() {
|
||||
|
||||
it('should remove active classes when switching from string to null',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-class]="strExpr"></div>`;
|
||||
var template = `<div [ngClass]="strExpr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -391,7 +382,7 @@ export function main() {
|
||||
|
||||
it('should take initial classes into account when switching from string to null',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div class="foo" [ng-class]="strExpr"></div>`;
|
||||
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -407,7 +398,7 @@ export function main() {
|
||||
|
||||
it('should ignore empty and blank strings',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div class="foo" [ng-class]="strExpr"></div>`;
|
||||
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -425,7 +416,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the class attribute',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div [ng-class]="objExpr" class="init foo"></div>';
|
||||
var template = '<div [ngClass]="objExpr" class="init foo"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -445,7 +436,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the interpolated class attribute',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-class]="objExpr" class="{{'init foo'}}"></div>`;
|
||||
var template = `<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -465,7 +456,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the class attribute and binding to it',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-class]="objExpr" class="init" [class]="'foo'"></div>`;
|
||||
var template = `<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -486,7 +477,7 @@ export function main() {
|
||||
it('should co-operate with the class attribute and class.name binding',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div class="init foo" [ng-class]="objExpr" [class.baz]="condition"></div>';
|
||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -508,7 +499,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with initial class and class attribute binding when binding changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div class="init" [ng-class]="objExpr" [class]="strExpr"></div>';
|
||||
var template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -532,8 +523,7 @@ export function main() {
|
||||
})
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp'})
|
||||
@View({directives: [NgClass, NgFor]})
|
||||
@Component({selector: 'test-cmp', directives: [NgClass, NgFor], template: ''})
|
||||
class TestComponent {
|
||||
condition: boolean = true;
|
||||
items: any[];
|
@ -1,27 +1,28 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Component, View, TemplateRef, ContentChild} from 'angular2/core';
|
||||
import {NgFor} from 'angular2/src/common/directives/ng_for';
|
||||
} 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';
|
||||
|
||||
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';
|
||||
|
||||
export function main() {
|
||||
describe('ng-for', () => {
|
||||
describe('ngFor', () => {
|
||||
var TEMPLATE =
|
||||
'<div><copy-me template="ng-for #item of items">{{item.toString()}};</copy-me></div>';
|
||||
'<div><copy-me template="ngFor let item of items">{{item.toString()}};</copy-me></div>';
|
||||
|
||||
it('should reflect initial elements',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
@ -98,7 +99,7 @@ export function main() {
|
||||
|
||||
it('should iterate over an array of objects',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<ul><li template="ng-for #item of items">{{item["name"]}};</li></ul>';
|
||||
var template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -128,7 +129,7 @@ export function main() {
|
||||
|
||||
it('should gracefully handle nulls',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<ul><li template="ng-for #item of null">{{item}};</li></ul>';
|
||||
var template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
@ -157,6 +158,24 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
if (!IS_DART) {
|
||||
it('should throw on non-iterable ref and suggest using an array',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
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 ref changing to string',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
@ -187,8 +206,8 @@ export function main() {
|
||||
it('should repeat over nested arrays',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>' +
|
||||
'<div template="ng-for #item of items">' +
|
||||
'<div template="ng-for #subitem of item">' +
|
||||
'<div template="ngFor let item of items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div>|' +
|
||||
'</div>' +
|
||||
@ -213,8 +232,8 @@ export function main() {
|
||||
|
||||
it('should repeat over nested arrays with no intermediate element',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div><template ng-for #item [ng-for-of]="items">' +
|
||||
'<div template="ng-for #subitem of item">' +
|
||||
var template = '<div><template ngFor let-item [ngForOf]="items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div></template></div>';
|
||||
|
||||
@ -232,10 +251,37 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should repeat over nested ngIf that are the last node in the ngFor temlate',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var 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];
|
||||
fixture.debugElement.componentInstance.items = items;
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|');
|
||||
|
||||
items.push(1);
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|1|');
|
||||
|
||||
items.push(1);
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|1|2|even|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display indices correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div><copy-me template="ng-for: var item of items; var i=index">{{i.toString()}}</copy-me></div>';
|
||||
'<div><copy-me template="ngFor: let item of items; let i=index">{{i.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -251,10 +297,29 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display first item correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalsefalse');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [2, 1];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display last item correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div><copy-me template="ng-for: var item of items; var isLast=last">{{isLast.toString()}}</copy-me></div>';
|
||||
'<div><copy-me template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -273,7 +338,7 @@ export function main() {
|
||||
it('should display even items correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div><copy-me template="ng-for: var item of items; var isEven=even">{{isEven.toString()}}</copy-me></div>';
|
||||
'<div><copy-me template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -292,7 +357,7 @@ export function main() {
|
||||
it('should display odd items correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div><copy-me template="ng-for: var item of items; var isOdd=odd">{{isOdd.toString()}}</copy-me></div>';
|
||||
'<div><copy-me template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -312,13 +377,13 @@ export function main() {
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideTemplate(
|
||||
TestComponent,
|
||||
'<ul><template ng-for [ng-for-of]="items" [ng-for-template]="contentTpl"></template></ul>')
|
||||
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>')
|
||||
.overrideTemplate(
|
||||
ComponentUsingTestComponent,
|
||||
'<test-cmp><li template="#item #i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.componentViewChildren[0];
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
@ -329,12 +394,12 @@ export function main() {
|
||||
|
||||
it('should use a default template if a custom one is null',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideTemplate(TestComponent, `<ul><template ng-for #item [ng-for-of]="items"
|
||||
[ng-for-template]="contentTpl" #i="index">{{i}}: {{item}};</template></ul>`)
|
||||
tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`)
|
||||
.overrideTemplate(ComponentUsingTestComponent, '<test-cmp></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.componentViewChildren[0];
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
@ -345,14 +410,14 @@ export function main() {
|
||||
|
||||
it('should use a custom template when both default and a custom one are present',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideTemplate(TestComponent, `<ul><template ng-for #item [ng-for-of]="items"
|
||||
[ng-for-template]="contentTpl" #i="index">{{i}}=> {{item}};</template></ul>`)
|
||||
tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`)
|
||||
.overrideTemplate(
|
||||
ComponentUsingTestComponent,
|
||||
'<test-cmp><li template="#item #i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.componentViewChildren[0];
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
@ -360,6 +425,83 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('track by', function() {
|
||||
it('should not replace tracked items',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var 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 firstP = buildItemList();
|
||||
var finalP = buildItemList();
|
||||
expect(finalP.nativeElement).toBe(firstP.nativeElement);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should update implicit local variable on view',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'blue'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('blue');
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'red'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('red');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should move items around and keep them updated ',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items =
|
||||
[{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('blueyellow');
|
||||
fixture.debugElement.componentInstance.items =
|
||||
[{'id': 'b', 'color': 'orange'}, {'id': 'a', 'color': 'red'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('orangered');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle added and removed items properly when tracking by index',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = ['a', 'b', 'c', 'd'];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = ['e', 'f', 'g', 'h'];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = ['e', 'f', 'h'];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('efh');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -367,16 +509,16 @@ class Foo {
|
||||
toString() { return 'foo'; }
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp'})
|
||||
@View({directives: [NgFor]})
|
||||
@Component({selector: 'test-cmp', directives: [NgFor, NgIf], template: ''})
|
||||
class TestComponent {
|
||||
@ContentChild(TemplateRef) contentTpl: TemplateRef;
|
||||
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
||||
items: any;
|
||||
constructor() { this.items = [1, 2]; }
|
||||
trackById(index: number, item: any): string { return item['id']; }
|
||||
trackByIndex(index: number, item: any): number { return index; }
|
||||
}
|
||||
|
||||
@Component({selector: 'outer-cmp'})
|
||||
@View({directives: [TestComponent]})
|
||||
@Component({selector: 'outer-cmp', directives: [TestComponent], template: ''})
|
||||
class ComponentUsingTestComponent {
|
||||
items: any;
|
||||
constructor() { this.items = [1, 2]; }
|
@ -1,35 +1,35 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {Component, View} from 'angular2/core';
|
||||
import {NgIf} from 'angular2/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
import {IS_DART} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('ng-if directive', () => {
|
||||
describe('ngIf directive', () => {
|
||||
it('should work in a template attribute',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html = '<div><copy-me template="ng-if booleanCondition">hello</copy-me></div>';
|
||||
var html = '<div><copy-me template="ngIf booleanCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
async.done();
|
||||
@ -39,13 +39,14 @@ export function main() {
|
||||
it('should work in a template element',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html =
|
||||
'<div><template [ng-if]="booleanCondition"><copy-me>hello2</copy-me></template></div>';
|
||||
'<div><template [ngIf]="booleanCondition"><copy-me>hello2</copy-me></template></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello2');
|
||||
async.done();
|
||||
@ -54,26 +55,29 @@ export function main() {
|
||||
|
||||
it('should toggle node when condition changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html = '<div><copy-me template="ng-if booleanCondition">hello</copy-me></div>';
|
||||
var html = '<div><copy-me template="ngIf booleanCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
@ -84,38 +88,43 @@ export function main() {
|
||||
it('should handle nested if correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html =
|
||||
'<div><template [ng-if]="booleanCondition"><copy-me *ng-if="nestedBooleanCondition">hello</copy-me></template></div>';
|
||||
'<div><template [ngIf]="booleanCondition"><copy-me *ngIf="nestedBooleanCondition">hello</copy-me></template></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.nestedBooleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.nestedBooleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
@ -127,30 +136,33 @@ export function main() {
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html =
|
||||
'<div>' +
|
||||
'<copy-me template="ng-if numberCondition + 1 >= 2">helloNumber</copy-me>' +
|
||||
'<copy-me template="ng-if stringCondition == \'foo\'">helloString</copy-me>' +
|
||||
'<copy-me template="ng-if functionCondition(stringCondition, numberCondition)">helloFunction</copy-me>' +
|
||||
'<copy-me template="ngIf numberCondition + 1 >= 2">helloNumber</copy-me>' +
|
||||
'<copy-me template="ngIf stringCondition == \'foo\'">helloString</copy-me>' +
|
||||
'<copy-me template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</copy-me>' +
|
||||
'</div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(3);
|
||||
expect(DOM.getText(fixture.debugElement.nativeElement))
|
||||
expect(getDOM().getText(fixture.debugElement.nativeElement))
|
||||
.toEqual('helloNumberhelloStringhelloFunction');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 0;
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('helloString');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 1;
|
||||
fixture.debugElement.componentInstance.stringCondition = "bar";
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(
|
||||
getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('helloNumber');
|
||||
async.done();
|
||||
@ -161,19 +173,23 @@ export function main() {
|
||||
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) => {
|
||||
var html = '<div><copy-me template="ng-if numberCondition">hello</copy-me></div>';
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
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(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
@ -183,20 +199,20 @@ export function main() {
|
||||
|
||||
it('should not recreate the element if the condition goes from true to true (JS)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html = '<div><copy-me template="ng-if numberCondition">hello</copy-me></div>';
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
DOM.addClass(DOM.querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
"foo");
|
||||
getDOM().addClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'), "foo");
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(
|
||||
DOM.hasClass(DOM.querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
"foo"))
|
||||
expect(getDOM().hasClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
"foo"))
|
||||
.toBe(true);
|
||||
|
||||
async.done();
|
||||
@ -207,13 +223,15 @@ export function main() {
|
||||
if (IS_DART) {
|
||||
it('should not create the element if the condition is not a boolean (DART)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var html = '<div><copy-me template="ng-if numberCondition">hello</copy-me></div>';
|
||||
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(DOM.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me').length)
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
@ -224,8 +242,7 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp'})
|
||||
@View({directives: [NgIf]})
|
||||
@Component({selector: 'test-cmp', directives: [NgIf], template: ''})
|
||||
class TestComponent {
|
||||
booleanCondition: boolean;
|
||||
nestedBooleanCondition: boolean;
|
135
modules/@angular/common/test/directives/ng_plural_spec.ts
Normal file
135
modules/@angular/common/test/directives/ng_plural_spec.ts
Normal file
@ -0,0 +1,135 @@
|
||||
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, ComponentFixture} from '@angular/compiler/testing';
|
||||
|
||||
import {Component, Injectable, provide} from '@angular/core';
|
||||
import {NgPlural, NgPluralCase, NgLocalization} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
beforeEachProviders(() => [provide(NgLocalization, {useClass: TestLocalizationMap})]);
|
||||
|
||||
it('should display the template according to the exact value',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
||||
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 0;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 1;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display the template according to the category',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 2;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have a few messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 8;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have many messages.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should default to other when no matches are found',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 100;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('default message.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should prioritize value matches over category matches',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="=2">you have two messages.</template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 2;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have two messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 3;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have a few messages.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class TestLocalizationMap extends NgLocalization {
|
||||
getPluralCategory(value: number): string {
|
||||
if (value > 1 && value < 4) {
|
||||
return 'few';
|
||||
} else if (value >= 4 && value < 10) {
|
||||
return 'many';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgPlural, NgPluralCase], template: ''})
|
||||
class TestComponent {
|
||||
switchValue: number;
|
||||
|
||||
constructor() { this.switchValue = null; }
|
||||
}
|
@ -1,39 +1,38 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
|
||||
import {Component, View} from 'angular2/core';
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {NgStyle} from 'angular2/src/common/directives/ng_style';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {NgStyle} from '@angular/common/src/directives/ng_style';
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS styles', () => {
|
||||
|
||||
it('should add styles specified in an object literal',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-style]="{'max-width': '40px'}"></div>`;
|
||||
var template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
|
||||
async.done();
|
||||
@ -42,7 +41,7 @@ export function main() {
|
||||
|
||||
it('should add and change styles specified in an object expression',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-style]="expr"></div>`;
|
||||
var template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -51,15 +50,15 @@ export function main() {
|
||||
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
|
||||
expr = fixture.debugElement.componentInstance.expr;
|
||||
expr['max-width'] = '30%';
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('30%');
|
||||
|
||||
async.done();
|
||||
@ -68,21 +67,21 @@ export function main() {
|
||||
|
||||
it('should remove styles when deleting a key in an object expression',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [ng-style]="expr"></div>`;
|
||||
var template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
|
||||
StringMapWrapper.delete(fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
|
||||
async.done();
|
||||
@ -91,27 +90,27 @@ export function main() {
|
||||
|
||||
it('should co-operate with the style attribute',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div style="font-size: 12px" [ng-style]="expr"></div>`;
|
||||
var template = `<div style="font-size: 12px" [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'font-size'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
StringMapWrapper.delete(fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'font-size'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
async.done();
|
||||
@ -120,28 +119,28 @@ export function main() {
|
||||
|
||||
it('should co-operate with the style.[styleName]="expr" special-case in the compiler',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<div [style.font-size.px]="12" [ng-style]="expr"></div>`;
|
||||
var template = `<div [style.font-size.px]="12" [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'font-size'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
StringMapWrapper.delete(fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'font-size'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getStyle(fixture.debugElement.componentViewChildren[0].nativeElement,
|
||||
'max-width'))
|
||||
expect(
|
||||
getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
|
||||
async.done();
|
||||
@ -150,8 +149,7 @@ export function main() {
|
||||
})
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp'})
|
||||
@View({directives: [NgStyle]})
|
||||
@Component({selector: 'test-cmp', directives: [NgStyle], template: ''})
|
||||
class TestComponent {
|
||||
expr;
|
||||
}
|
@ -1,20 +1,18 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
} 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';
|
||||
|
||||
import {Component, View} from 'angular2/core';
|
||||
|
||||
import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from 'angular2/src/common/directives/ng_switch';
|
||||
import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
@ -22,9 +20,9 @@ export function main() {
|
||||
it('should switch amongst when values',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ng-switch]="switchValue">' +
|
||||
'<template ng-switch-when="a"><li>when a</li></template>' +
|
||||
'<template ng-switch-when="b"><li>when b</li></template>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchWhen="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchWhen="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -48,9 +46,9 @@ export function main() {
|
||||
it('should switch amongst when values with fallback to default',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ng-switch]="switchValue">' +
|
||||
'<li template="ng-switch-when \'a\'">when a</li>' +
|
||||
'<li template="ng-switch-default">when default</li>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<li template="ngSwitchWhen \'a\'">when a</li>' +
|
||||
'<li template="ngSwitchDefault">when default</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -74,13 +72,13 @@ export function main() {
|
||||
it('should support multiple whens with the same value',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ng-switch]="switchValue">' +
|
||||
'<template ng-switch-when="a"><li>when a1;</li></template>' +
|
||||
'<template ng-switch-when="b"><li>when b1;</li></template>' +
|
||||
'<template ng-switch-when="a"><li>when a2;</li></template>' +
|
||||
'<template ng-switch-when="b"><li>when b2;</li></template>' +
|
||||
'<template ng-switch-default><li>when default1;</li></template>' +
|
||||
'<template ng-switch-default><li>when default2;</li></template>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchWhen="a"><li>when a1;</li></template>' +
|
||||
'<template ngSwitchWhen="b"><li>when b1;</li></template>' +
|
||||
'<template ngSwitchWhen="a"><li>when a2;</li></template>' +
|
||||
'<template ngSwitchWhen="b"><li>when b2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -107,10 +105,10 @@ export function main() {
|
||||
it('should switch amongst when values',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ng-switch]="switchValue">' +
|
||||
'<template [ng-switch-when]="when1"><li>when 1;</li></template>' +
|
||||
'<template [ng-switch-when]="when2"><li>when 2;</li></template>' +
|
||||
'<template ng-switch-default><li>when default;</li></template>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template [ngSwitchWhen]="when1"><li>when 1;</li></template>' +
|
||||
'<template [ngSwitchWhen]="when2"><li>when 2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -145,8 +143,8 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp'})
|
||||
@View({directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault]})
|
||||
@Component(
|
||||
{selector: 'test-cmp', directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault], template: ''})
|
||||
class TestComponent {
|
||||
switchValue: any;
|
||||
when1: any;
|
@ -0,0 +1,110 @@
|
||||
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';
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('insert', () => {
|
||||
it('should do nothing if templateRef is null',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<template [ngTemplateOutlet]="null"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should insert content specified by TemplateRef',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should clear content if TemplateRef becomes null',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
fixture.componentInstance.currentTplRef = null;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should swap content if TemplateRef changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = `<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.last;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
|
||||
class CaptureTplRefs {
|
||||
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgTemplateOutlet, CaptureTplRefs], template: ''})
|
||||
class TestComponent {
|
||||
currentTplRef: TemplateRef<any>;
|
||||
}
|
@ -1,25 +1,24 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {Component, Directive, View} from 'angular2/core';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {Component, Directive} from '@angular/core';
|
||||
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('non-bindable', () => {
|
||||
it('should not interpolate children',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div>{{text}}<span ng-non-bindable>{{text}}</span></div>';
|
||||
var template = '<div>{{text}}<span ngNonBindable>{{text}}</span></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
@ -31,29 +30,29 @@ export function main() {
|
||||
|
||||
it('should ignore directives on child nodes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div ng-non-bindable><span id=child test-dec>{{text}}</span></div>';
|
||||
var template = '<div ngNonBindable><span id=child test-dec>{{text}}</span></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
// We must use DOM.querySelector instead of fixture.query here
|
||||
// We must use getDOM().querySelector instead of fixture.query here
|
||||
// since the elements inside are not compiled.
|
||||
var span = DOM.querySelector(fixture.debugElement.nativeElement, '#child');
|
||||
expect(DOM.hasClass(span, 'compiled')).toBeFalsy();
|
||||
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
|
||||
expect(getDOM().hasClass(span, 'compiled')).toBeFalsy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should trigger directives on the same node',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div><span id=child ng-non-bindable test-dec>{{text}}</span></div>';
|
||||
var template = '<div><span id=child ngNonBindable test-dec>{{text}}</span></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
var span = DOM.querySelector(fixture.debugElement.nativeElement, '#child');
|
||||
expect(DOM.hasClass(span, 'compiled')).toBeTruthy();
|
||||
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
|
||||
expect(getDOM().hasClass(span, 'compiled')).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@ -62,11 +61,10 @@ export function main() {
|
||||
|
||||
@Directive({selector: '[test-dec]'})
|
||||
class TestDirective {
|
||||
constructor(el: ElementRef) { DOM.addClass(el.nativeElement, 'compiled'); }
|
||||
constructor(el: ElementRef) { getDOM().addClass(el.nativeElement, 'compiled'); }
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp'})
|
||||
@View({directives: [TestDirective]})
|
||||
@Component({selector: 'test-cmp', directives: [TestDirective], template: ''})
|
||||
class TestComponent {
|
||||
text: string;
|
||||
constructor() { this.text = 'foo'; }
|
@ -6,9 +6,7 @@ import 'package:angular2/core.dart' show ChangeDetectorRef;
|
||||
import 'package:angular2/common.dart' show ObservableListDiffFactory;
|
||||
|
||||
@proxy
|
||||
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {}
|
||||
|
||||
main() {
|
||||
describe('ObservableListDiff', () {
|
@ -1,24 +1,24 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
fakeAsync,
|
||||
flushMicrotasks,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
el,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
tick
|
||||
} from 'angular2/testing_internal';
|
||||
inject
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks,
|
||||
Log,
|
||||
tick,
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import {SpyNgControl, SpyValueAccessor} from '../spies';
|
||||
|
||||
import {QueryList} from 'angular2/core';
|
||||
|
||||
import {
|
||||
ControlGroup,
|
||||
Control,
|
||||
@ -35,13 +35,13 @@ import {
|
||||
CheckboxControlValueAccessor,
|
||||
SelectControlValueAccessor,
|
||||
Validator
|
||||
} from 'angular2/common';
|
||||
} from '@angular/common';
|
||||
|
||||
|
||||
import {selectValueAccessor, composeValidators} from 'angular2/src/common/forms/directives/shared';
|
||||
import {TimerWrapper} from 'angular2/src/facade/async';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {SimpleChange} from 'angular2/src/core/change_detection';
|
||||
import {selectValueAccessor, composeValidators} from '@angular/common/src/forms/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;
|
||||
@ -71,7 +71,7 @@ function asyncValidator(expected, timeout = 0) {
|
||||
|
||||
export function main() {
|
||||
describe("Form Directives", () => {
|
||||
var defaultAccessor;
|
||||
var defaultAccessor: DefaultValueAccessor;
|
||||
|
||||
beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null, null); });
|
||||
|
||||
@ -94,21 +94,21 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should return select accessor when provided", () => {
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null);
|
||||
expect(selectValueAccessor(dir, [defaultAccessor, selectAccessor]))
|
||||
.toEqual(selectAccessor);
|
||||
});
|
||||
|
||||
it("should throw when more than one build-in accessor is provided", () => {
|
||||
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null);
|
||||
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
|
||||
});
|
||||
|
||||
it("should return custom accessor when provided", () => {
|
||||
var customAccessor = new SpyValueAccessor();
|
||||
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||
expect(selectValueAccessor(dir, [defaultAccessor, customAccessor, checkboxAccessor]))
|
||||
expect(selectValueAccessor(dir, <any>[defaultAccessor, customAccessor, checkboxAccessor]))
|
||||
.toEqual(customAccessor);
|
||||
});
|
||||
|
@ -6,11 +6,10 @@ import {
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
el
|
||||
} from 'angular2/testing_internal';
|
||||
import {Control, FormBuilder} from 'angular2/common';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
afterEach
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {Control, FormBuilder} from '@angular/common';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
|
||||
export function main() {
|
||||
function syncValidator(_) { return null; }
|
1473
modules/@angular/common/test/forms/integration_spec.ts
Normal file
1473
modules/@angular/common/test/forms/integration_spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,19 +7,17 @@ import {
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
el,
|
||||
AsyncTestCompleter,
|
||||
fakeAsync,
|
||||
tick,
|
||||
inject
|
||||
} from 'angular2/testing_internal';
|
||||
import {ControlGroup, Control, ControlArray, Validators} from 'angular2/common';
|
||||
import {IS_DART, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {TimerWrapper, ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
|
||||
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';
|
||||
import {IS_DART, isPresent} from '../../src/facade/lang';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {TimerWrapper, ObservableWrapper, EventEmitter} from '../../src/facade/async';
|
||||
|
||||
export function main() {
|
||||
function asyncValidator(expected, timeouts = CONST_EXPR({})) {
|
||||
function asyncValidator(expected, timeouts = /*@ts2dart_const*/ {}) {
|
||||
return (c) => {
|
||||
var completer = PromiseWrapper.completer();
|
||||
var t = isPresent(timeouts[c.value]) ? timeouts[c.value] : 0;
|
@ -6,15 +6,12 @@ import {
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
fakeAsync,
|
||||
tick,
|
||||
el
|
||||
} from 'angular2/testing_internal';
|
||||
import {ControlGroup, Control, Validators, AbstractControl, ControlArray} from 'angular2/common';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {EventEmitter, ObservableWrapper, TimerWrapper} from 'angular2/src/facade/async';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
afterEach
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {fakeAsync, flushMicrotasks, Log, tick} from '@angular/core/testing';
|
||||
import {ControlGroup, Control, Validators, AbstractControl, ControlArray} from '@angular/common';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {EventEmitter, ObservableWrapper, TimerWrapper} from '../../src/facade/async';
|
||||
|
||||
export function main() {
|
||||
function validator(key: string, error: any) {
|
||||
@ -35,6 +32,9 @@ export function main() {
|
||||
|
||||
it("should not error on a non-empty string",
|
||||
() => { expect(Validators.required(new Control("not empty"))).toEqual(null); });
|
||||
|
||||
it("should accept zero as valid",
|
||||
() => { expect(Validators.required(new Control(0))).toEqual(null); });
|
||||
});
|
||||
|
||||
describe("minLength", () => {
|
||||
@ -63,12 +63,28 @@ export function main() {
|
||||
it("should not error on valid strings",
|
||||
() => { expect(Validators.maxLength(2)(new Control("aa"))).toEqual(null); });
|
||||
|
||||
it("should error on short strings", () => {
|
||||
it("should error on long strings", () => {
|
||||
expect(Validators.maxLength(2)(new Control("aaa")))
|
||||
.toEqual({"maxlength": {"requiredLength": 2, "actualLength": 3}});
|
||||
});
|
||||
});
|
||||
|
||||
describe("pattern", () => {
|
||||
it("should not error on an empty string",
|
||||
() => { expect(Validators.pattern("[a-zA-Z ]*")(new Control(""))).toEqual(null); });
|
||||
|
||||
it("should not error on null",
|
||||
() => { expect(Validators.pattern("[a-zA-Z ]*")(new Control(null))).toEqual(null); });
|
||||
|
||||
it("should not error on valid strings",
|
||||
() => { expect(Validators.pattern("[a-zA-Z ]*")(new Control("aaAA"))).toEqual(null); });
|
||||
|
||||
it("should error on failure to match string", () => {
|
||||
expect(Validators.pattern("[a-zA-Z ]*")(new Control("aaa0")))
|
||||
.toEqual({"pattern": {"requiredPattern": "^[a-zA-Z ]*$", "actualValue": "aaa0"}});
|
||||
});
|
||||
});
|
||||
|
||||
describe("compose", () => {
|
||||
it("should return null when given null",
|
||||
() => { expect(Validators.compose(null)).toBe(null); });
|
||||
@ -121,7 +137,7 @@ export function main() {
|
||||
]);
|
||||
|
||||
var value = null;
|
||||
c(new Control("invalid")).then(v => value = v);
|
||||
(<Promise<any>>c(new Control("invalid"))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
@ -132,7 +148,7 @@ export function main() {
|
||||
var c = Validators.composeAsync([asyncValidator("expected", {"one": true})]);
|
||||
|
||||
var value = null;
|
||||
c(new Control("expected")).then(v => value = v);
|
||||
(<Promise<any>>c(new Control("expected"))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
@ -143,7 +159,7 @@ export function main() {
|
||||
var c = Validators.composeAsync([asyncValidator("expected", {"one": true}), null]);
|
||||
|
||||
var value = null;
|
||||
c(new Control("invalid")).then(v => value = v);
|
||||
(<Promise<any>>c(new Control("invalid"))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
@ -7,22 +7,22 @@ import {
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
browserDetection
|
||||
} from 'angular2/testing_internal';
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {SpyChangeDetectorRef} from '../spies';
|
||||
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {AsyncPipe} from 'angular2/common';
|
||||
import {WrappedValue} from 'angular2/core';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
import {WrappedValue} from '@angular/core';
|
||||
import {
|
||||
EventEmitter,
|
||||
ObservableWrapper,
|
||||
PromiseWrapper,
|
||||
TimerWrapper
|
||||
} from 'angular2/src/facade/async';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
} from '../../src/facade/async';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {PromiseCompleter} from '../../src/facade/promise';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
|
||||
export function main() {
|
||||
describe("AsyncPipe", () => {
|
||||
@ -92,7 +92,7 @@ export function main() {
|
||||
TimerWrapper.setTimeout(() => {
|
||||
expect(ref.spy('markForCheck')).toHaveBeenCalled();
|
||||
async.done();
|
||||
}, 0)
|
||||
}, 10)
|
||||
}));
|
||||
});
|
||||
|
||||
@ -116,16 +116,16 @@ export function main() {
|
||||
|
||||
describe("Promise", () => {
|
||||
var message = new Object();
|
||||
var pipe;
|
||||
var completer;
|
||||
var ref;
|
||||
var pipe: AsyncPipe;
|
||||
var completer: PromiseCompleter<any>;
|
||||
var ref: SpyChangeDetectorRef;
|
||||
// adds longer timers for passing tests in IE
|
||||
var timer = (!isBlank(DOM) && browserDetection.isIE) ? 50 : 0;
|
||||
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10;
|
||||
|
||||
beforeEach(() => {
|
||||
completer = PromiseWrapper.completer();
|
||||
ref = new SpyChangeDetectorRef();
|
||||
pipe = new AsyncPipe(ref);
|
||||
pipe = new AsyncPipe(<any>ref);
|
||||
});
|
||||
|
||||
describe("transform", () => {
|
||||
@ -173,11 +173,12 @@ export function main() {
|
||||
|
||||
it("should request a change detection check upon receiving a new value",
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var markForCheck = ref.spy('markForCheck');
|
||||
pipe.transform(completer.promise);
|
||||
completer.resolve(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
expect(ref.spy('markForCheck')).toHaveBeenCalled();
|
||||
expect(markForCheck).toHaveBeenCalled();
|
||||
async.done();
|
||||
}, timer)
|
||||
}));
|
||||
@ -206,14 +207,14 @@ export function main() {
|
||||
describe('null', () => {
|
||||
it('should return null when given null', () => {
|
||||
var pipe = new AsyncPipe(null);
|
||||
expect(pipe.transform(null, [])).toEqual(null);
|
||||
expect(pipe.transform(null)).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('other types', () => {
|
||||
it('should throw when given an invalid object', () => {
|
||||
var pipe = new AsyncPipe(null);
|
||||
expect(() => pipe.transform(<any>"some bogus object", [])).toThrowError();
|
||||
expect(() => pipe.transform(<any>"some bogus object")).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
82
modules/@angular/common/test/pipes/date_pipe_spec.ts
Normal file
82
modules/@angular/common/test/pipes/date_pipe_spec.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach
|
||||
} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {DateWrapper} from '../../src/facade/lang';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
|
||||
export function main() {
|
||||
describe("DatePipe", () => {
|
||||
var date;
|
||||
var pipe;
|
||||
|
||||
beforeEach(() => {
|
||||
date = DateWrapper.create(2015, 6, 15, 21, 43, 11);
|
||||
pipe = new DatePipe();
|
||||
});
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(DatePipe).pure).toEqual(true); });
|
||||
|
||||
describe("supports", () => {
|
||||
it("should support date", () => { expect(pipe.supports(date)).toBe(true); });
|
||||
it("should support int", () => { expect(pipe.supports(123456789)).toBe(true); });
|
||||
|
||||
it("should not support other objects", () => {
|
||||
expect(pipe.supports(new Object())).toBe(false);
|
||||
expect(pipe.supports(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(mlaval): enable tests when Intl API is no longer used, see
|
||||
// https://github.com/angular/angular/issues/3333
|
||||
if (browserDetection.supportsIntlApi) {
|
||||
describe("transform", () => {
|
||||
it('should format each component correctly', () => {
|
||||
expect(pipe.transform(date, 'y')).toEqual('2015');
|
||||
expect(pipe.transform(date, 'yy')).toEqual('15');
|
||||
expect(pipe.transform(date, 'M')).toEqual('6');
|
||||
expect(pipe.transform(date, 'MM')).toEqual('06');
|
||||
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
|
||||
expect(pipe.transform(date, 'MMMM')).toEqual('June');
|
||||
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, 'm')).toEqual('43');
|
||||
expect(pipe.transform(date, 's')).toEqual('11');
|
||||
});
|
||||
|
||||
it('should format common multi component patterns', () => {
|
||||
expect(pipe.transform(date, 'yMEd')).toEqual('Mon, 6/15/2015');
|
||||
expect(pipe.transform(date, 'MEd')).toEqual('Mon, 6/15');
|
||||
expect(pipe.transform(date, 'MMMd')).toEqual('Jun 15');
|
||||
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'jms')).toEqual('9:43:11 PM');
|
||||
expect(pipe.transform(date, 'ms')).toEqual('43:11');
|
||||
});
|
||||
|
||||
it('should format with pattern aliases', () => {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:43:11 PM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:43 PM');
|
||||
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:43:11 PM');
|
||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:43 PM');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user