mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
Compare commits
1067 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5f69e3f64 | ||
|
|
dd24c78373 | ||
|
|
36d37c0e38 | ||
|
|
24699ee8f0 | ||
|
|
aa6a0e3fc6 | ||
|
|
8761ddc0e3 | ||
|
|
058842ad04 | ||
|
|
431bad0183 | ||
|
|
5850e61c82 | ||
|
|
d2e4e49986 | ||
|
|
0da6cc9118 | ||
|
|
cc60ba1f35 | ||
|
|
64d58a5b52 | ||
|
|
3bf4390339 | ||
|
|
e7ac7aa43b | ||
|
|
37781ed145 | ||
|
|
19ba6510d0 | ||
|
|
bc5ceee275 | ||
|
|
106af49258 | ||
|
|
d5eedf38db | ||
|
|
c0c0b2b674 | ||
|
|
ce1f1f97f0 | ||
|
|
8205158e47 | ||
|
|
6609e3da76 | ||
|
|
ef210e5e11 | ||
|
|
fd61e222c3 | ||
|
|
074b0675a1 | ||
|
|
5ed721b9b5 | ||
|
|
c22ab5d2e2 | ||
|
|
339a1658cd | ||
|
|
29432ffe37 | ||
|
|
caed2dfe4f | ||
|
|
7f4edaff6e | ||
|
|
6dcfccb32c | ||
|
|
e2173f9101 | ||
|
|
7a78aed160 | ||
|
|
7aef2d54e0 | ||
|
|
ce37ae2868 | ||
|
|
95f0bf9b52 | ||
|
|
8a0be355a9 | ||
|
|
849a1489c7 | ||
|
|
f54f0f98a0 | ||
|
|
766b3d5c87 | ||
|
|
4f735b0605 | ||
|
|
319dd1a449 | ||
|
|
88a14b4e25 | ||
|
|
451bde1244 | ||
|
|
4f827f587b | ||
|
|
131410b61b | ||
|
|
ca6b7d0fa2 | ||
|
|
40dc806e03 | ||
|
|
5e9835b4f2 | ||
|
|
e0209169bf | ||
|
|
b07afa0465 | ||
|
|
c3b5e16d84 | ||
|
|
7f444a205e | ||
|
|
82213efff2 | ||
|
|
ec59be67bc | ||
|
|
79e519feda | ||
|
|
7cf5544a9f | ||
|
|
030a9b8d33 | ||
|
|
310f129c1d | ||
|
|
0fc64ad8a1 | ||
|
|
12d1f5700d | ||
|
|
60e80509a8 | ||
|
|
3c12d36e73 | ||
|
|
fd6bac7de5 | ||
|
|
6d525f06c0 | ||
|
|
d7e6f1b192 | ||
|
|
5b9ff6cf48 | ||
|
|
f09b6aa5b5 | ||
|
|
8b395ff325 | ||
|
|
6c9131ef10 | ||
|
|
99c5027bf2 | ||
|
|
90e60d2d54 | ||
|
|
928d000db7 | ||
|
|
42ec95ebae | ||
|
|
1028cfaa30 | ||
|
|
756c52d6c1 | ||
|
|
c3e1a41d6f | ||
|
|
8dd4f14a04 | ||
|
|
7ba30fd2e7 | ||
|
|
5adea0ba64 | ||
|
|
2262ca6697 | ||
|
|
cd74f74468 | ||
|
|
fced1c0c16 | ||
|
|
1cdcddb5cc | ||
|
|
0e4d7cacad | ||
|
|
6a9ccacd62 | ||
|
|
e591ddcb30 | ||
|
|
cd0af8a771 | ||
|
|
51a7f9dc4a | ||
|
|
61eb426ab4 | ||
|
|
8ea8da4f11 | ||
|
|
07ee29c563 | ||
|
|
9f5d0cf79f | ||
|
|
1413328e6a | ||
|
|
7d09bd30f9 | ||
|
|
dde1b29497 | ||
|
|
4ae3184c59 | ||
|
|
ed53100a0d | ||
|
|
6df598d9f5 | ||
|
|
4aa9df7a7a | ||
|
|
7d5d62dafe | ||
|
|
524650a40e | ||
|
|
02a45826f1 | ||
|
|
e324c14907 | ||
|
|
e1cfb1957f | ||
|
|
2a3586381f | ||
|
|
834d316829 | ||
|
|
c61be8d0e6 | ||
|
|
465212835f | ||
|
|
b3acddea37 | ||
|
|
308598795a | ||
|
|
2cd09c9f0e | ||
|
|
34fee06ca7 | ||
|
|
c7a46d4b8a | ||
|
|
de065f1961 | ||
|
|
c3ab915d2e | ||
|
|
f4a4f42abb | ||
|
|
b2c84ccde3 | ||
|
|
2b344dbd20 | ||
|
|
cde840fdf8 | ||
|
|
f9656dab2d | ||
|
|
a0d759c613 | ||
|
|
0d421f093d | ||
|
|
5f937e54df | ||
|
|
0f9a1c21e6 | ||
|
|
ece7854972 | ||
|
|
8ace8073fd | ||
|
|
43a2f3d0bf | ||
|
|
a9cccbe14f | ||
|
|
36c9e42de2 | ||
|
|
9f566db33c | ||
|
|
c77b2bcca3 | ||
|
|
5a4145fe16 | ||
|
|
039b990d8d | ||
|
|
5a9cb8be3f | ||
|
|
63cd873fef | ||
|
|
69452fa94f | ||
|
|
2ed4ad5502 | ||
|
|
1d2a388830 | ||
|
|
ac05276a51 | ||
|
|
cb9c0f200a | ||
|
|
b1d676b7f7 | ||
|
|
9ddef840b6 | ||
|
|
28fc80bba0 | ||
|
|
b6c42d5e81 | ||
|
|
1c045f1b46 | ||
|
|
95e1b2d612 | ||
|
|
75345e3487 | ||
|
|
f4fe28bd92 | ||
|
|
ace13b94e6 | ||
|
|
5df7e73adf | ||
|
|
e115342fce | ||
|
|
e89150ca0f | ||
|
|
9693a426e3 | ||
|
|
162485d303 | ||
|
|
affcbad501 | ||
|
|
7e916455b3 | ||
|
|
cdc4d485a6 | ||
|
|
c894470d41 | ||
|
|
1b0718bf89 | ||
|
|
53fd24ffcb | ||
|
|
eb90672aae | ||
|
|
28cfd96fdc | ||
|
|
99d5defb1a | ||
|
|
efbc242875 | ||
|
|
d4d58f287f | ||
|
|
dc89db33df | ||
|
|
5dc27959d5 | ||
|
|
4c21355940 | ||
|
|
6f6cb5c8d8 | ||
|
|
821ed310a7 | ||
|
|
a7aa4cc0a9 | ||
|
|
e0ce9ed36d | ||
|
|
caeb740265 | ||
|
|
1bb33cccbe | ||
|
|
d9ed9c5ac1 | ||
|
|
00cac6ed10 | ||
|
|
2e9d7cc6cb | ||
|
|
32cc6cbb6f | ||
|
|
3b1a4fe0c8 | ||
|
|
9569778f2f | ||
|
|
0f61316b24 | ||
|
|
86151b0cea | ||
|
|
1b7a6c66f8 | ||
|
|
0ef76dde41 | ||
|
|
c6d04b3a3d | ||
|
|
e31560cf6b | ||
|
|
3d38fff8b4 | ||
|
|
bc492c0fc1 | ||
|
|
162144202c | ||
|
|
05596527ed | ||
|
|
5fea3471e8 | ||
|
|
131e4014b8 | ||
|
|
01c5be4681 | ||
|
|
fd9a03e147 | ||
|
|
6c17d02bc4 | ||
|
|
6a6f71f238 | ||
|
|
cf686285c2 | ||
|
|
7dfedb732d | ||
|
|
760f2fb731 | ||
|
|
c9705b7556 | ||
|
|
53ec33f07f | ||
|
|
884ef0dbcd | ||
|
|
010413f90a | ||
|
|
4f57236614 | ||
|
|
50bf029625 | ||
|
|
eff52ad877 | ||
|
|
e9ee492d35 | ||
|
|
e415e916e8 | ||
|
|
07084e1c8b | ||
|
|
186a591228 | ||
|
|
a80049fd0a | ||
|
|
d158dd131e | ||
|
|
1147f21999 | ||
|
|
bddd46c8ec | ||
|
|
80e7a45584 | ||
|
|
498365f219 | ||
|
|
056c849352 | ||
|
|
7d6e5a2d01 | ||
|
|
d1c4766d14 | ||
|
|
98473835a2 | ||
|
|
870232bd05 | ||
|
|
c31df32ca0 | ||
|
|
df2b88e230 | ||
|
|
83451d552e | ||
|
|
af7203e0b8 | ||
|
|
98ee3719f9 | ||
|
|
94b5f2dadb | ||
|
|
fe7decd1b0 | ||
|
|
cef084ade9 | ||
|
|
937caab647 | ||
|
|
3fc8017119 | ||
|
|
54637a335f | ||
|
|
277a5ea05d | ||
|
|
9865a7c0ad | ||
|
|
efba4731e4 | ||
|
|
a965984733 | ||
|
|
14d3e559d4 | ||
|
|
3d156a76e3 | ||
|
|
c7a1d1ab0b | ||
|
|
26d43cacdc | ||
|
|
4f5758e666 | ||
|
|
274a6734ef | ||
|
|
f0e3dfd008 | ||
|
|
bc3ff2cecd | ||
|
|
8f329ffb82 | ||
|
|
864b2596b2 | ||
|
|
f3a796e522 | ||
|
|
09f8962df2 | ||
|
|
a13c4ba770 | ||
|
|
040e743b39 | ||
|
|
bf816d3ade | ||
|
|
74b4ab8867 | ||
|
|
6e2359caa0 | ||
|
|
41534816a4 | ||
|
|
30252a0504 | ||
|
|
3dc18037e8 | ||
|
|
57d50582aa | ||
|
|
73c66715c9 | ||
|
|
cb29632a58 | ||
|
|
5c97731a22 | ||
|
|
b2e472e7a2 | ||
|
|
6ac773f350 | ||
|
|
3174f73336 | ||
|
|
3c62e4244e | ||
|
|
c432999572 | ||
|
|
f8d319c11a | ||
|
|
4f72433392 | ||
|
|
2f91cfd0d2 | ||
|
|
d5c5e2b584 | ||
|
|
cbb3ce2c30 | ||
|
|
45af02de04 | ||
|
|
6144df52af | ||
|
|
b0474cb984 | ||
|
|
9a4c9e6487 | ||
|
|
11fff8fa0d | ||
|
|
da8ab2f928 | ||
|
|
6d01384a55 | ||
|
|
109ffac975 | ||
|
|
8c10db3847 | ||
|
|
03088d6010 | ||
|
|
18e0768a2b | ||
|
|
ed4a1fddce | ||
|
|
cfde6f507c | ||
|
|
3468ad1b61 | ||
|
|
e9c79cad43 | ||
|
|
e455e7d878 | ||
|
|
3410f65e79 | ||
|
|
f3de5b6eac | ||
|
|
fcd2a8131a | ||
|
|
62dbe85798 | ||
|
|
1d5e18b062 | ||
|
|
a0ed371389 | ||
|
|
05e4fd3488 | ||
|
|
30a8b7d0b5 | ||
|
|
f8944efe70 | ||
|
|
43072e3812 | ||
|
|
9396d55414 | ||
|
|
82e97cf53e | ||
|
|
cf2a7614a4 | ||
|
|
9e538e7c31 | ||
|
|
4ac21ac039 | ||
|
|
f69dc16241 | ||
|
|
f1a8d419d5 | ||
|
|
8864e54f1f | ||
|
|
dc4df93177 | ||
|
|
043190f397 | ||
|
|
f4d850e168 | ||
|
|
8ec2743ca1 | ||
|
|
ecbee8147b | ||
|
|
f8c6ee3df5 | ||
|
|
fe84f7bef8 | ||
|
|
d653607162 | ||
|
|
082fe180ec | ||
|
|
d3491083a5 | ||
|
|
c3d6ca97e1 | ||
|
|
a14266e464 | ||
|
|
b4d44e1298 | ||
|
|
ca116c35a6 | ||
|
|
78ba429e6a | ||
|
|
dbf8c3c745 | ||
|
|
3602c9785b | ||
|
|
acaac21fd1 | ||
|
|
c98ef94706 | ||
|
|
b0972a2e75 | ||
|
|
2dbb6f9a54 | ||
|
|
785a5fd7c1 | ||
|
|
a55c1e79cf | ||
|
|
d070450cd2 | ||
|
|
09648e4888 | ||
|
|
2adbcf189b | ||
|
|
39c5ffb2a6 | ||
|
|
04a570d31c | ||
|
|
958d3d56b1 | ||
|
|
0e50810c53 | ||
|
|
21e48abbc1 | ||
|
|
b6d5439343 | ||
|
|
93901bdde4 | ||
|
|
d802ed1b36 | ||
|
|
e8f4305e9d | ||
|
|
b38a2287f2 | ||
|
|
1e7675ad4c | ||
|
|
280b5ce3c0 | ||
|
|
fbc5cf514b | ||
|
|
f01087f802 | ||
|
|
4ac6424e87 | ||
|
|
d3c486dd6d | ||
|
|
2d0f6ccba8 | ||
|
|
9a81b8668a | ||
|
|
9481d69d1c | ||
|
|
7615723547 | ||
|
|
338f949259 | ||
|
|
d0192b31a3 | ||
|
|
6127528b50 | ||
|
|
0410572322 | ||
|
|
fd2371cfc2 | ||
|
|
267fcc999c | ||
|
|
84187b6d94 | ||
|
|
5d6482bb3b | ||
|
|
023765c593 | ||
|
|
4a401bbcf3 | ||
|
|
7401c70718 | ||
|
|
bb36bc7edf | ||
|
|
bf1972dc1e | ||
|
|
689dfb1679 | ||
|
|
1169b54456 | ||
|
|
81b81856ee | ||
|
|
fd4b99936e | ||
|
|
09271a8ab9 | ||
|
|
5a8d9acacb | ||
|
|
04d5a5072f | ||
|
|
55c30e1be6 | ||
|
|
97fc84c151 | ||
|
|
4ee0687f3f | ||
|
|
ddff347b91 | ||
|
|
05ef1bd853 | ||
|
|
d0f8bd30a6 | ||
|
|
1a8d3c8b3a | ||
|
|
753687e5c2 | ||
|
|
1a15c01b64 | ||
|
|
7f33e1ca89 | ||
|
|
28d00945ba | ||
|
|
6f40c88f47 | ||
|
|
68dd621082 | ||
|
|
3abfb4ef51 | ||
|
|
1014e52349 | ||
|
|
cda061f723 | ||
|
|
1497c6c1fb | ||
|
|
e41e445b51 | ||
|
|
7ab73190b7 | ||
|
|
450b3a5460 | ||
|
|
38fb542838 | ||
|
|
7ab5098c14 | ||
|
|
bcca80548d | ||
|
|
736c8fbbae | ||
|
|
947562220d | ||
|
|
333523483f | ||
|
|
68ceb17272 | ||
|
|
5bd6596856 | ||
|
|
b3f2a20832 | ||
|
|
e8d8c7a8d7 | ||
|
|
7a91d7fa7e | ||
|
|
c6bd58eb58 | ||
|
|
c2e45c769e | ||
|
|
b08427dde9 | ||
|
|
ffd075b440 | ||
|
|
3fcd228441 | ||
|
|
8383ecfcdf | ||
|
|
eed2333298 | ||
|
|
a2809dacc4 | ||
|
|
b837a31afa | ||
|
|
66b0fcd3c0 | ||
|
|
2efe82309a | ||
|
|
a090400f09 | ||
|
|
84e0eea164 | ||
|
|
bcf12e70e5 | ||
|
|
1ca98b2c09 | ||
|
|
3efdeebcb7 | ||
|
|
16febf8357 | ||
|
|
0f7c4ca671 | ||
|
|
a3172a285f | ||
|
|
84c408ce63 | ||
|
|
40647b179c | ||
|
|
0421cb4200 | ||
|
|
6f1050df4f | ||
|
|
4d16472b91 | ||
|
|
9e89a31b12 | ||
|
|
e6521e7491 | ||
|
|
0a7cbb33b0 | ||
|
|
579242346c | ||
|
|
c42d0a0418 | ||
|
|
3fbb25e25c | ||
|
|
6760d7a315 | ||
|
|
062fbed8fc | ||
|
|
76e4db6f3d | ||
|
|
0cd7e8f227 | ||
|
|
ba1b47f85b | ||
|
|
0a3481e23a | ||
|
|
e33c365144 | ||
|
|
e3ceb50b73 | ||
|
|
6b5772bbbd | ||
|
|
6288cf5ca4 | ||
|
|
f6ecf9a3c9 | ||
|
|
a4e6d962d7 | ||
|
|
7874a4d007 | ||
|
|
1d50663b38 | ||
|
|
ec3c4f94c7 | ||
|
|
6b8bbe4d90 | ||
|
|
7067a8fb0b | ||
|
|
c47abd0dd7 | ||
|
|
68d71bbc01 | ||
|
|
334303e485 | ||
|
|
b95fd53c6e | ||
|
|
c77dd040b4 | ||
|
|
dc027f22e5 | ||
|
|
043500fb27 | ||
|
|
3ceb6ab477 | ||
|
|
999fa44616 | ||
|
|
5f9121ad56 | ||
|
|
b4cf8483d7 | ||
|
|
8a9816e06b | ||
|
|
c0e10683a6 | ||
|
|
cad5a367c3 | ||
|
|
f2453eabb3 | ||
|
|
aa0b11d794 | ||
|
|
b9fa5c5a67 | ||
|
|
751f058f30 | ||
|
|
29274e1d8d | ||
|
|
23ba287897 | ||
|
|
8f1e3606dd | ||
|
|
ac56d1c9d9 | ||
|
|
de2919cb9a | ||
|
|
61943276f0 | ||
|
|
88ce00a3cf | ||
|
|
a3eb6baf58 | ||
|
|
f4fcaa8757 | ||
|
|
40e34a924b | ||
|
|
90f87072e8 | ||
|
|
c785918cbd | ||
|
|
8425e9fe38 | ||
|
|
94764ee089 | ||
|
|
04492ef227 | ||
|
|
c6016a6a85 | ||
|
|
977e2f55de | ||
|
|
4184ff8ff7 | ||
|
|
f3d4fe6209 | ||
|
|
9e5cd92fa9 | ||
|
|
c07f1e1c9f | ||
|
|
fd7bca22e1 | ||
|
|
8f283fe473 | ||
|
|
cb8061c75c | ||
|
|
b1366c32d4 | ||
|
|
b122194425 | ||
|
|
089bf5f0e3 | ||
|
|
f6fa7c9c95 | ||
|
|
938b2e1217 | ||
|
|
dbc6696b68 | ||
|
|
5d632af926 | ||
|
|
ed9e570a12 | ||
|
|
d7ed885984 | ||
|
|
4ab16aaaf7 | ||
|
|
89f435de84 | ||
|
|
bcc6e8d4f6 | ||
|
|
b2137c9fdf | ||
|
|
bee56a82b0 | ||
|
|
bcdbfdfeae | ||
|
|
afbed10feb | ||
|
|
f69ee170ed | ||
|
|
a59976be18 | ||
|
|
40d1e10520 | ||
|
|
5bf81bc111 | ||
|
|
96ad0c7594 | ||
|
|
717a6705e2 | ||
|
|
37ac4724ba | ||
|
|
8c18ef67cf | ||
|
|
dfe6400537 | ||
|
|
f925e8caa6 | ||
|
|
e1254b266d | ||
|
|
fa82a31fa6 | ||
|
|
4612705ec2 | ||
|
|
9577702e8d | ||
|
|
a61b65d01b | ||
|
|
fb483d56a7 | ||
|
|
c5c75386e4 | ||
|
|
2734b9f560 | ||
|
|
dcdbcaf2b5 | ||
|
|
9a8179d311 | ||
|
|
44fe7b6dbb | ||
|
|
95102a5afe | ||
|
|
ae2cdeb2de | ||
|
|
6a0aff84c4 | ||
|
|
e4181182dd | ||
|
|
907f71597a | ||
|
|
aea76f0d5c | ||
|
|
ffa9d0a6db | ||
|
|
9d00458563 | ||
|
|
98adc9e038 | ||
|
|
5cfacec499 | ||
|
|
1df3da361d | ||
|
|
27e9340b3c | ||
|
|
b5af198f0d | ||
|
|
3fe4491a6b | ||
|
|
97c7a4e379 | ||
|
|
d0efd5eefc | ||
|
|
909cabd36d | ||
|
|
3662140201 | ||
|
|
ed8640b964 | ||
|
|
8a5daaed42 | ||
|
|
fc060dfc08 | ||
|
|
947a44d1ee | ||
|
|
eb51b024c9 | ||
|
|
56d0917799 | ||
|
|
c1260716de | ||
|
|
5614fd283a | ||
|
|
da344daa40 | ||
|
|
6fb191570e | ||
|
|
d378f5500a | ||
|
|
9470080762 | ||
|
|
aba0fe6830 | ||
|
|
b1d4d580e5 | ||
|
|
9d69a0a7c7 | ||
|
|
7d2d2575a2 | ||
|
|
b89584db10 | ||
|
|
41a2d5b30f | ||
|
|
e53ff431e1 | ||
|
|
974b6d4a5b | ||
|
|
952fea69fb | ||
|
|
0fca288224 | ||
|
|
d4493fda2c | ||
|
|
4c8fa35324 | ||
|
|
e196413df6 | ||
|
|
727b232fb2 | ||
|
|
6dcdb4e306 | ||
|
|
8cfaeb0e82 | ||
|
|
159a63f538 | ||
|
|
5b1c972723 | ||
|
|
c82058ad57 | ||
|
|
a92b419551 | ||
|
|
8f989d652f | ||
|
|
9483373c33 | ||
|
|
c6923d2df4 | ||
|
|
de2cced192 | ||
|
|
84166029ec | ||
|
|
8d4e3fdd31 | ||
|
|
9a828738cd | ||
|
|
3d6a89e888 | ||
|
|
5b620653f6 | ||
|
|
6578bd0c82 | ||
|
|
e19067c9bb | ||
|
|
9d0a69772c | ||
|
|
117de8e6e2 | ||
|
|
2ae29b1ce3 | ||
|
|
89a67ca77c | ||
|
|
627f9ba091 | ||
|
|
e53e2c2d58 | ||
|
|
217feda041 | ||
|
|
6e77f80827 | ||
|
|
d355b6c48e | ||
|
|
a1c9a41639 | ||
|
|
8a3aba7358 | ||
|
|
d434eabec3 | ||
|
|
7f0767acab | ||
|
|
3d4c80cc3e | ||
|
|
d3b38dda06 | ||
|
|
32b3494aa1 | ||
|
|
cf54510b2c | ||
|
|
6818542c69 | ||
|
|
7484830744 | ||
|
|
29e40c1f1c | ||
|
|
6d23591c31 | ||
|
|
18ae985c3a | ||
|
|
797c99eabe | ||
|
|
f3e3a65e29 | ||
|
|
c9a1df3ae9 | ||
|
|
c36e73de03 | ||
|
|
0429f6c7f5 | ||
|
|
7c5e2511fd | ||
|
|
8e5e641c0e | ||
|
|
dfccdd5b86 | ||
|
|
2cdb588b86 | ||
|
|
0bbb9e6258 | ||
|
|
81d5bc860d | ||
|
|
9f2c8e935a | ||
|
|
256fcb6884 | ||
|
|
9453c61093 | ||
|
|
8b74738568 | ||
|
|
96da4c14a7 | ||
|
|
a4d3146ee9 | ||
|
|
d1a285e5d3 | ||
|
|
3cfe9575e9 | ||
|
|
05bc53d34d | ||
|
|
28ab4b600a | ||
|
|
fca7bcaf43 | ||
|
|
9f0d4085e7 | ||
|
|
af34a4a2c0 | ||
|
|
a1806bb460 | ||
|
|
31ebeeef7d | ||
|
|
e73bd40e85 | ||
|
|
ab3a3bf07a | ||
|
|
0a84739776 | ||
|
|
79223eae50 | ||
|
|
9bf9c236cf | ||
|
|
28bf357553 | ||
|
|
34b1ceb42e | ||
|
|
924abdae9f | ||
|
|
c1e6afca11 | ||
|
|
280354c3f9 | ||
|
|
4645c95702 | ||
|
|
c47a366bc4 | ||
|
|
bb1395b452 | ||
|
|
e7177d3159 | ||
|
|
3e79c9b098 | ||
|
|
e8a3569be6 | ||
|
|
459e85bfea | ||
|
|
1ae34aac81 | ||
|
|
82dec9b81e | ||
|
|
76b628bcb3 | ||
|
|
46d396df72 | ||
|
|
db9c6a3528 | ||
|
|
3b046c9eaa | ||
|
|
3c05e36e79 | ||
|
|
88e35fa8d6 | ||
|
|
93132b96bf | ||
|
|
faf5b980da | ||
|
|
e57d5b89ca | ||
|
|
e690eaae53 | ||
|
|
d3930fdfd9 | ||
|
|
1c621bdf23 | ||
|
|
bf29cbc53f | ||
|
|
261057db9b | ||
|
|
4b653aeac1 | ||
|
|
269bc7e51f | ||
|
|
e69c287293 | ||
|
|
32ab648c79 | ||
|
|
a7e12b7959 | ||
|
|
3f568b22f9 | ||
|
|
2623de1426 | ||
|
|
f5289fe84f | ||
|
|
74912802c6 | ||
|
|
6c20ec193f | ||
|
|
06557aab44 | ||
|
|
f2fab49830 | ||
|
|
934a95d3ef | ||
|
|
2d8d6dd192 | ||
|
|
a86cf20e67 | ||
|
|
b9557b0a86 | ||
|
|
280e33d22a | ||
|
|
3661bab202 | ||
|
|
08f376f2ea | ||
|
|
14438058da | ||
|
|
e8cc85f733 | ||
|
|
c22adbf160 | ||
|
|
dba566a96d | ||
|
|
6d324c76f0 | ||
|
|
3aefd3a4f0 | ||
|
|
ff923a5d45 | ||
|
|
0d63ad5ccf | ||
|
|
08cdd77c28 | ||
|
|
7566215f47 | ||
|
|
a780df8779 | ||
|
|
c106b80c8d | ||
|
|
0dd987bb91 | ||
|
|
0f16b1d32e | ||
|
|
977b482b0b | ||
|
|
e79a20e1ca | ||
|
|
27519e9a33 | ||
|
|
4696af79ec | ||
|
|
b76ed0b28c | ||
|
|
cd216c4c30 | ||
|
|
63c5334c84 | ||
|
|
b7af76b4c5 | ||
|
|
4357da8575 | ||
|
|
83fbaa54f1 | ||
|
|
9f4d99f4ea | ||
|
|
b103fae8c7 | ||
|
|
6df2b8a968 | ||
|
|
55998a6371 | ||
|
|
c40b977d94 | ||
|
|
8ec7668131 | ||
|
|
a61d7999a4 | ||
|
|
01cbe2f398 | ||
|
|
08ba91364d | ||
|
|
989ca61a61 | ||
|
|
c317a7b51a | ||
|
|
dd7cb15bcf | ||
|
|
3205166d80 | ||
|
|
c73801bfed | ||
|
|
10bae7b62a | ||
|
|
e86aaa992f | ||
|
|
b1e604e38c | ||
|
|
1438f1b626 | ||
|
|
cc5846073e | ||
|
|
23c698821f | ||
|
|
3f31a7c769 | ||
|
|
079dd93991 | ||
|
|
7ef9dbb3df | ||
|
|
e819d21fa0 | ||
|
|
07272608d8 | ||
|
|
b019a48bb1 | ||
|
|
74ef7f14a4 | ||
|
|
3eb2fbf745 | ||
|
|
cbd69db292 | ||
|
|
9efa46ae64 | ||
|
|
64fd2c421e | ||
|
|
b6a37d112b | ||
|
|
5dc35b527b | ||
|
|
2fe5a2def0 | ||
|
|
5fc6eb0b5d | ||
|
|
9bbd62f454 | ||
|
|
1366556ace | ||
|
|
fe65932404 | ||
|
|
f031430a6f | ||
|
|
556e8eece6 | ||
|
|
d769b8b8f0 | ||
|
|
ad5256452b | ||
|
|
80d2c85e3c | ||
|
|
9fd92cc3c9 | ||
|
|
f7fc00841b | ||
|
|
507d8021b1 | ||
|
|
bed08c9c66 | ||
|
|
b56b21a898 | ||
|
|
2b5ce84fca | ||
|
|
a80e96cea1 | ||
|
|
670cd9c165 | ||
|
|
7a586e5c19 | ||
|
|
fb99f54206 | ||
|
|
9af8f4e585 | ||
|
|
d3fce9e82c | ||
|
|
4d6a7781d7 | ||
|
|
281feba4ca | ||
|
|
805efb4c47 | ||
|
|
28fe4464d8 | ||
|
|
47f7bd706e | ||
|
|
9089468092 | ||
|
|
78eead6775 | ||
|
|
7645e05e4d | ||
|
|
55422b88d8 | ||
|
|
e8a139c6d4 | ||
|
|
8336b3a286 | ||
|
|
49e06eace5 | ||
|
|
948e8ca325 | ||
|
|
1c18c93be1 | ||
|
|
0ff86c3233 | ||
|
|
31f190d4d5 | ||
|
|
fe2145016c | ||
|
|
a27b4cf5fd | ||
|
|
333e3375e0 | ||
|
|
1691c4e9a3 | ||
|
|
7fec4bae80 | ||
|
|
2c00476bae | ||
|
|
2e5680a4e3 | ||
|
|
a005797afa | ||
|
|
b5214aa20a | ||
|
|
171feb8fc6 | ||
|
|
3a231d4cbe | ||
|
|
0f7237d764 | ||
|
|
575f63ac50 | ||
|
|
d3fcacedd6 | ||
|
|
e66c23fe55 | ||
|
|
e36e28ebd4 | ||
|
|
5b8c78843e | ||
|
|
fc8034b352 | ||
|
|
d70711481e | ||
|
|
78a5889bc6 | ||
|
|
10cc1a42c9 | ||
|
|
40414827f4 | ||
|
|
8e6e3ebad9 | ||
|
|
ac72bee400 | ||
|
|
2977067726 | ||
|
|
6231a7cf6a | ||
|
|
e773029717 | ||
|
|
8e1276c011 | ||
|
|
6972596ce9 | ||
|
|
e9a222418a | ||
|
|
936101041a | ||
|
|
2a63dfa6cc | ||
|
|
0d0330adc2 | ||
|
|
0ca5426184 | ||
|
|
025c921903 | ||
|
|
ac2d06b19d | ||
|
|
f316c314ca | ||
|
|
4033cf2814 | ||
|
|
587e8e2ba5 | ||
|
|
93ce5923e9 | ||
|
|
1f686c489d | ||
|
|
ba38de2212 | ||
|
|
5a1a6b86a8 | ||
|
|
742271ffa3 | ||
|
|
df9426dbc6 | ||
|
|
106ee8f850 | ||
|
|
fc05f5e701 | ||
|
|
06a611c4b4 | ||
|
|
0868a076e0 | ||
|
|
d79822e73a | ||
|
|
2ebf931630 | ||
|
|
8727fce312 | ||
|
|
2df3c9f58d | ||
|
|
448bd14445 | ||
|
|
1618adfab1 | ||
|
|
e2efa23015 | ||
|
|
4f845437a8 | ||
|
|
4ff1a65031 | ||
|
|
21527db747 | ||
|
|
5bb9ba2c54 | ||
|
|
e5eeb2e825 | ||
|
|
45028e5804 | ||
|
|
f2160a4c64 | ||
|
|
4998d3ee79 | ||
|
|
15fa29fb4b | ||
|
|
b891c708cf | ||
|
|
2361572012 | ||
|
|
7c38b292f8 | ||
|
|
c785267eb8 | ||
|
|
6aaae06217 | ||
|
|
6a8edc1d43 | ||
|
|
6cadabf910 | ||
|
|
eea76a0ebe | ||
|
|
147c6929a2 | ||
|
|
0a63adce68 | ||
|
|
acc2fb8486 | ||
|
|
3b3e89d5d6 | ||
|
|
e0c134b8bf | ||
|
|
418d7a36ff | ||
|
|
4beb98131a | ||
|
|
12ae60052f | ||
|
|
eb5b422842 | ||
|
|
ba279f9464 | ||
|
|
94c8378f18 | ||
|
|
01fdf683cf | ||
|
|
8c0d5b6e80 | ||
|
|
50194147ac | ||
|
|
31c56f5400 | ||
|
|
f8f8f754b0 | ||
|
|
6c59e77008 | ||
|
|
e2751292da | ||
|
|
e2068ad426 | ||
|
|
3ed094d142 | ||
|
|
5eb1fb6cb2 | ||
|
|
255e8c13cf | ||
|
|
88317a2888 | ||
|
|
602e226d36 | ||
|
|
f12c61e984 | ||
|
|
72ad746b85 | ||
|
|
2468974a53 | ||
|
|
666705c0a4 | ||
|
|
c20b569dbe | ||
|
|
3b8b0eb1fb | ||
|
|
910788ed9c | ||
|
|
80ab7eaf15 | ||
|
|
a1c4f6fbb7 | ||
|
|
04c9cae5d9 | ||
|
|
2fb36cbd50 | ||
|
|
2dcbec6c06 | ||
|
|
bda87f606f | ||
|
|
47dff0f3db | ||
|
|
3589f17824 | ||
|
|
be0b485669 | ||
|
|
4b71bbc988 | ||
|
|
265f0b5225 | ||
|
|
4aa9d2aa1e | ||
|
|
777ba71ffe | ||
|
|
62e6b4eeb6 | ||
|
|
d206f3d2ad | ||
|
|
bd478db770 | ||
|
|
d241438d49 | ||
|
|
3328d33ed0 | ||
|
|
83f93889e1 | ||
|
|
3b5fd53e2d | ||
|
|
da3dc5c5fe | ||
|
|
1b5217b1d6 | ||
|
|
a53d197db3 | ||
|
|
2bb0e1a604 | ||
|
|
b15686cb79 | ||
|
|
aa3c54c73f | ||
|
|
f115751d54 | ||
|
|
64e7b7774e | ||
|
|
21e9e8cf68 | ||
|
|
bf512bb8ee | ||
|
|
4a0f052242 | ||
|
|
c0886be148 | ||
|
|
f86395aadf | ||
|
|
c0db11c607 | ||
|
|
573d41b73c | ||
|
|
d8987c170f | ||
|
|
1567440665 | ||
|
|
26685782b3 | ||
|
|
d63a50c3c3 | ||
|
|
ce351e69b1 | ||
|
|
e4415d21d3 | ||
|
|
79dc95cca6 | ||
|
|
01a7ec0866 | ||
|
|
4def730de7 | ||
|
|
d50ed6bfb8 | ||
|
|
28f56a383e | ||
|
|
32ad292611 | ||
|
|
e12e584ca8 | ||
|
|
42a5f1e99a | ||
|
|
2dd99c3f11 | ||
|
|
c66ec5413c | ||
|
|
ae70ec7946 | ||
|
|
62ea5c3a91 | ||
|
|
327a78604a | ||
|
|
d72fc304e0 | ||
|
|
21b1d22563 | ||
|
|
7ed5599a4b | ||
|
|
9d6d3969f1 | ||
|
|
cf0e6f3609 | ||
|
|
81b0b6ea7b | ||
|
|
b6312af19f | ||
|
|
4ed47c4b12 | ||
|
|
80ea36256f | ||
|
|
c0ad2efb24 | ||
|
|
7b203fcff6 | ||
|
|
9a7adb5455 | ||
|
|
40c0220c47 | ||
|
|
36ad40b18c | ||
|
|
4e15c4fb47 | ||
|
|
4382df03fa | ||
|
|
d11a34a351 | ||
|
|
f737c97df0 | ||
|
|
8e48c4ff6a | ||
|
|
44ad61e56c | ||
|
|
d73d2abe85 | ||
|
|
d81d2b41ab | ||
|
|
427ee93f11 | ||
|
|
366c0e084b | ||
|
|
068d8615d2 | ||
|
|
baaa73ee1e | ||
|
|
c65fcc003d | ||
|
|
d9dbc6a844 | ||
|
|
d6fdd31fdc | ||
|
|
3379eebe4b | ||
|
|
475eddf678 | ||
|
|
4138a80611 | ||
|
|
03f597328a | ||
|
|
df17a2c749 | ||
|
|
a30a3cf31c | ||
|
|
c7b0d8494d | ||
|
|
e86de0db56 | ||
|
|
4114f9c21b | ||
|
|
70b44ad32a | ||
|
|
0f56cfdd13 | ||
|
|
52ec83a0ed | ||
|
|
6382e21fb2 | ||
|
|
6b91aa0a18 | ||
|
|
42af8eada2 | ||
|
|
cbf06a5d64 | ||
|
|
92700509c8 | ||
|
|
9b94169aaa | ||
|
|
e848099d1b | ||
|
|
5d9f42050a | ||
|
|
80d0f98263 | ||
|
|
c64a985307 | ||
|
|
7909ebedc2 | ||
|
|
1d06a94385 | ||
|
|
7c605ddf1c | ||
|
|
ee2f3d21da | ||
|
|
fb3a7db080 | ||
|
|
040aa11ceb | ||
|
|
b89a4e49b9 | ||
|
|
eefcdad013 | ||
|
|
74ae3edf86 | ||
|
|
699f86c535 | ||
|
|
a671b0423c | ||
|
|
57c43dd376 | ||
|
|
99175f4294 | ||
|
|
ce669edfa1 | ||
|
|
a3aa41888c | ||
|
|
7d188d630c | ||
|
|
8ee9a3e902 | ||
|
|
834b76b8c3 | ||
|
|
4bd8b81bb0 | ||
|
|
411c84bf2c | ||
|
|
c61bb52c95 | ||
|
|
2c7df1550c | ||
|
|
d40884d1b0 | ||
|
|
637c9b1611 | ||
|
|
ad77d67cb7 | ||
|
|
00af067d82 | ||
|
|
eed299a31b | ||
|
|
bf79bd4194 | ||
|
|
5c4ffb36de | ||
|
|
ffe1665d02 | ||
|
|
b1a43cd04e | ||
|
|
b7a54497b5 | ||
|
|
6749fef227 | ||
|
|
63e9ea18f8 | ||
|
|
d307242e10 | ||
|
|
b43c09c372 | ||
|
|
f009962256 | ||
|
|
44b6b72e5e | ||
|
|
35d4993c3d | ||
|
|
705404ff8e | ||
|
|
00f784cda8 | ||
|
|
3bc4e7fd20 | ||
|
|
3a65822023 | ||
|
|
37123cd285 | ||
|
|
fe267e30b9 | ||
|
|
02d42fda6d | ||
|
|
3b6ec782d8 | ||
|
|
031f8a093f | ||
|
|
8a000a586d | ||
|
|
dba5e16269 | ||
|
|
380854fd2c | ||
|
|
680b8174ac | ||
|
|
5e45fd4ac6 | ||
|
|
60af2ec9a4 | ||
|
|
920a380413 | ||
|
|
f757f86b6c | ||
|
|
96bbf729dd | ||
|
|
7215afa21c | ||
|
|
90979061e8 | ||
|
|
670ca75c8a | ||
|
|
b73c46c2fd | ||
|
|
405f3267b8 | ||
|
|
c63fbbbcd1 | ||
|
|
23a59aebad | ||
|
|
5f3d69a52b | ||
|
|
c6ca827150 | ||
|
|
79a88ecfa6 | ||
|
|
d8c010ac1e | ||
|
|
bd03a556c0 | ||
|
|
f6435044dc | ||
|
|
3ad2e44cc3 |
616 changed files with 32496 additions and 17707 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -6,6 +6,7 @@ regression/temp*.html
|
|||
performance/temp*.html
|
||||
.idea/workspace.xml
|
||||
*~
|
||||
*.swp
|
||||
angular.js.tmproj
|
||||
/node_modules/
|
||||
/components/
|
||||
|
|
@ -15,3 +16,5 @@ angular.xcodeproj
|
|||
.agignore
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
/tmp/
|
||||
/scripts/bower/bower-*
|
||||
|
|
|
|||
3
.jscs.json
Normal file
3
.jscs.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"disallowKeywords": ["with"]
|
||||
}
|
||||
22
.jscs.json.todo
Normal file
22
.jscs.json.todo
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// This is an incomplete TODO list of checks we want to start enforcing
|
||||
//
|
||||
// The goal is to enable these checks one by one by moving them to .jscs.json along with commits
|
||||
// that correct the existing code base issues and make the new check pass.
|
||||
|
||||
{
|
||||
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"requireRightStickedOperators": ["!"],
|
||||
"requireLeftStickedOperators": [","],
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireLineFeedAtFileEnd": true,
|
||||
"validateJSDoc": {
|
||||
"checkParamNames": true,
|
||||
"requireParamTypes": true
|
||||
}
|
||||
}
|
||||
22
.travis.yml
22
.travis.yml
|
|
@ -1,12 +1,28 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- 0.10
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- JOB=unit
|
||||
- JOB=e2e
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./lib/sauce/sauce_connect_setup.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt bower
|
||||
- grunt bower
|
||||
- grunt package-without-bower
|
||||
- ./scripts/travis/wait_for_browser_provider.sh
|
||||
|
||||
script:
|
||||
- ./travis_build.sh
|
||||
- ./scripts/travis/build.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/travis/print_logs.sh
|
||||
|
|
|
|||
1275
CHANGELOG.md
1275
CHANGELOG.md
File diff suppressed because it is too large
Load diff
268
CONTRIBUTING.md
268
CONTRIBUTING.md
|
|
@ -1,32 +1,262 @@
|
|||
## Submitting issues
|
||||
#Contributing to AngularJS
|
||||
|
||||
If you have questions about how to use AngularJS, please direct these to the
|
||||
[Google Group][groups] discussion list or [StackOverflow][stackoverflow]. We are
|
||||
also available on [IRC][irc].
|
||||
We'd love for you to contribute to our source code and to make AngularJS even better than it is
|
||||
today! Here are the guidelines we'd like you to follow:
|
||||
|
||||
### Guidelines
|
||||
## Got a Question or Problem?
|
||||
|
||||
* Search the archive first, it's likely that your question was already answered.
|
||||
* A live example demonstrating your problem or question, will get an answer faster.
|
||||
* Create one using this [template][template]
|
||||
* If you get help, help others. Good karma rulez!
|
||||
If you have questions about how to use AngularJS, please direct these to the [Google Group][groups]
|
||||
discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][irc].
|
||||
|
||||
## Found an Issue?
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||
submitting and issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
|
||||
with a fix.
|
||||
|
||||
***Localization Issue:*** *Angular.js uses the [Google Closure I18N library], to generate its own I18N files. This means that
|
||||
any changes to these files would be lost the next time that we import the library. The recommended
|
||||
approach is to submit a patch to the I18N project directly, instead of submitting it here.*
|
||||
|
||||
**Please see the Submission Guidelines below**.
|
||||
|
||||
## Want a Feature?
|
||||
You can request a new feature by submitting an issue to our [GitHub Repository][github]. If you
|
||||
would like to implement a new feature then consider what kind of change it is:
|
||||
|
||||
* **Major Changes** that you wish to contribute to the project should be discussed first on our
|
||||
[dev mailing list][angular-dev] or [IRC][irc] so that we can better coordinate our efforts, prevent
|
||||
duplication of work, and help you to craft the change so that it is successfully accepted into the
|
||||
project.
|
||||
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
|
||||
|
||||
|
||||
## Want a Doc Fix?
|
||||
If you want to help improve the docs, it's a good idea to let others know what you're working on to
|
||||
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
|
||||
Comment on an issue to let others know what you're working on, or create a new issue if your work
|
||||
doesn't fit within the scope of any of the existing doc fix projects.
|
||||
|
||||
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
|
||||
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
|
||||
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
|
||||
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
|
||||
|
||||
## Submission Guidelines
|
||||
|
||||
### Submitting an Issue
|
||||
Before you submit your issue search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues.
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
|
||||
[template]: http://plnkr.co/edit/gist:3510140
|
||||
* **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(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) 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)
|
||||
|
||||
## Contributing to Source Code
|
||||
Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069
|
||||
|
||||
We'd love for you to contribute to our source code and to make AngularJS even
|
||||
better than it is today!
|
||||
**If you get help, help others. Good karma rulez!**
|
||||
|
||||
Please read the [contribution guidelines][contribute] to learn about how to submit code as well as
|
||||
other useful info like how to build and test AngularJS code.
|
||||
### Submitting a Pull Request
|
||||
Before you submit your pull request consider the following guidelines:
|
||||
|
||||
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
requests. We cannot accept code without this.
|
||||
* Make your changes in a new git branch
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
`validate-commit-msg.js`):
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
|
||||
* Build your changes locally to ensure all the tests pass
|
||||
|
||||
```shell
|
||||
grunt test
|
||||
```
|
||||
|
||||
* Push your branch to Github:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
* In Github, send a pull request to `angular:master`.
|
||||
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
|
||||
repository to update the Pull Request:
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on Github:
|
||||
|
||||
```shell
|
||||
git push origin --delete my-fix-branch
|
||||
```
|
||||
|
||||
* Check out the master branch:
|
||||
|
||||
```shell
|
||||
git checkout master -f
|
||||
```
|
||||
|
||||
* Delete the local branch:
|
||||
|
||||
```shell
|
||||
git branch -D my-fix-branch
|
||||
```
|
||||
|
||||
* Update your master with the latest upstream version:
|
||||
|
||||
```shell
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
### GitHub Pull Request Helper
|
||||
|
||||
We track Pull Requests by attaching labels and assigning to milestones. For some reason GitHub
|
||||
does not provide a good UI for managing labels on Pull Requests (unlike Issues). We have developed
|
||||
a simple Chrome Extension that enables you to view (and manage if you have permission) the labels
|
||||
on Pull Requests. You can get the extension from the Chrome WebStore -
|
||||
[GitHub PR Helper][github-pr-helper]
|
||||
|
||||
## Coding Rules
|
||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||
|
||||
* All features or bug fixes **must be tested** by one or more [specs][unit-testing].
|
||||
* All public API methods **must be documented** with ngdoc, an extended version of jsdoc (we added
|
||||
support for markdown and templating via @ngdoc tag). To see how we document our APIs, please check
|
||||
out the existing ngdocs and see [this wiki page][ngDocs].
|
||||
* With the exceptions listed below, we follow the rules contained in
|
||||
[Google's JavaScript Style Guide][js-style-guide]:
|
||||
* **Do not use namespaces**: Instead, wrap the entire angular code base in an anonymous closure and
|
||||
export our API explicitly rather than implicitly.
|
||||
* Wrap all code at **100 characters**.
|
||||
* Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypical
|
||||
inheritance only when absolutely necessary.
|
||||
* We **love functions and closures** and, whenever possible, prefer them over objects.
|
||||
* To write concise code that can be better minified, we **use aliases internally** that map to the
|
||||
external API. See our existing code to see what we mean.
|
||||
* We **don't go crazy with type annotations** for private internal APIs unless it's an internal API
|
||||
that is used throughout AngularJS. The best guidance is to do what makes the most sense.
|
||||
|
||||
## Git Commit Guidelines
|
||||
|
||||
We have very precise rules over how our git commit messages can be formatted. This leads to **more
|
||||
readable messages** that are easy to follow when looking through the **project history**. But also,
|
||||
we use the git commit messages to **generate the AngularJS change log**.
|
||||
|
||||
### Commit Message Format
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on github as well as in various git tools.
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
* **docs**: Documentation only changes
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug or adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests
|
||||
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||
generation
|
||||
|
||||
### Scope
|
||||
The scope could be anything specifying place of the commit change. For example `$location`,
|
||||
`$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc...
|
||||
|
||||
### Subject
|
||||
The subject contains succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize first letter
|
||||
* no dot (.) at the end
|
||||
|
||||
###Body
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
###Footer
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
## Signing the CLA
|
||||
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
* For individuals we have a [simple click-through form][individual-cla].
|
||||
* For corporations we'll need you to
|
||||
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
||||
|
||||
## Further Information
|
||||
You can find out more detailed information about contributing in the
|
||||
[AngularJS documentation][contributing].
|
||||
|
||||
|
||||
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
|
||||
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[contribute]: http://docs.angularjs.org/misc/contribute
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
|
||||
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
|
||||
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[contributing]: http://docs.angularjs.org/misc/contribute
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
|
|
|||
153
Gruntfile.js
153
Gruntfile.js
|
|
@ -1,16 +1,11 @@
|
|||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-jasmine-node');
|
||||
grunt.loadNpmTasks('grunt-ddescribe-iit');
|
||||
grunt.loadNpmTasks('grunt-merge-conflict');
|
||||
grunt.loadNpmTasks('grunt-parallel');
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
|
|
@ -27,15 +22,9 @@ module.exports = function(grunt) {
|
|||
|
||||
parallel: {
|
||||
travis: {
|
||||
options: {
|
||||
stream: true
|
||||
},
|
||||
tasks: [
|
||||
util.parallelTask('tests:docs'),
|
||||
util.parallelTask('tests:modules'),
|
||||
util.parallelTask('tests:jquery'),
|
||||
util.parallelTask('tests:jqlite'),
|
||||
util.parallelTask('tests:end2end')
|
||||
util.parallelTask(['test:unit', 'test:docgen', 'test:promises-aplus', 'tests:docs'], {stream: true}),
|
||||
util.parallelTask(['test:e2e'])
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -61,6 +50,11 @@ module.exports = function(grunt) {
|
|||
},
|
||||
testserver: {
|
||||
options: {
|
||||
// We use end2end task (which does not start the webserver)
|
||||
// and start the webserver as a separate process (in travis_build.sh)
|
||||
// to avoid https://github.com/joyent/libuv/issues/826
|
||||
port: 8000,
|
||||
hostname: '0.0.0.0',
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
function(req, resp, next) {
|
||||
|
|
@ -84,9 +78,7 @@ module.exports = function(grunt) {
|
|||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
docs: 'karma-docs.conf.js',
|
||||
modules: 'karma-modules.conf.js',
|
||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
||||
end2end: 'karma-e2e.conf.js'
|
||||
modules: 'karma-modules.conf.js'
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -98,9 +90,59 @@ module.exports = function(grunt) {
|
|||
},
|
||||
|
||||
|
||||
clean: {build: ['build']},
|
||||
runprotractor: {
|
||||
normal: 'protractor-conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {
|
||||
build: ['build'],
|
||||
tmp: ['tmp']
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: true,
|
||||
},
|
||||
ng: {
|
||||
files: { src: files['angularSrc'] },
|
||||
},
|
||||
ngAnimate: {
|
||||
files: { src: 'src/ngAnimate/**/*.js' },
|
||||
},
|
||||
ngCookies: {
|
||||
files: { src: 'src/ngCookies/**/*.js' },
|
||||
},
|
||||
ngLocale: {
|
||||
files: { src: 'src/ngLocale/**/*.js' },
|
||||
},
|
||||
ngMock: {
|
||||
files: { src: 'src/ngMock/**/*.js' },
|
||||
},
|
||||
ngResource: {
|
||||
files: { src: 'src/ngResource/**/*.js' },
|
||||
},
|
||||
ngRoute: {
|
||||
files: { src: 'src/ngRoute/**/*.js' },
|
||||
},
|
||||
ngSanitize: {
|
||||
files: { src: 'src/ngSanitize/**/*.js' },
|
||||
},
|
||||
ngScenario: {
|
||||
files: { src: 'src/ngScenario/**/*.js' },
|
||||
},
|
||||
ngTouch: {
|
||||
files: { src: 'src/ngTouch/**/*.js' },
|
||||
}
|
||||
},
|
||||
|
||||
jscs: {
|
||||
src: ['src/**/*.js', 'test/**/*.js'],
|
||||
options: {
|
||||
config: ".jscs.json"
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
|
|
@ -117,54 +159,46 @@ module.exports = function(grunt) {
|
|||
src: util.wrap([files['angularSrc']], 'angular'),
|
||||
styles: {
|
||||
css: ['css/angular.css'],
|
||||
generateCspCssFile: true,
|
||||
minify: true
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
dest: 'build/angular-loader.js',
|
||||
src: util.wrap(['src/loader.js'], 'loader')
|
||||
src: util.wrap(files['angularLoader'], 'loader')
|
||||
},
|
||||
touch: {
|
||||
dest: 'build/angular-touch.js',
|
||||
src: util.wrap([
|
||||
'src/ngTouch/touch.js',
|
||||
'src/ngTouch/swipe.js',
|
||||
'src/ngTouch/directive/ngClick.js',
|
||||
'src/ngTouch/directive/ngSwipe.js'
|
||||
], 'module')
|
||||
src: util.wrap(files['angularModules']['ngTouch'], 'module')
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: ['src/ngMock/angular-mocks.js'],
|
||||
src: util.wrap(files['angularModules']['ngMock'], 'module'),
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
dest: 'build/angular-sanitize.js',
|
||||
src: util.wrap([
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/filter/linky.js'
|
||||
], 'module')
|
||||
src: util.wrap(files['angularModules']['ngSanitize'], 'module')
|
||||
},
|
||||
resource: {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
||||
src: util.wrap(files['angularModules']['ngResource'], 'module')
|
||||
},
|
||||
animate: {
|
||||
dest: 'build/angular-animate.js',
|
||||
src: util.wrap(['src/ngAnimate/animate.js'], 'module')
|
||||
src: util.wrap(files['angularModules']['ngAnimate'], 'module')
|
||||
},
|
||||
route: {
|
||||
dest: 'build/angular-route.js',
|
||||
src: util.wrap([
|
||||
'src/ngRoute/routeUtils.js',
|
||||
'src/ngRoute/route.js',
|
||||
'src/ngRoute/routeParams.js',
|
||||
'src/ngRoute/directive/ngView.js'
|
||||
], 'module')
|
||||
src: util.wrap(files['angularModules']['ngRoute'], 'module')
|
||||
},
|
||||
cookies: {
|
||||
dest: 'build/angular-cookies.js',
|
||||
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
|
||||
src: util.wrap(files['angularModules']['ngCookies'], 'module')
|
||||
},
|
||||
"promises-aplus-adapter": {
|
||||
dest:'tmp/promises-aplus-adapter++.js',
|
||||
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -185,8 +219,8 @@ module.exports = function(grunt) {
|
|||
process: ['build/docs/*.html', 'build/docs/.htaccess']
|
||||
},
|
||||
|
||||
"jasmine-node": {
|
||||
run: { spec: 'docs/spec' }
|
||||
"jasmine_node": {
|
||||
projectRoot: 'docs/spec'
|
||||
},
|
||||
|
||||
"ddescribe-iit": {
|
||||
|
|
@ -216,32 +250,55 @@ module.exports = function(grunt) {
|
|||
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip'},
|
||||
options: {archive: 'build/' + dist +'.zip', mode: 'zip'},
|
||||
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
|
||||
}
|
||||
},
|
||||
|
||||
shell:{
|
||||
"promises-aplus-tests":{
|
||||
options:{
|
||||
//stdout:true,
|
||||
stderr:true,
|
||||
failOnError:true
|
||||
},
|
||||
command:path.normalize('./node_modules/.bin/promises-aplus-tests tmp/promises-aplus-adapter++.js')
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
write: {
|
||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||
},
|
||||
|
||||
bump: {
|
||||
options: {
|
||||
files: ['package.json'],
|
||||
commit: false,
|
||||
createTag: false,
|
||||
push: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['package','test:unit', 'tests:docs', 'test:e2e']);
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
|
||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
|
||||
grunt.registerTask('test:e2e', 'Run the end to end tests with Karma and keep a test server running in the background', ['connect:testserver', 'tests:end2end']);
|
||||
grunt.registerTask('test:docgen', ['jasmine-node']);
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
|
||||
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||
grunt.registerTask('test:docgen', ['jasmine_node']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
|
||||
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict']);
|
||||
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
23
README.closure.md
Normal file
23
README.closure.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Using AngularJS with the Closure Compiler
|
||||
=========================================
|
||||
|
||||
The Closure Compiler project contains externs definitions for AngularJS
|
||||
JavaScript in its `contrib/externs` directory.
|
||||
|
||||
The definitions contain externs for use with the Closure compiler (aka
|
||||
JSCompiler). Passing these files to the --externs parameter of a compiler
|
||||
pass allows using type annotations for AngularJS objects. For example,
|
||||
Angular's $scope objects can be annotated as:
|
||||
```js
|
||||
/** @type {angular.Scope} */
|
||||
var scope = $scope;
|
||||
```
|
||||
|
||||
This allows JSCompiler to type check accesses to scope, give warnings about
|
||||
missing methods or incorrect arguments, and also prevents renaming of property
|
||||
accesses with advanced compilation.
|
||||
|
||||
The externs are incomplete and maintained on an as-needed basis, but strive to
|
||||
be correct. Externs for individual modules should be added in separate files.
|
||||
|
||||
See https://developers.google.com/closure/compiler/
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
AngularJS
|
||||
AngularJS [](https://travis-ci.org/angular/angular.js)
|
||||
=========
|
||||
|
||||
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you
|
||||
|
|
@ -7,7 +7,7 @@ syntax to express your application’s components clearly and succinctly. It au
|
|||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
||||
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ it makes development fun!
|
|||
* API Docs: http://docs.angularjs.org/api
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
|
|
@ -37,3 +38,7 @@ To execute end-to-end (e2e) tests, use:
|
|||
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
|
|
|
|||
63
TRIAGING.md
Normal file
63
TRIAGING.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Triage new issues/PRs on github
|
||||
|
||||
This document shows the steps the Angular team is using to triage issues.
|
||||
The labels are used later on for planning releases.
|
||||
|
||||
## Tips ##
|
||||
|
||||
* install [github pr helper extension](https://github.com/petebacondarwin/github-pr-helper) and become 356% more productive
|
||||
* Label "resolution:*"
|
||||
* these tags can be used for labeling a closed issue/PR with a reason why it was closed. (we can add reasons as we need them, right there are only a few rejection reasons. it doesn't make sense to label issues that were fixed or prs that were merged)
|
||||
|
||||
|
||||
## Automatic processing ##
|
||||
|
||||
We have automatic tools (e.g. Mary Poppins) that automatically add comments / labels to issues and PRs.
|
||||
The following is done automatically and should not be done manually:
|
||||
|
||||
* Label "cla: yes" or "cla: no" for pull requests
|
||||
|
||||
## Process ##
|
||||
|
||||
1. Open list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
|
||||
1. Assign milestone:
|
||||
* "Docs only" milestone - for documentation PR -> **Done**.
|
||||
* Current/next milestone - regressions
|
||||
* 1.2.x - everything else
|
||||
1. Label "GH: *" (to be automated via Mary Poppins)
|
||||
* PR - issue is a PR
|
||||
* issue - otherwise
|
||||
1. Bugs:
|
||||
* Label "Type: Bug"
|
||||
* Label "Type: Regression" - if the bug is a regression
|
||||
* Duplicate? - Check if there are comments pointing out that this is a dupe, if they do exist verify that this is indeed a dupe and close it and go to the last step
|
||||
* Reproducible? - Steps to reproduce the bug are clear, if not ask for clarification (ideally plunker or fiddle)
|
||||
* Reproducible on master? - http://code.angularjs.org/snapshot/
|
||||
|
||||
1. Non bugs:
|
||||
* Label "Type: Feature" or "Type: Chore" or "Type: Perf"
|
||||
* Label "needs: breaking change" - if needed
|
||||
* Label "needs: public api" - if a new public api is needed
|
||||
* Understandable? - verify if the description of the request is clear. if not ask for clarification
|
||||
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
|
||||
|
||||
1. Label "component: *"
|
||||
* In rare cases, it's ok to have multiple components.
|
||||
1. Label "impact: *"
|
||||
* small - obscure issue affecting one or handful of developers
|
||||
* medium - impacts some usage patterns
|
||||
* large - impacts most or all of angular apps
|
||||
1. Label "complexity: *"
|
||||
* small - trivial change
|
||||
* medium - non-trivial but straightforward change
|
||||
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
|
||||
1. Label "PRs plz!" for "GH: issue"
|
||||
* if complexity is small or medium and the problem as well as solution are well captured in the issue
|
||||
1. Label "origin: google" for issues from Google
|
||||
1. Label "high priority" for security issues, major performance regressions or memory leaks
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
67
angularFiles.js
vendored
67
angularFiles.js
vendored
|
|
@ -20,12 +20,14 @@ angularFiles = {
|
|||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/interval.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/timeout.js',
|
||||
|
|
@ -64,24 +66,41 @@ angularFiles = {
|
|||
'src/ng/directive/style.js'
|
||||
],
|
||||
|
||||
'angularSrcModules': [
|
||||
'src/ngAnimate/animate.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngRoute/routeUtils.js',
|
||||
'src/ngRoute/route.js',
|
||||
'src/ngRoute/routeParams.js',
|
||||
'src/ngRoute/directive/ngView.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngTouch/touch.js',
|
||||
'src/ngTouch/swipe.js',
|
||||
'src/ngTouch/directive/ngClick.js',
|
||||
'src/ngTouch/directive/ngSwipe.js',
|
||||
'docs/components/angular-bootstrap/bootstrap.js'
|
||||
'angularLoader': [
|
||||
'src/minErr.js',
|
||||
'src/loader.js'
|
||||
],
|
||||
|
||||
'angularModules': {
|
||||
'ngAnimate': [
|
||||
'src/ngAnimate/animate.js'
|
||||
],
|
||||
'ngCookies': [
|
||||
'src/ngCookies/cookies.js'
|
||||
],
|
||||
'ngResource': [
|
||||
'src/ngResource/resource.js'
|
||||
],
|
||||
'ngRoute': [
|
||||
'src/ngRoute/route.js',
|
||||
'src/ngRoute/routeParams.js',
|
||||
'src/ngRoute/directive/ngView.js'
|
||||
],
|
||||
'ngSanitize': [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/filter/linky.js'
|
||||
],
|
||||
'ngMock': [
|
||||
'src/ngMock/angular-mocks.js'
|
||||
],
|
||||
'ngTouch': [
|
||||
'src/ngTouch/touch.js',
|
||||
'src/ngTouch/swipe.js',
|
||||
'src/ngTouch/directive/ngClick.js',
|
||||
'src/ngTouch/directive/ngSwipe.js'
|
||||
],
|
||||
},
|
||||
|
||||
'angularScenario': [
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
|
|
@ -100,8 +119,7 @@ angularFiles = {
|
|||
],
|
||||
|
||||
'angularTest': [
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/*.js',
|
||||
|
|
@ -143,8 +161,7 @@ angularFiles = {
|
|||
'build/angular.js',
|
||||
'@angularSrcModules',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/matchers.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
|
|
@ -173,6 +190,16 @@ angularFiles = {
|
|||
]
|
||||
};
|
||||
|
||||
angularFiles['angularSrcModules'] = [].concat(
|
||||
angularFiles['angularModules']['ngAnimate'],
|
||||
angularFiles['angularModules']['ngCookies'],
|
||||
angularFiles['angularModules']['ngResource'],
|
||||
angularFiles['angularModules']['ngRoute'],
|
||||
angularFiles['angularModules']['ngSanitize'],
|
||||
angularFiles['angularModules']['ngMock'],
|
||||
angularFiles['angularModules']['ngTouch']
|
||||
);
|
||||
|
||||
if (exports) {
|
||||
exports.files = angularFiles;
|
||||
exports.mergeFilesFor = function() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "AngularJS",
|
||||
"devDependencies": {
|
||||
"jquery": "git://github.com/components/jquery.git#v1.8.3",
|
||||
"jquery": "1.10.2",
|
||||
"lunr.js": "0.4.0",
|
||||
"google-code-prettify": "1.0.0",
|
||||
"components-font-awesome": "3.1.0",
|
||||
"bootstrap": "https://raw.github.com/twbs/bootstrap/v2.0.2/docs/assets/bootstrap.zip",
|
||||
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.1/assets/ng-closure-runner.zip"
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.2/assets/ng-closure-runner.zip"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
changelog.js
10
changelog.js
|
|
@ -16,7 +16,6 @@ var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
|
|||
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
|
||||
|
||||
var EMPTY_COMPONENT = '$$';
|
||||
var MAX_SUBJECT_LENGTH = 80;
|
||||
|
||||
|
||||
var warn = function() {
|
||||
|
|
@ -54,11 +53,6 @@ var parseRawCommit = function(raw) {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (match[3].length > MAX_SUBJECT_LENGTH) {
|
||||
warn('Too long subject: %s %s', msg.hash, msg.subject);
|
||||
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
|
||||
}
|
||||
|
||||
msg.type = match[1];
|
||||
msg.component = match[2];
|
||||
msg.subject = match[3];
|
||||
|
|
@ -148,6 +142,7 @@ var writeChangelog = function(stream, commits, version) {
|
|||
var sections = {
|
||||
fix: {},
|
||||
feat: {},
|
||||
perf: {},
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
|
|
@ -175,6 +170,7 @@ var writeChangelog = function(stream, commits, version) {
|
|||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
printSection(stream, 'Bug Fixes', sections.fix);
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Performance Improvements', sections.perf);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks, false);
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +188,7 @@ var getPreviousTag = function() {
|
|||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
|
||||
readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
163
compare-master-to-stable.js
Executable file
163
compare-master-to-stable.js
Executable file
|
|
@ -0,0 +1,163 @@
|
|||
#!/usr/local/bin/node
|
||||
|
||||
var util = require('util');
|
||||
var cp = require('child_process');
|
||||
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var semver = require('semver');
|
||||
|
||||
var exec = function (cmd) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args.unshift(cmd);
|
||||
var fullCmd = util.format.apply(util, args);
|
||||
return Q.nfcall(cp.exec, fullCmd).then(function (out) {
|
||||
return out[0].split('\n');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var andThen = function (fn, after) {
|
||||
return function () {
|
||||
return fn.apply(this, arguments).then(after);
|
||||
};
|
||||
};
|
||||
|
||||
var oneArg = function (fn) {
|
||||
return function (arg) {
|
||||
return fn(arg);
|
||||
};
|
||||
};
|
||||
|
||||
var oneLine = function (lines) {
|
||||
return lines[0].trim();
|
||||
};
|
||||
|
||||
var noArgs = function (fn) {
|
||||
return function () {
|
||||
return fn();
|
||||
};
|
||||
};
|
||||
|
||||
var identity = function (i) { return i; };
|
||||
|
||||
// like Q.all, but runs the comands in series
|
||||
// useful for ensuring env state (like which branch is checked out)
|
||||
var allInSeries = function (fn) {
|
||||
return function (args) {
|
||||
var results = [];
|
||||
var def;
|
||||
while (args.length > 0) {
|
||||
(function (arg) {
|
||||
if (def) {
|
||||
def = def.then(function () {
|
||||
return fn(arg);
|
||||
});
|
||||
} else {
|
||||
def = fn(arg);
|
||||
}
|
||||
def = def.then(function (res) {
|
||||
results.push(res);
|
||||
});
|
||||
}(args.pop()));
|
||||
}
|
||||
return def.then(function () {
|
||||
return results;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var compareBranches = function (left, right) {
|
||||
console.log('# These commits are in ' + left.name + ' but not in ' + right.name + '\n');
|
||||
console.log(_(left.log).
|
||||
difference(right.log).
|
||||
map(function (line) {
|
||||
return left.full[left.log.indexOf(line)]; // lol O(n^2)
|
||||
}).
|
||||
value().
|
||||
join('\n'));
|
||||
};
|
||||
|
||||
var checkout = oneArg(exec('git checkout %s'));
|
||||
|
||||
var getCurrentBranch = andThen(noArgs(exec('git rev-parse --abbrev-ref HEAD')), oneLine);
|
||||
var getTags = noArgs(exec('git tag'));
|
||||
var getShaOfTag = oneArg(exec('git rev-list %s | head -n 1'));
|
||||
var getTheLog = oneArg(exec('git log --pretty=oneline %s..HEAD | cat'));
|
||||
|
||||
// remember this so we can restore state
|
||||
var currentBranch;
|
||||
|
||||
getCurrentBranch().
|
||||
then(function (branch) {
|
||||
currentBranch = branch;
|
||||
}).
|
||||
then(getTags).
|
||||
then(function (tags) {
|
||||
return tags.
|
||||
filter(semver.valid).
|
||||
map(semver.clean).
|
||||
sort(semver.rcompare);
|
||||
}).
|
||||
then(function (tags) {
|
||||
var major = tags[0].split('.')[0] + '.x';
|
||||
return tags.
|
||||
filter(function (ver) {
|
||||
return semver.satisfies(ver, major);
|
||||
});
|
||||
}).
|
||||
then(function (tags) {
|
||||
return _(tags).
|
||||
groupBy(function (tag) {
|
||||
return tag.split('.')[1];
|
||||
}).
|
||||
map(function (group) {
|
||||
return _.first(group);
|
||||
}).
|
||||
map(function (tag) {
|
||||
return 'v' + tag;
|
||||
}).
|
||||
value();
|
||||
}).
|
||||
then(function (tags) {
|
||||
return [
|
||||
{ name: 'v1.0.x', tag: tags[0] },
|
||||
{ name: 'master', tag: tags[1] }
|
||||
];
|
||||
}).
|
||||
then(allInSeries(function (branch) {
|
||||
return checkout(branch.name).
|
||||
then(function () {
|
||||
return getTheLog(branch.tag);
|
||||
}).
|
||||
then(function (log) {
|
||||
return log.
|
||||
filter(identity);
|
||||
}).
|
||||
then(function (log) {
|
||||
branch.full = log.map(function (line) {
|
||||
line = line.split(' ');
|
||||
var sha = line.shift();
|
||||
var msg = line.join(' ');
|
||||
return sha + (msg.toLowerCase().indexOf('fix') === -1 ? ' ' : ' * ') + msg;
|
||||
});
|
||||
branch.log = log.map(function (line) {
|
||||
return line.substr(41)
|
||||
});
|
||||
return branch;
|
||||
});
|
||||
})).
|
||||
then(function (pairs) {
|
||||
compareBranches(pairs[0], pairs[1]);
|
||||
console.log('\n');
|
||||
compareBranches(pairs[1], pairs[0]);
|
||||
return pairs;
|
||||
}).
|
||||
then(function () {
|
||||
return checkout(currentBranch);
|
||||
}).
|
||||
catch(function (e) {
|
||||
console.log(e.stack);
|
||||
});
|
||||
|
||||
|
|
@ -5,9 +5,13 @@ describe('Docs Annotations', function() {
|
|||
var body;
|
||||
beforeEach(function() {
|
||||
body = angular.element(document.body);
|
||||
body.html('');
|
||||
body.empty();
|
||||
});
|
||||
|
||||
var normalizeHtml = function(html) {
|
||||
return html.toLowerCase().replace(/\s*$/, '');
|
||||
};
|
||||
|
||||
describe('popover directive', function() {
|
||||
|
||||
var $scope, element;
|
||||
|
|
@ -57,7 +61,7 @@ describe('Docs Annotations', function() {
|
|||
$scope.$apply();
|
||||
element.triggerHandler('click');
|
||||
expect(popoverElement.title()).toBe('#title_text');
|
||||
expect(popoverElement.content()).toBe('<h1>heading</h1>\n');
|
||||
expect(normalizeHtml(popoverElement.content())).toMatch('<h1>heading</h1>');
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
@ -65,10 +69,12 @@ describe('Docs Annotations', function() {
|
|||
|
||||
describe('foldout directive', function() {
|
||||
|
||||
var $scope, parent, element, url, window;
|
||||
// Do not run this suite on Internet Explorer.
|
||||
if (msie < 10) return;
|
||||
|
||||
var $scope, parent, element, url;
|
||||
beforeEach(function() {
|
||||
module(function($provide, $animateProvider) {
|
||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
||||
$animateProvider.register('.foldout', function($timeout) {
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
|
|
@ -112,14 +118,15 @@ describe('Docs Annotations', function() {
|
|||
expect(foldout.html()).toContain('loading');
|
||||
}));
|
||||
|
||||
it('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout) {
|
||||
//TODO(matias): this test is bad. it's not clear what is being tested and what the assertions are.
|
||||
// Additionally, now that promises get auto-flushed there are extra tasks in the deferred queue which screws up
|
||||
// these brittle tests.
|
||||
xit('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout, $sniffer) {
|
||||
$httpBackend.expect('GET', url).respond('hello');
|
||||
|
||||
element.triggerHandler('click');
|
||||
$httpBackend.flush();
|
||||
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(1);
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(1000);
|
||||
|
||||
|
|
@ -128,27 +135,25 @@ describe('Docs Annotations', function() {
|
|||
expect(foldout.text()).toContain('hello');
|
||||
}));
|
||||
|
||||
it('should hide then show when clicked again', inject(function($httpBackend, $timeout) {
|
||||
//TODO(matias): this test is bad. it's not clear what is being tested and what the assertions are.
|
||||
// Additionally, now that promises get auto-flushed there are extra tasks in the deferred queue which screws up
|
||||
// these brittle tests.
|
||||
xit('should hide then show when clicked again', inject(function($httpBackend, $timeout, $sniffer) {
|
||||
$httpBackend.expect('GET', url).respond('hello');
|
||||
|
||||
//enter
|
||||
element.triggerHandler('click');
|
||||
$httpBackend.flush();
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(1);
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(1000);
|
||||
|
||||
//hide
|
||||
element.triggerHandler('click');
|
||||
$timeout.flushNext(1);
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(200);
|
||||
$timeout.flushNext(0);
|
||||
|
||||
//show
|
||||
element.triggerHandler('click');
|
||||
$timeout.flushNext(1);
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(500);
|
||||
$timeout.flushNext(0);
|
||||
|
|
@ -158,17 +163,13 @@ describe('Docs Annotations', function() {
|
|||
|
||||
describe('DocsController fold', function() {
|
||||
|
||||
var window, $scope, ctrl;
|
||||
var $scope, ctrl;
|
||||
beforeEach(function() {
|
||||
module(function($provide, $animateProvider) {
|
||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
||||
});
|
||||
inject(function($rootScope, $controller, $location, $cookies, sections) {
|
||||
$scope = $rootScope.$new();
|
||||
ctrl = $controller('DocsController',{
|
||||
$scope : $scope,
|
||||
$location : $location,
|
||||
$window : window,
|
||||
$cookies : $cookies,
|
||||
sections : sections
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ describe("docsSearch", function() {
|
|||
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
|
||||
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
|
||||
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
|
||||
results[3] = { section: 'cookbook', shortName: 'item cookbook', keywords: 'item, other' };
|
||||
results[4] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
results[3] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
|
||||
$provide.value('NG_PAGES', results);
|
||||
$provide.factory('lunrSearch', function() {
|
||||
|
|
@ -41,19 +40,14 @@ describe("docsSearch", function() {
|
|||
expect(items['api'].length).toBe(2);
|
||||
}));
|
||||
|
||||
it("should place cookbook items in the tutorial", inject(function(docsSearch) {
|
||||
var items = docsSearch('item');
|
||||
expect(items['tutorial'].length).toBe(3);
|
||||
}));
|
||||
|
||||
it("should return all results without a search", inject(function(docsSearch) {
|
||||
var items = docsSearch();
|
||||
expect(items['tutorial'].length).toBe(3);
|
||||
expect(items['tutorial'].length).toBe(2);
|
||||
expect(items['api'].length).toBe(2);
|
||||
}));
|
||||
|
||||
it("should store values with and without a ng prefix", inject(function(docsSearch) {
|
||||
expect(interceptedLunrResults[4].title).toBe('ngRepeat repeat');
|
||||
expect(interceptedLunrResults[3].title).toBe('ngRepeat repeat');
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
|
|||
52
docs/component-spec/errorLinkFilterSpec.js
Normal file
52
docs/component-spec/errorLinkFilterSpec.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
describe("errorLinkFilter", function () {
|
||||
|
||||
var errorLinkFilter;
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
beforeEach(inject(function ($filter) {
|
||||
errorLinkFilter = $filter('errorLink');
|
||||
}));
|
||||
|
||||
it('should not change text that does not contain links', function () {
|
||||
expect(errorLinkFilter('This is a test')).toBe('This is a test');
|
||||
});
|
||||
|
||||
it('should find links in text and linkify them', function () {
|
||||
var output = errorLinkFilter("http://ab/ (http://a/) http://1.2/v:~-123. c");
|
||||
//temporary fix for IE8 sanitization whitespace bug
|
||||
output = output.replace('</a>(','</a> (');
|
||||
expect(output).
|
||||
toBe('<a href="http://ab/">http://ab/</a> ' +
|
||||
'(<a href="http://a/">http://a/</a>) ' +
|
||||
'<a href="http://1.2/v:~-123">http://1.2/v:~-123</a>. c');
|
||||
expect(errorLinkFilter(undefined)).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle mailto', function () {
|
||||
expect(errorLinkFilter("mailto:me@example.com")).
|
||||
toBe('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(errorLinkFilter("me@example.com")).
|
||||
toBe('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(errorLinkFilter("send email to me@example.com, but")).
|
||||
toBe('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
|
||||
});
|
||||
|
||||
it('should handle target', function () {
|
||||
expect(errorLinkFilter("http://example.com", "_blank")).
|
||||
toBe('<a target="_blank" href="http://example.com">http://example.com</a>')
|
||||
expect(errorLinkFilter("http://example.com", "someNamedIFrame")).
|
||||
toBe('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>')
|
||||
});
|
||||
|
||||
it('should not linkify stack trace URLs', function () {
|
||||
expect(errorLinkFilter("http://example.com/angular.min.js:42:1337")).
|
||||
toBe("http://example.com/angular.min.js:42:1337");
|
||||
});
|
||||
|
||||
it('should truncate linked URLs at 60 characters', function () {
|
||||
expect(errorLinkFilter("http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp")).
|
||||
toBe('<a href="http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp">' +
|
||||
'http://errors.angularjs.org/very-long-version-string/$inj...</a>');
|
||||
});
|
||||
});
|
||||
32
docs/component-spec/mocks.js
Normal file
32
docs/component-spec/mocks.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copy/pasted from src/Angular.js, so that we can disable specific tests on IE.
|
||||
var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
|
||||
|
||||
var createMockWindow = function() {
|
||||
var mockWindow = {};
|
||||
var setTimeoutQueue = [];
|
||||
|
||||
mockWindow.location = window.location;
|
||||
mockWindow.document = window.document;
|
||||
mockWindow.getComputedStyle = angular.bind(window, window.getComputedStyle);
|
||||
mockWindow.scrollTo = angular.bind(window, window.scrollTo);
|
||||
mockWindow.navigator = window.navigator;
|
||||
mockWindow.setTimeout = function(fn, delay) {
|
||||
setTimeoutQueue.push({fn: fn, delay: delay});
|
||||
};
|
||||
mockWindow.setTimeout.queue = setTimeoutQueue;
|
||||
mockWindow.setTimeout.expect = function(delay) {
|
||||
if (setTimeoutQueue.length > 0) {
|
||||
return {
|
||||
process: function() {
|
||||
var tick = setTimeoutQueue.shift();
|
||||
expect(tick.delay).toEqual(delay);
|
||||
tick.fn();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
expect('SetTimoutQueue empty. Expecting delay of ').toEqual(delay);
|
||||
}
|
||||
};
|
||||
|
||||
return mockWindow;
|
||||
};
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
describe('DocsApp', function() {
|
||||
|
||||
// Do not run this suite on Internet Explorer.
|
||||
if (msie < 10) return;
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
describe('DocsVersionsCtrl', function() {
|
||||
|
|
@ -7,22 +10,7 @@ describe('DocsApp', function() {
|
|||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('NG_VERSIONS',[
|
||||
'1.0.0',
|
||||
'1.0.1',
|
||||
'1.0.2',
|
||||
'1.0.3',
|
||||
'1.0.4',
|
||||
'1.0.5',
|
||||
'1.0.6',
|
||||
'1.1.0',
|
||||
'1.1.1',
|
||||
'1.1.2',
|
||||
'1.1.3',
|
||||
'1.1.4',
|
||||
'2.1.3'
|
||||
]);
|
||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
||||
$provide.value('$window', window = createMockWindow());
|
||||
});
|
||||
inject(function($controller, $rootScope) {
|
||||
$scope = $rootScope.$new();
|
||||
|
|
@ -34,91 +22,11 @@ describe('DocsApp', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should have the correct version of angular', function() {
|
||||
expect(version).toBe($scope.version);
|
||||
});
|
||||
|
||||
it('should order versions in decending order', function() {
|
||||
expect($scope.versions.length).toBeGreaterThan(0);
|
||||
|
||||
var one = $scope.versions[0].version;
|
||||
var two = $scope.versions[1].version;
|
||||
|
||||
expect(one).toBeGreaterThan(two);
|
||||
});
|
||||
|
||||
it('should list unstable versions at the top of the list', function() {
|
||||
expect($scope.versions[0].stable).toBe(false);
|
||||
});
|
||||
|
||||
it('should list all items below the last stable as stable regardless of version number', function() {
|
||||
var limit = $scope.versions.length - 1,
|
||||
lastUnstableIndex = 0;
|
||||
|
||||
while(lastUnstableIndex <= limit) {
|
||||
if($scope.versions[lastUnstableIndex++].stable) break;
|
||||
}
|
||||
|
||||
for(var i=lastUnstableIndex;i<=limit;i++) {
|
||||
expect($scope.versions[i].stable).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
describe('changing the URL', function() {
|
||||
it('should not support the old < 1.0 docs pages', function() {
|
||||
window.location = 'old';
|
||||
|
||||
$scope.versions.unshift({
|
||||
stable : true,
|
||||
version : '0.9.10'
|
||||
});
|
||||
$scope.jumpToDocsVersion('0.9.10');
|
||||
expect(window.location).toBe('old');
|
||||
|
||||
$scope.versions.unshift({
|
||||
stable : true,
|
||||
version : '0.10.1'
|
||||
});
|
||||
$scope.jumpToDocsVersion('0.10.1');
|
||||
expect(window.location).toBe('old');
|
||||
|
||||
$scope.jumpToDocsVersion('2.1.3');
|
||||
expect(window.location).toBe('http://code.angularjs.org/2.1.3/docs');
|
||||
});
|
||||
|
||||
it('should jump to the older versions of current docs for version >= 1.0.2', function() {
|
||||
$scope.jumpToDocsVersion('1.0.1');
|
||||
expect(window.location).toBe('http://code.angularjs.org/1.0.1/docs-1.0.1');
|
||||
|
||||
$scope.jumpToDocsVersion('1.0.2');
|
||||
expect(window.location).toBe('http://code.angularjs.org/1.0.2/docs');
|
||||
|
||||
$scope.jumpToDocsVersion('1.1.2');
|
||||
expect(window.location).toBe('http://code.angularjs.org/1.1.2/docs');
|
||||
it('should jump to the url provided', function() {
|
||||
$scope.jumpToDocsVersion({ version: '1.0.1', url : 'page123'});
|
||||
expect(window.location).toBe('page123');
|
||||
});
|
||||
|
||||
it('should use the current docs.angularjs.org page when the selected version is the last stable version', function() {
|
||||
$scope.versions = [{
|
||||
stable : true,
|
||||
title : 'test',
|
||||
version : '1.1.1'
|
||||
}];
|
||||
|
||||
$scope.jumpToDocsVersion('1.1.1');
|
||||
expect(window.location).toBe('http://docs.angularjs.org');
|
||||
|
||||
$scope.versions.unshift({
|
||||
stable : true,
|
||||
title : 'test2',
|
||||
version : '1.2.1'
|
||||
});
|
||||
|
||||
$scope.jumpToDocsVersion('1.1.1');
|
||||
expect(window.location).toBe('http://code.angularjs.org/1.1.1/docs');
|
||||
$scope.jumpToDocsVersion('1.2.1');
|
||||
expect(window.location).toBe('http://docs.angularjs.org');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function escape(text) {
|
|||
function setHtmlIe8SafeWay(element, html) {
|
||||
var newElement = angular.element('<pre>' + html + '</pre>');
|
||||
|
||||
element.html('');
|
||||
element.empty();
|
||||
element.append(newElement.contents());
|
||||
return element;
|
||||
}
|
||||
|
|
@ -102,7 +102,12 @@ directive.prettyprint = ['reindentCode', function(reindentCode) {
|
|||
//ensure that angular won't compile {{ curly }} values
|
||||
html = html.replace(/\{\{/g, '<span>{{</span>')
|
||||
.replace(/\}\}/g, '<span>}}</span>');
|
||||
element.html(window.prettyPrintOne(reindentCode(html), undefined, true));
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) {
|
||||
element.html(html);
|
||||
}
|
||||
else {
|
||||
element.html(window.prettyPrintOne(reindentCode(html), undefined, true));
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
|
@ -164,28 +169,30 @@ directive.ngSetHtml = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
|||
directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
compile: function (element, attr) {
|
||||
var script = getEmbeddedTemplate(attr.ngEvalJavascript);
|
||||
|
||||
try {
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(script || '""'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(script);
|
||||
var fileNames = attr.ngEvalJavascript.split(' ');
|
||||
angular.forEach(fileNames, function(fileName) {
|
||||
var script = getEmbeddedTemplate(fileName);
|
||||
try {
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(script || '""'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(script + '//@ sourceURL=' + fileName);
|
||||
}
|
||||
} catch (e) {
|
||||
if (window.console) {
|
||||
window.console.log(script, '\n', e);
|
||||
} else {
|
||||
window.alert(e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (window.console) {
|
||||
window.console.log(script, '\n', e);
|
||||
} else {
|
||||
window.alert(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer', '$animate',
|
||||
function($templateCache, $browser, docsRootScope, $location, $sniffer, $animate) {
|
||||
function($templateCache, $browser, docsRootScope, $location, $sniffer, $animate) {
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attrs) {
|
||||
|
|
@ -194,11 +201,11 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
|||
deregisterEmbedRootScope;
|
||||
|
||||
modules.push(['$provide', function($provide) {
|
||||
$provide.value('$animate', $animate);
|
||||
$provide.value('$templateCache', $templateCache);
|
||||
$provide.value('$anchorScroll', angular.noop);
|
||||
$provide.value('$browser', $browser);
|
||||
$provide.value('$sniffer', $sniffer);
|
||||
$provide.value('$animate', $animate);
|
||||
$provide.provider('$location', function() {
|
||||
this.$get = ['$rootScope', function($rootScope) {
|
||||
docsRootScope.$on('$locationChangeSuccess', function(event, oldUrl, newUrl) {
|
||||
|
|
@ -208,19 +215,14 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
|||
}];
|
||||
this.html5Mode = angular.noop;
|
||||
});
|
||||
$provide.decorator('$timeout', ['$rootScope', '$delegate', function($rootScope, $delegate) {
|
||||
return angular.extend(function(fn, delay) {
|
||||
if (delay && delay > 50) {
|
||||
return setTimeout(function() {
|
||||
$rootScope.$apply(fn);
|
||||
}, delay);
|
||||
} else {
|
||||
return $delegate.apply(this, arguments);
|
||||
}
|
||||
}, $delegate);
|
||||
}]);
|
||||
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
|
||||
// Since we are teleporting the $animate service, which relies on the $$postDigestQueue
|
||||
// we need the embedded scope to use the same $$postDigestQueue as the outer scope
|
||||
embedRootScope.$$postDigestQueue = docsRootScope.$$postDigestQueue;
|
||||
|
||||
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
embedRootScope.$digest();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -193,7 +193,9 @@ directive.table = function() {
|
|||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
element[0].className = 'table table-bordered table-striped code-table';
|
||||
if (!attrs['class']) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,297 @@
|
|||
@name API Reference
|
||||
@description
|
||||
|
||||
Use the API Reference documentation when you need more information about a specific feature. Check out
|
||||
{@link guide/ Developer Guide} for AngularJS concepts. If you are new to AngularJS we recommend the
|
||||
{@link tutorial/ Tutorial}.
|
||||
# AngularJS API Docs
|
||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
|
||||
To prevent accidental name collisions with your code,
|
||||
Angular prefixes names of public objects with `$` and names of private objects with `$$`.
|
||||
Please do not use the `$` or `$$` prefix in your code.
|
||||
</div>
|
||||
|
||||
## Angular Namespace
|
||||
|
||||
|
||||
## {@link ng ng (core module)}
|
||||
This module is provided by default and contains the core components of AngularJS.
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>{@link ng#directive Directives}</td>
|
||||
<td>
|
||||
<p>
|
||||
This is the core collection of directives you would use in your template code to build an AngularJS application.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ng.directive:ngClick ngClick},
|
||||
{@link ng.directive:ngInclude ngInclude},
|
||||
{@link ng.directive:ngRepeat ngRepeat},
|
||||
etc… <br />
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ng#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
This is the core collection of services which are used within the DI of your application.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ng.$compile $compile},
|
||||
{@link ng.$http $http},
|
||||
{@link ngRoute.$routeParams $routeParams},
|
||||
{@link ng.$location $location},
|
||||
etc…
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ng#filter Filters}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ng.filter:filter filter},
|
||||
{@link ng.filter:date date},
|
||||
{@link ng.filter:currency currency},
|
||||
{@link ng.filter:lowercase lowercase},
|
||||
{@link ng.filter:uppercase uppercase},
|
||||
etc...
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ng#function Global APIs}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core global API functions are attached to the angular object. These core functions are useful for low level JavaScript operations within your application.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link angular.copy angular.copy()},
|
||||
{@link angular.equals angular.equals()},
|
||||
{@link angular.element angular.element()},
|
||||
etc...
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## {@link ngRoute ngRoute}
|
||||
|
||||
Use ngRoute to enable URL routing to your application. The ngRoute module supports URL management via both hashbang and HTML5 pushState.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-route.js** file and set **ngRoute** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngRoute#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The following services are used for route management:
|
||||
<ul>
|
||||
<li>{@link ngRoute.$routeParams $routeParams} is used to access the querystring values present in the URL.</li>
|
||||
<li>{@link ngRoute.$route $route} is used to access the details of the route that is currently being accessed.</li>
|
||||
<li>{@link ngRoute.$routeProvider $routeProvider} is used to register routes for the application.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngRoute#directive Directives}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngRoute.directive:ngView ngView} directive will display the template of the current route within the page.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## {@link ngAnimate ngAnimate}
|
||||
|
||||
Use ngAnimate to enable animation features into your application. Various core ng directives will provide
|
||||
animation hooks into your application when ngAnimate is included. Animations are defined by using CSS transitions/animations
|
||||
or JavaScript callbacks.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-animate.js** file and set **ngAnimate** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngAnimate#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
Use {@link ngAnimate.$animate $animate} to trigger animation operations within your directive code.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngAnimate CSS-based animations}
|
||||
</td>
|
||||
<td>
|
||||
Follow ngAnimate’s CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngAnimate JS-based animations}
|
||||
</td>
|
||||
<td>
|
||||
Use {@link angular.Module#methods_animation module.animation()} to register a JavaScript animation. Once registered the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## {@link ngResource ngResource}
|
||||
|
||||
Use the ngResource module when querying and posting data to a REST API.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-resource.js** file and set **ngResource** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngResource#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngResource.$resource $resource} service is used to define RESTful objects which communicate with a REST API.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngCookies ngCookies}
|
||||
|
||||
Use the ngCookies module to handle cookie management within your application.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-cookies.js** file and set **ngCookies** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngCookies#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The following services are used for cookie management:
|
||||
<ul>
|
||||
<li>The {@link ngCookies.$cookies $cookie} service is a convenient wrapper to store simple data within browser cookies.</li>
|
||||
<li>{@link ngCookies.$cookieStore $cookieStore} is used to store more complex data using serialization.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngTouch ngTouch}
|
||||
|
||||
Use ngTouch when developing for mobile browsers/devices.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-touch.js** file and set **ngTouch** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngTouch#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngTouch#directive Directives}
|
||||
</td>
|
||||
<td>
|
||||
Various directives are available in ngTouch to emulate mobile DOM events.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngSanitize ngSanitize}
|
||||
|
||||
Use ngSanitize to securely parse and manipulate HTML data in your application.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-sanitize.js** file and set **ngSanitize** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngSanitize#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngSanitize.$sanitize $sanitize} service is used to clean up dangerous HTML code in a quick and convenient way.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngTouch#filter Filters}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngSanitize.filter:linky linky filter} is used to turn URLs into HTML links within the provided string.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngMock ngMock}
|
||||
|
||||
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
|
||||
|
||||
<div class="alert alert-info">Include the **angular-mocks.js** file into your test runner for this to work.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngMock#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
ngMock will extend the behavior of various core services to become testing aware and manageable in a synchronous manner.
|
||||
<p>
|
||||
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ngMock.$timeout $timeout},
|
||||
{@link ngMock.$interval $interval},
|
||||
{@link ngMock.$log $log},
|
||||
{@link ngMock.$httpBackend $httpBackend},
|
||||
etc...
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngMock#function Global APIs}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
Various helper functions are available to inject and mock modules within unit test code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some examples
|
||||
{@link angular.mock.inject inject()},
|
||||
{@link angular.mock.module module()},
|
||||
{@link angular.mock.dump dump()},
|
||||
etc...
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -2,4 +2,10 @@
|
|||
@name ng
|
||||
@description
|
||||
|
||||
The `ng` is an angular module which contains all of the core angular services.
|
||||
# ng (core module)
|
||||
The ng module is loaded by default when an AngularJS application is started. The module itself
|
||||
contains the essential components for an AngularJS application to function. The table below
|
||||
lists a high level breakdown of each of the services/factories, filters, directives and testing
|
||||
components available within this core module.
|
||||
|
||||
<div doc-module-components="ng"></div>
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Advanced Form
|
||||
@description
|
||||
|
||||
Here we extend the basic form example to include common features such as reverting, dirty state
|
||||
detection, and preventing invalid form submission.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function UserForm($scope) {
|
||||
var master = {
|
||||
name: 'John Smith',
|
||||
address:{
|
||||
line1: '123 Main St.',
|
||||
city:'Anytown',
|
||||
state:'AA',
|
||||
zip:'12345'
|
||||
},
|
||||
contacts:[
|
||||
{type:'phone', value:'1(234) 555-1212'}
|
||||
]
|
||||
};
|
||||
|
||||
$scope.state = /^\w\w$/;
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy(master);
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
master = $scope.form;
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.addContact = function() {
|
||||
$scope.form.contacts.push({type:'', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
var contacts = $scope.form.contacts;
|
||||
for (var i = 0, ii = contacts.length; i < ii; i++) {
|
||||
if (contact === contacts[i]) {
|
||||
contacts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isCancelDisabled = function() {
|
||||
return angular.equals(master, $scope.form);
|
||||
};
|
||||
|
||||
$scope.isSaveDisabled = function() {
|
||||
return $scope.myForm.$invalid || angular.equals(master, $scope.form);
|
||||
};
|
||||
|
||||
$scope.cancel();
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="UserForm">
|
||||
|
||||
<form name="myForm">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" ng-model="form.name" required/> <br/><br/>
|
||||
|
||||
<label>Address:</label> <br/>
|
||||
<input type="text" ng-model="form.address.line1" size="33" required/> <br/>
|
||||
<input type="text" ng-model="form.address.city" size="12" required/>,
|
||||
<input type="text" ng-model="form.address.state" size="2"
|
||||
ng-pattern="state" required/>
|
||||
<input type="text" ng-model="form.address.zip" size="5"
|
||||
ng-pattern="zip" required/><br/><br/>
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
<div ng-repeat="contact in form.contacts">
|
||||
<select ng-model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>form={{form}}</pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enable save button', function() {
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Save)').click();
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
});
|
||||
it('should enable cancel button', function() {
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Cancel)').click();
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
#Things to notice
|
||||
|
||||
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
|
||||
save.
|
||||
* Save button is only enabled if there are no validation errors on the form.
|
||||
* Cancel reverts the form changes back to original state.
|
||||
* Save updates the internal model of the form.
|
||||
* Debug view shows the two models. One presented to the user form and the other being the pristine
|
||||
copy master.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Resources - Buzz
|
||||
@description
|
||||
|
||||
External resources are URLs that provide JSON data, which are then rendered with the help of
|
||||
templates. Angular has a resource factory that can be used to give names to the URLs and then
|
||||
attach behavior to them. For example you can use the
|
||||
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz
|
||||
API}
|
||||
to retrieve Buzz activity and comments.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
BuzzController.$inject = ['$scope', '$resource'];
|
||||
function BuzzController($scope, $resource) {
|
||||
$scope.userId = 'googlebuzz';
|
||||
$scope.Activity = $resource(
|
||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
||||
{alt: 'json', callback: 'JSON_CALLBACK'},
|
||||
{ get: {method: 'JSONP', params: {visibility: '@self'}},
|
||||
replies: {method: 'JSONP', params: {visibility: '@self', comments: '@comments'}}
|
||||
});
|
||||
|
||||
$scope.fetch = function() {
|
||||
$scope.activities = $scope.Activity.get({userId:this.userId});
|
||||
}
|
||||
|
||||
$scope.expandReplies = function(activity) {
|
||||
activity.replies = $scope.Activity.replies({userId: this.userId, activityId: activity.id});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div ng-controller="BuzzController">
|
||||
<input ng-model="userId"/>
|
||||
<button ng-click="fetch()">fetch</button>
|
||||
<hr/>
|
||||
<div class="buzz" ng-repeat="item in activities.data.items">
|
||||
<h1 style="font-size: 15px;">
|
||||
<img ng-src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng-href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href ng-click="expandReplies(item)" style="float: right;">
|
||||
Expand replies: {{item.links.replies[0].count}}
|
||||
</a>
|
||||
</h1>
|
||||
{{item.object.content | html}}
|
||||
<div class="reply" ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
||||
<img ng-src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng-href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
|
||||
{{reply.content | html}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
xit('fetch buzz and expand', function() {
|
||||
element(':button:contains(fetch)').click();
|
||||
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
|
||||
element('.buzz a:contains(Expand replies):first').click();
|
||||
expect(repeater('div.reply').count()).toBeGreaterThan(0);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Deep Linking
|
||||
@description
|
||||
|
||||
Deep linking allows you to encode the state of the application in the URL so that it can be
|
||||
bookmarked and the application can be restored from the URL to the same state.
|
||||
|
||||
While Angular does not force you to deal with bookmarks in any particular way, it has services
|
||||
which make the common case described here very easy to implement.
|
||||
|
||||
# Assumptions
|
||||
|
||||
Your application consists of a single HTML page which bootstraps the application. We will refer
|
||||
to this page as the chrome.
|
||||
Your application is divided into several screens (or views) which the user can visit. For example,
|
||||
the home screen, settings screen, details screen, etc. For each of these screens, we would like to
|
||||
assign a URL so that it can be bookmarked and later restored. Each of these screens will be
|
||||
associated with a controller which define the screen's behavior. The most common case is that the
|
||||
screen will be constructed from an HTML snippet, which we will refer to as the partial. Screens can
|
||||
have multiple partials, but a single partial is the most common construct. This example makes the
|
||||
partial boundary visible using a blue line.
|
||||
|
||||
You can make a routing table which shows which URL maps to which partial view template and which
|
||||
controller.
|
||||
|
||||
# Example
|
||||
|
||||
In this example we have a simple app which consist of two screens:
|
||||
|
||||
* Welcome: url `welcome` Show the user contact information.
|
||||
* Settings: url `settings` Show an edit screen for user contact information.
|
||||
|
||||
<example module="deepLinking" deps="angular-route.js, angular-sanitize.js">
|
||||
<file name="script.js">
|
||||
angular.module('deepLinking', ['ngRoute', 'ngSanitize'])
|
||||
.config(function($routeProvider) {
|
||||
$routeProvider.
|
||||
when("/welcome", {templateUrl:'welcome.html', controller:WelcomeCntl}).
|
||||
when("/settings", {templateUrl:'settings.html', controller:SettingsCntl});
|
||||
});
|
||||
|
||||
AppCntl.$inject = ['$scope', '$route']
|
||||
function AppCntl($scope, $route) {
|
||||
$scope.$route = $route;
|
||||
|
||||
// initialize the model to something useful
|
||||
$scope.person = {
|
||||
name:'anonymous',
|
||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
||||
};
|
||||
}
|
||||
|
||||
function WelcomeCntl($scope) {
|
||||
$scope.greet = function() {
|
||||
alert("Hello " + $scope.person.name);
|
||||
};
|
||||
}
|
||||
|
||||
function SettingsCntl($scope, $location) {
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy($scope.person);
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
angular.copy($scope.form, $scope.person);
|
||||
$location.path('/welcome');
|
||||
};
|
||||
|
||||
$scope.cancel();
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
[ng-view] {
|
||||
border: 1px solid blue;
|
||||
margin: 0;
|
||||
padding:1em;
|
||||
}
|
||||
|
||||
.partial-info {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="AppCntl">
|
||||
<h1>Your App Chrome</h1>
|
||||
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
|
||||
<hr/>
|
||||
<span class="partial-info">
|
||||
Partial: {{$route.current.template}}
|
||||
</span>
|
||||
<div ng-view></div>
|
||||
<small>Your app footer </small>
|
||||
</div>
|
||||
</file>
|
||||
<file name="settings.html">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng:model="form.name" required>
|
||||
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select ng:model="contact.type">
|
||||
<option>url</option>
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
</select>
|
||||
<input type="text" ng:model="contact.url">
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<div>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
</div>
|
||||
|
||||
<button ng:click="cancel()">Cancel</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
</file>
|
||||
<file name="welcome.html">
|
||||
Hello {{person.name}},
|
||||
<div>
|
||||
Your contact information:
|
||||
<div ng:repeat="contact in person.contacts">{{contact.type}}:
|
||||
<span ng-bind-html="contact.url|linky"></span>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should navigate to URL', function() {
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
input('form.name').enter('yourname');
|
||||
element(':button:contains(Save)').click();
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the
|
||||
initialization of the {@link api/ngRoute.$route $route} service with the proper URL
|
||||
routes.
|
||||
* The {@link api/ngRoute.$route $route} service then watches the URL and instantiates the
|
||||
appropriate controller when the URL changes.
|
||||
* The {@link api/ngRoute.directive:ngView ngView} widget loads the
|
||||
view when the URL changes. It also sets the view scope to the newly instantiated controller.
|
||||
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
|
||||
the URL is changed programatically or by the user.
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Form
|
||||
@description
|
||||
|
||||
A web application's main purpose is to present and gather data. For this reason Angular strives
|
||||
to make both of these operations trivial. This example shows off how you can build a simple form to
|
||||
allow a user to enter data.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function FormController($scope) {
|
||||
var user = $scope.user = {
|
||||
name: 'John Smith',
|
||||
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
|
||||
contacts:[{type:'phone', value:'1(234) 555-1212'}]
|
||||
};
|
||||
$scope.state = /^\w\w$/;
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.addContact = function() {
|
||||
user.contacts.push({type:'email', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
for (var i = 0, ii = user.contacts.length; i < ii; i++) {
|
||||
if (contact === user.contacts[i]) {
|
||||
$scope.user.contacts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="FormController" class="example">
|
||||
|
||||
<label>Name:</label><br>
|
||||
<input type="text" ng-model="user.name" required/> <br><br>
|
||||
|
||||
<label>Address:</label><br>
|
||||
<input type="text" ng-model="user.address.line1" size="33" required> <br>
|
||||
<input type="text" ng-model="user.address.city" size="12" required>,
|
||||
<input type="text" ng-model="user.address.state"
|
||||
ng-pattern="state" size="2" required>
|
||||
<input type="text" ng-model="user.address.zip" size="5"
|
||||
ng-pattern="zip" required><br><br>
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
<div ng-repeat="contact in user.contacts">
|
||||
<select ng-model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>user={{user | json}}</pre>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show debug', function() {
|
||||
expect(binding('user')).toMatch(/John Smith/);
|
||||
});
|
||||
it('should add contact', function() {
|
||||
using('.example').element('a:contains(add)').click();
|
||||
using('.example div:last').input('contact.value').enter('you@example.org');
|
||||
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
|
||||
expect(binding('user')).toMatch(/you@example.org/);
|
||||
});
|
||||
|
||||
it('should remove contact', function() {
|
||||
using('.example').element('a:contains(X)').click();
|
||||
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
|
||||
});
|
||||
|
||||
it('should validate zip', function() {
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.zip').enter('abc');
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).toMatch(/ng-invalid/);
|
||||
});
|
||||
|
||||
it('should validate state', function() {
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.state').enter('XXX');
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.toMatch(/ng-invalid/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The user data model is initialized {@link api/ng.directive:ngController controller} and is
|
||||
available in the {@link api/ng.$rootScope.Scope scope} with the initial data.
|
||||
* For debugging purposes we have included a debug view of the model to better understand what
|
||||
is going on.
|
||||
* The {@link api/ng.directive:input input directives} simply refer
|
||||
to the model and are data-bound.
|
||||
* The inputs validate. (Try leaving them blank or entering non digits in the zip field)
|
||||
* In your application you can simply read from or write to the model and the form will be updated.
|
||||
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
|
||||
reflected in the view.
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Hello World
|
||||
@description
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function HelloCntl($scope) {
|
||||
$scope.name = 'World';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="HelloCntl">
|
||||
Your name: <input type="text" ng-model="name"/>
|
||||
<hr/>
|
||||
Hello {{name || "World"}}!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should change the binding when user enters text', function() {
|
||||
expect(binding('name')).toEqual('World');
|
||||
input('name').enter('angular');
|
||||
expect(binding('name')).toEqual('angular');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# Things to notice
|
||||
|
||||
Take a look through the source and note:
|
||||
|
||||
* The script tag that {@link guide/bootstrap bootstraps} the Angular environment.
|
||||
* The text {@link api/ng.directive:input input form control} which is
|
||||
bound to the greeting name text.
|
||||
* There is no need for listener registration and event firing on change events.
|
||||
* The implicit presence of the `name` variable which is in the root {@link api/ng.$rootScope.Scope scope}.
|
||||
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
|
||||
* The concept of {@link guide/dev_guide.templates.databinding data binding}, which reflects any
|
||||
changes to the
|
||||
input field in the greeting text.
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook
|
||||
@description
|
||||
|
||||
Welcome to the Angular cookbook. Here we will show you typical uses of Angular by example.
|
||||
|
||||
|
||||
# Hello World
|
||||
|
||||
{@link helloworld Hello World}: The simplest possible application that demonstrates the
|
||||
classic Hello World!
|
||||
|
||||
|
||||
# Basic Form
|
||||
|
||||
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
|
||||
of web applications. Angular makes forms easy through bidirectional data binding.
|
||||
|
||||
|
||||
# Advanced Form
|
||||
|
||||
{@link advancedform Advanced Form}: Taking the form example to the next level and
|
||||
providing advanced features such as dirty detection, form reverting and submit disabling if
|
||||
validation errors exist.
|
||||
|
||||
|
||||
# Model View Controller
|
||||
|
||||
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
|
||||
to separate the behavior (JavaScript controller) from the presentation (HTML view). This
|
||||
separation aids in maintainability and testability of your project.
|
||||
|
||||
|
||||
# Multi-page App and Deep Linking
|
||||
|
||||
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the
|
||||
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
|
||||
is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL
|
||||
prevent you from emailing links to a particular screen within your application.
|
||||
|
||||
Deep linking tries to solve this by changing the URL anchor without reloading a page, thus
|
||||
allowing you to send links to specific screens in your app.
|
||||
|
||||
|
||||
# Services
|
||||
|
||||
{@link api/ng Services}: Services are long lived objects in your applications that are
|
||||
available across controllers. A collection of useful services are pre-bundled with Angular but you
|
||||
will likely add your own. Services are initialized using dependency injection, which resolves the
|
||||
order of initialization. This safeguards you from the perils of global state (a common way to
|
||||
implement long lived objects).
|
||||
|
||||
|
||||
# External Resources
|
||||
|
||||
{@link buzz Resources}: Web applications must be able to communicate with the external
|
||||
services to get and update data. Resources are the abstractions of external URLs which are
|
||||
specially tailored to Angular data binding.
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: MVC
|
||||
@description
|
||||
|
||||
MVC allows for a clean and testable separation between the behavior (controller) and the view
|
||||
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
|
||||
view. This makes it very easy for the controller and the view to share the model.
|
||||
|
||||
The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
|
||||
This makes it very easy to test the controller in isolation since one can simply instantiate the
|
||||
controller and test without a view, because there is no connection between the controller and the
|
||||
view.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function TicTacToeCntl($scope, $location) {
|
||||
$scope.cellStyle= {
|
||||
'height': '20px',
|
||||
'width': '20px',
|
||||
'border': '1px solid black',
|
||||
'text-align': 'center',
|
||||
'vertical-align': 'middle',
|
||||
'cursor': 'pointer'
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.board = [
|
||||
['', '', ''],
|
||||
['', '', ''],
|
||||
['', '', '']
|
||||
];
|
||||
$scope.nextMove = 'X';
|
||||
$scope.winner = '';
|
||||
setUrl();
|
||||
};
|
||||
|
||||
$scope.dropPiece = function(row, col) {
|
||||
if (!$scope.winner && !$scope.board[row][col]) {
|
||||
$scope.board[row][col] = $scope.nextMove;
|
||||
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
|
||||
setUrl();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
$scope.$watch(function() { return $location.search().board;}, readUrl);
|
||||
|
||||
function setUrl() {
|
||||
var rows = [];
|
||||
angular.forEach($scope.board, function(row) {
|
||||
rows.push(row.join(','));
|
||||
});
|
||||
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
|
||||
}
|
||||
|
||||
function grade() {
|
||||
var b = $scope.board;
|
||||
$scope.winner =
|
||||
row(0) || row(1) || row(2) ||
|
||||
col(0) || col(1) || col(2) ||
|
||||
diagonal(-1) || diagonal(1);
|
||||
function row(row) { return same(b[row][0], b[row][1], b[row][2]);}
|
||||
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
|
||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
||||
}
|
||||
|
||||
function readUrl(value) {
|
||||
if (value) {
|
||||
value = value.split('/');
|
||||
$scope.nextMove = value[1];
|
||||
angular.forEach(value[0].split(';'), function(row, col){
|
||||
$scope.board[col] = row.split(',');
|
||||
});
|
||||
grade();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3>Tic-Tac-Toe</h3>
|
||||
<div ng-controller="TicTacToeCntl">
|
||||
Next Player: {{nextMove}}
|
||||
<div class="winner" ng-show="winner">Player {{winner}} has won!</div>
|
||||
<table class="board">
|
||||
<tr ng-repeat="row in board track by $index" style="height:15px;">
|
||||
<td ng-repeat="cell in row track by $index" ng-style="cellStyle"
|
||||
ng-click="dropPiece($parent.$index, $index)">{{cell}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng-click="reset()">reset board</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should play a game', function() {
|
||||
piece(1, 1);
|
||||
expect(binding('nextMove')).toEqual('O');
|
||||
piece(3, 1);
|
||||
expect(binding('nextMove')).toEqual('X');
|
||||
piece(1, 2);
|
||||
piece(3, 2);
|
||||
piece(1, 3);
|
||||
expect(element('.winner').text()).toEqual('Player X has won!');
|
||||
});
|
||||
|
||||
function piece(row, col) {
|
||||
element('.board tr:nth-child('+row+') td:nth-child('+col+')').click();
|
||||
}
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The controller is defined in JavaScript and has no reference to the rendering logic.
|
||||
* The controller is instantiated by Angular and injected into the view.
|
||||
* The controller can be instantiated in isolation (without a view) and the code will still execute.
|
||||
This makes it very testable.
|
||||
* The HTML view is a projection of the model. In the above example, the model is stored in the
|
||||
board variable.
|
||||
* All of the controller's properties (such as board and nextMove) are available to the view.
|
||||
* Changing the model changes the view.
|
||||
* The view can call any controller function.
|
||||
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
|
||||
hash so the browser's back button will undo game steps. See deep-linking. This example calls {@link
|
||||
api/ng.$rootScope.Scope#$watch $watch()} to set up a listener that invokes `readUrl()` when needed.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
@fullName Missing Required Controller
|
||||
@description
|
||||
|
||||
This error occurs when {@link api/ng.$compile template compiler} tries process the directive that specifies the `require` option in a {@link guide/directive#directivedefinitionobject directive definition},
|
||||
This error occurs when {@link api/ng.$compile HTML compiler} tries to process a directive that specifies the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object `require` option} in a {@link api/ng.$compile#description_comprehensive-directive-api directive definition},
|
||||
but the required directive controller is not present on the current DOM element (or its ancestor element, if `^` was specified).
|
||||
|
||||
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@ myModule.directive('directiveName', function factory() {
|
|||
});
|
||||
```
|
||||
|
||||
Please refer to the {@link guide/directive#directivedefinitionobject directive definition docs} to learn more about the api.
|
||||
Please refer to the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
|
||||
`scope` option} of the directive definition documentation to learn more about the API.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
@fullName Multiple Directive Resource Contention
|
||||
@description
|
||||
|
||||
This error occurs when multiple directives are applied to the same DOM element, and processing them would result in an collisions or unsupported configuration.
|
||||
This error occurs when multiple directives are applied to the same DOM element, and
|
||||
processing them would result in a collision or an unsupported configuration.
|
||||
|
||||
|
||||
To resolve this issue remove one of the directives which is causing the collision.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ For these reasons binding to event handler attributes (all attributes that start
|
|||
|
||||
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
|
||||
```
|
||||
<input ng-mode="username">
|
||||
<input ng-model="username">
|
||||
<div onclick="{{username}}">click me</div>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
@fullName Non-Assignable Expression
|
||||
@description
|
||||
|
||||
This error occurs when a directive defines an isolate scope property that support two-way data-binding (using the `=` mode in the {@link guide/directive#directivedefinitionobject directive definition}) but the directive is used with an expression that is not-assignable.
|
||||
This error occurs when a directive defines an isolate scope property
|
||||
(using the `=` mode in the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
|
||||
`scope` option} of a directive definition) but the directive is used with an expression that is not-assignable.
|
||||
|
||||
In order for the two-way data-binding to work, it must be possible to write new values back into the path defined with the expression.
|
||||
|
||||
|
|
@ -23,8 +25,11 @@ myModule.directive('myDirective', function factory() {
|
|||
|
||||
Following are invalid uses of this directive:
|
||||
```
|
||||
<my-directive bind="1+2"> <!-- ERROR because `1+2=localValue` is an invalid statement -->
|
||||
<my-directive bind="myFn()"> <!-- ERROR because `myFn()=localValue` is an invalid statement -->
|
||||
<!-- ERROR because `1+2=localValue` is an invalid statement -->
|
||||
<my-directive bind="1+2">
|
||||
|
||||
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
|
||||
<my-directive bind="myFn()">
|
||||
```
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@
|
|||
@fullName Invalid Template Root
|
||||
@description
|
||||
|
||||
When a directive is declared with `template` (or `templateUrl`) and `replace` mode on, the template must have exactly one root element.
|
||||
Otherwise the replacement operation would result in a single element being replaced with multiple elements or nodes, which is unsupported and not commonly needed in practice.
|
||||
When a directive is declared with `template` (or `templateUrl`) and `replace` mode on, the template
|
||||
must have exactly one root element. That is, the text of the template property or the content
|
||||
referenced by the templateUrl must be contained within a single html element.
|
||||
For example, `<p>blah <em>blah</em> blah</p>` instead of simply `blah <em>blah</em> blah`.
|
||||
Otherwise, the replacement operation would result in a single element (the directive) being replaced
|
||||
with multiple elements or nodes, which is unsupported and not commonly needed in practice.
|
||||
|
||||
|
||||
For example a directive with definition:
|
||||
|
||||
```
|
||||
myModule.directive('myDirective', function factory() {
|
||||
return {
|
||||
|
|
@ -19,12 +24,16 @@ myModule.directive('myDirective', function factory() {
|
|||
});
|
||||
```
|
||||
|
||||
And a template provided at URL `someUrl`. The template must be an html fragment that has only a single root element, like the `div` element in this template:
|
||||
And a template provided at URL `someUrl`. The template must be an html fragment that has only a
|
||||
single root element, like the `div` element in this template:
|
||||
|
||||
```
|
||||
<div><b>Hello</b> World!</div>
|
||||
```
|
||||
|
||||
An an invalid template to be used with this directive is one that defines multiple root nodes or elements. For example:
|
||||
An an invalid template to be used with this directive is one that defines multiple root nodes or
|
||||
elements. For example:
|
||||
|
||||
```
|
||||
<b>Hello</b> World!
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@
|
|||
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
|
||||
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
|
||||
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
|
||||
supported browser.
|
||||
supported browser.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,3 +5,7 @@
|
|||
|
||||
This error occurs when a module fails to load due to some exception. The error
|
||||
message above should provide additional context.
|
||||
|
||||
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
|
||||
If you are getting this error after upgrading to `1.2.x`, be sure that you've
|
||||
installed {@link api/ngRoute `ngRoute`}.
|
||||
|
|
|
|||
|
|
@ -22,5 +22,5 @@ angular.module("myApp", [])
|
|||
.provider('good', GoodProvider);
|
||||
```
|
||||
|
||||
For more information, refer to the {@link api/AUTO.$provide#provider
|
||||
For more information, refer to the {@link api/AUTO.$provide#methods_provider
|
||||
$provide.provider} api doc.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
@fullName Unsupported Selector Lookup
|
||||
@description
|
||||
|
||||
In order to keep Angular small, Angular implements only a subset of the selectors in {@link api/angular.element#angularsjqlite jqLite}.
|
||||
In order to keep Angular small, Angular implements only a subset of the selectors in {@link api/angular.element#description_angulars-jqlite jqLite}.
|
||||
This error occurs when a jqLite instance is invoked with a selector other than this subset.
|
||||
|
||||
In order to resolve this error, rewrite your code to only use tag name selectors and manually traverse the DOM using the APIs provided by jqLite.
|
||||
|
|
|
|||
8
docs/content/error/ng/badname.ngdoc
Normal file
8
docs/content/error/ng/badname.ngdoc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@ngdoc error
|
||||
@name ng:badname
|
||||
@fullName Bad `hasOwnProperty` Name
|
||||
@description
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` in a context where it is not allow.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
|
|
@ -4,4 +4,4 @@
|
|||
@description
|
||||
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
|
||||
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:select input} directive docs.
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ For example the issue can be triggered by this *invalid* code:
|
|||
<div ng-repeat="value in [4, 4]"></div>
|
||||
```
|
||||
|
||||
To resolve this error either ensure that the items in the collection have unique identity of use the `track by` syntax to specify how to track the association between models and DOM.
|
||||
To resolve this error either ensure that the items in the collection have unique identity or use the `track by` syntax to specify how to track the association between models and DOM.
|
||||
|
||||
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
||||
|
||||
|
|
|
|||
12
docs/content/error/ngTransclude/orphan.ngdoc
Normal file
12
docs/content/error/ngTransclude/orphan.ngdoc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
@ngdoc error
|
||||
@name ngTransclude:orphan
|
||||
@fullName Orphan ngTransclude Directive
|
||||
@description
|
||||
|
||||
Occurs when an `ngTransclude` occurs without a transcluded ancestor element.
|
||||
|
||||
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTransclude` in the directive's template.
|
||||
|
||||
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
|
||||
|
||||
Consult the API documentation for {@link guide/directive writing directives} to learn more.
|
||||
16
docs/content/error/parse/isecdom.ngdoc
Normal file
16
docs/content/error/parse/isecdom.ngdoc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
@ngdoc error
|
||||
@name $parse:isecdom
|
||||
@fullName Referencing a DOM node in Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access a DOM node.
|
||||
|
||||
AngularJS restricts access to DOM nodes from within expressions since it's a known way to
|
||||
execute arbitrary Javascript code.
|
||||
|
||||
This check is only performed on object index and function calls in Angular expressions. These are
|
||||
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
|
||||
perform this check - it's up to the developer to not expose such sensitive and powerful objects
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid access to DOM nodes.
|
||||
16
docs/content/error/parse/isecwindow.ngdoc
Normal file
16
docs/content/error/parse/isecwindow.ngdoc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
@ngdoc error
|
||||
@name $parse:isecwindow
|
||||
@fullName Referencing Window object in Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access a Window object.
|
||||
|
||||
AngularJS restricts access to the Window object from within expressions since it's a known way to
|
||||
execute arbitrary Javascript code.
|
||||
|
||||
This check is only performed on object index and function calls in Angular expressions. These are
|
||||
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
|
||||
perform this check - it's up to the developer to not expose such sensitive and powerful objects
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid Window access.
|
||||
27
docs/content/error/resource/badmember.ngdoc
Normal file
27
docs/content/error/resource/badmember.ngdoc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@ngdoc error
|
||||
@name $resource:badmember
|
||||
@fullName Syntax error in param value using @member lookup
|
||||
@description
|
||||
|
||||
Occurs when there is a syntax error when attempting to extract a param
|
||||
value from the data object.
|
||||
|
||||
Here's an example of valid syntax for `params` or `paramsDefault`:
|
||||
|
||||
````javascript
|
||||
{
|
||||
bar: '@foo.bar'
|
||||
}
|
||||
````
|
||||
|
||||
The part following the `@`, `foo.bar` in this case, should be a simple
|
||||
dotted member lookup using only ASCII identifiers. This error occurs
|
||||
when there is an error in that expression. The following are all syntax
|
||||
errors
|
||||
|
||||
| Value | Error |
|
||||
|---------|----------------|
|
||||
| `@` | Empty expression following `@`. |
|
||||
| `@1.a` | `1` is an invalid javascript identifier. |
|
||||
| `@.a` | Leading `.` is invalid. |
|
||||
| `@a[1]` | Only dotted lookups are supported (no index operator) |
|
||||
8
docs/content/error/resource/badname.ngdoc
Normal file
8
docs/content/error/resource/badname.ngdoc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@ngdoc error
|
||||
@name $resource:badname
|
||||
@fullName Cannot use hasOwnProperty as a parameter name
|
||||
@description
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` as a name of a parameter.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
@fullName Infinite $digest Loop
|
||||
@description
|
||||
|
||||
This error occurs when the application's model becomes unstable and each `$digest` cycle triggers a state change and subsequent `$digest` cycle. Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
|
||||
This error occurs when the application's model becomes unstable and each `$digest` cycle triggers a state change and subsequent `$digest` cycle.
|
||||
Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
|
||||
|
||||
For example, the situation can occur by setting up a watch on a path and subsequently updating the same path when the value changes.
|
||||
|
||||
|
|
@ -11,4 +12,6 @@ For example, the situation can occur by setting up a watch on a path and subsequ
|
|||
$scope.$watch('foo', function() {
|
||||
$scope.foo = $scope.foo + 1;
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
The maximum number of allowed iterations of the `$digest` cycle is controlled via TTL setting which can be configured via {@link api/ng.$rootScopeProvider $rootScopeProvider}.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
@fullName Invalid / Unknown SCE context
|
||||
@description
|
||||
|
||||
The context enum passed to {@link api/ng.$sce#trustAs $sce.trustAs} was not recognized.
|
||||
The context enum passed to {@link api/ng.$sce#methods_trustAs $sce.trustAs} was not recognized.
|
||||
|
||||
Please consult the list of {@link api/ng.$sce#contexts supported Strict Contextual Escaping (SCE) contexts}.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
@fullName IE8 in quirks mode is unsupported
|
||||
@description
|
||||
|
||||
This error occurs when you are using AngularJS with {@link api/ng.$sce#strictcontextualescaping Strict Contextual Escaping (SCE)} mode enabled (the default) on IE8 or lower in quirks mode.
|
||||
This error occurs when you are using AngularJS with {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode enabled (the default) on IE8 or lower in quirks mode.
|
||||
|
||||
In this mode, IE8 allows one to execute arbitrary javascript by the use of the `expression()` syntax and is not supported.
|
||||
Refer {@link http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx MSDN Blogs > IEBlog > Ending Expressions} to learn more about them.
|
||||
|
|
|
|||
9
docs/content/error/sce/imatcher.ngdoc
Normal file
9
docs/content/error/sce/imatcher.ngdoc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@ngdoc error
|
||||
@name $sce:imatcher
|
||||
@fullName Invalid matcher (only string patterns and RegExp instances are supported)
|
||||
@description
|
||||
|
||||
Please see {@link api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist
|
||||
$sceDelegateProvider.resourceUrlWhitelist} and {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} for the
|
||||
list of acceptable items.
|
||||
|
|
@ -3,21 +3,21 @@
|
|||
@fullName Processing of a Resource from Untrusted Source Blocked
|
||||
@description
|
||||
|
||||
AngularJS' {@link api/ng.$sce#strictcontextualescaping Strict Contextual Escaping (SCE)} mode (enabled by default) has blocked loading a resource from an insecure URL.
|
||||
AngularJS' {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode (enabled by default) has blocked loading a resource from an insecure URL.
|
||||
|
||||
Typically, this would occur if you're attempting to load an Angular template from an untrusted source.
|
||||
It's also possible that a custom directive threw this error for a similar reason.
|
||||
|
||||
Angular only loads templates from trusted URLs (by calling {@link api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl} on the template URL).
|
||||
Angular only loads templates from trusted URLs (by calling {@link api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl} on the template URL).
|
||||
|
||||
By default, only URLs that belong to the same origin are trusted. These are urls with the same domain and protocol as the application document.
|
||||
|
||||
The {@link api/ng.directive:ngInclude ngInclude} directive and {@link guide/directive directives} that specify a `templateUrl` require a trusted resource URL.
|
||||
|
||||
To load templates from other domains and/or protocols, either adjust the {@link
|
||||
api/ng.$sceDelegateProvider#resourceUrlWhitelist whitelist}/ {@link
|
||||
api/ng.$sceDelegateProvider#resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link
|
||||
api/ng.$sce#trustAsResourceUrl $sce.trustAsResourceUrl}.
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist}/ {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link
|
||||
api/ng.$sce#methods_trustAsResourceUrl $sce.trustAsResourceUrl}.
|
||||
|
||||
**Note**: The browser's {@link
|
||||
https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest Same Origin
|
||||
|
|
@ -3,6 +3,6 @@
|
|||
@fullName String Value is Required for SCE Trust Call
|
||||
@description
|
||||
|
||||
{@link api/ng.$sce#trustAs $sce.trustAs} requires a string value.
|
||||
{@link api/ng.$sce#methods_trustAs $sce.trustAs} requires a string value.
|
||||
|
||||
Read more about {@link api/ng.$sce#strictcontextualescaping Strict Contextual Escaping (SCE)} in AngularJS.
|
||||
Read more about {@link api/ng.$sce Strict Contextual Escaping (SCE)} in AngularJS.
|
||||
|
|
|
|||
9
docs/content/error/sce/iwcard.ngdoc
Normal file
9
docs/content/error/sce/iwcard.ngdoc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@ngdoc error
|
||||
@name $sce:iwcard
|
||||
@fullName The sequence *** is not a valid pattern wildcard
|
||||
@description
|
||||
|
||||
The strings in {@link api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist
|
||||
$sceDelegateProvider.resourceUrlWhitelist} and {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} may not
|
||||
contain the undefined sequence `***`. Only `*` and `**` wildcard patterns are defined.
|
||||
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
The value provided for use in a specific context was not found to be safe/trusted for use.
|
||||
|
||||
Angular's {@link api/ng.$sce#strictcontextualescaping Strict Contextual Escaping (SCE)} mode
|
||||
Angular's {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode
|
||||
(enabled by default), requires bindings in certain
|
||||
contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an
|
||||
Angular template from a URL requires that the URL is one considered safe for loading resources.)
|
||||
|
||||
This helps prevent XSS and other security issues. Read more at {@link
|
||||
api/ng.$sce#strictcontextualescaping Strict Contextual Escaping (SCE)}
|
||||
api/ng.$sce Strict Contextual Escaping (SCE)}
|
||||
|
||||
|
|
|
|||
289
docs/content/guide/animations.ngdoc
Normal file
289
docs/content/guide/animations.ngdoc
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Animations
|
||||
@description
|
||||
|
||||
|
||||
# Animations
|
||||
|
||||
AngularJS 1.2 provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
|
||||
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
|
||||
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
|
||||
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
|
||||
or with JavaScript code when it's defined as a factory.
|
||||
|
||||
Animations are not available unless you include the {@link api/ngAnimate `ngAnimate` module} as a dependency within your application.
|
||||
|
||||
Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-init="checked=true">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
|
||||
</label>
|
||||
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
|
||||
Visible...
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.sample-show-hide {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
transition:all linear 0.5s;
|
||||
display:block!important;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.sample-show-hide.ng-hide-remove {
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add,
|
||||
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
opacity:1;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
## Installation
|
||||
|
||||
See the {@link api/ngAnimate API docs for `ngAnimate`} for instructions on installing the module.
|
||||
|
||||
You may also want to setup a separate CSS file for defining CSS-based animations.
|
||||
|
||||
## How they work
|
||||
|
||||
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within
|
||||
your website, you can apply animations to it. Lets say for example that we have an HTML template with a repeater in it like so:
|
||||
|
||||
<pre>
|
||||
<div ng-repeat="item in items" class="repeated-item">
|
||||
{{ item.id }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
As you can see, the `.repeated-item` class is present on the element that will be repeated and this class will be
|
||||
used as a reference within our application's CSS and/or JavaScript animation code to tell AngularJS to perform an animation.
|
||||
|
||||
As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add
|
||||
a `ng-enter` class name to the element that is being added. When removed it will apply a `ng-leave` class name and when moved around
|
||||
it will apply a `ng-move` class name.
|
||||
|
||||
Taking a look at the following CSS code, we can see some transition and keyframe animation code set for each of those events that
|
||||
occur when ngRepeat triggers them:
|
||||
|
||||
<pre>
|
||||
/*
|
||||
We're using CSS transitions for when
|
||||
the enter and move events are triggered
|
||||
for the element that has the .repeated-item
|
||||
class
|
||||
*/
|
||||
.repeated-item.ng-enter, .repeated-item.ng-move {
|
||||
-webkit-transition:0.5s linear all;
|
||||
-moz-transition:0.5s linear all;
|
||||
-o-transition:0.5s linear all;
|
||||
transition:0.5s linear all;
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
/*
|
||||
The ng-enter-active and ng-move-active
|
||||
are where the transition destination properties
|
||||
are set so that the animation knows what to
|
||||
animate.
|
||||
*/
|
||||
.repeated-item.ng-enter.ng-enter-active,
|
||||
.repeated-item.ng-move.ng-move-active {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
/*
|
||||
We're using CSS keyframe animations for when
|
||||
the leave event is triggered for the element
|
||||
that has the .repeated-item class
|
||||
*/
|
||||
.repeated-item.ng-leave {
|
||||
-webkit-animation:0.5s my_animation;
|
||||
-moz-animation:0.5s my_animation;
|
||||
-o-animation:0.5s my_animation;
|
||||
animation:0.5s my_animation;
|
||||
}
|
||||
|
||||
@keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
|
||||
/*
|
||||
Unfortunately each browser vendor requires
|
||||
its own definition of keyframe animation code...
|
||||
*/
|
||||
@-webkit-keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
|
||||
@-moz-keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
|
||||
@-o-keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
</pre>
|
||||
|
||||
The same approach to animation can be used using JavaScript code (**jQuery is used within to perform animations**):
|
||||
|
||||
<pre>
|
||||
myModule.animation('.repeated-item', function() {
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
element.css('opacity',0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
leave : function(element, done) {
|
||||
element.css('opacity', 1);
|
||||
jQuery(element).animate({
|
||||
opacity: 0
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
move : function(element, done) {
|
||||
element.css('opacity', 0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// you can also capture these animation events
|
||||
addClass : function(element, className, done) {},
|
||||
removeClass : function(element, className, done) {}
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
With these generated CSS class names present on the element at the time, AngularJS automatically
|
||||
figures out whether to perform a CSS and/or JavaScript animation. If both CSS and JavaScript animation
|
||||
code is present, and match the CSS class name on the element, then AngularJS will run both animations at the same time.
|
||||
|
||||
## Class and ngClass animation hooks
|
||||
|
||||
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks.
|
||||
This means that if a CSS class is added to or removed from an element then an animation can be executed in between
|
||||
before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be
|
||||
able to capture class changes if an **expression** or the **ng-class** directive is used on the element.)
|
||||
|
||||
The example below shows how to perform animations during class changes:
|
||||
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<p>
|
||||
<input type="button" value="set" ng-click="myCssVar='css-class'">
|
||||
<input type="button" value="clear" ng-click="myCssVar=''">
|
||||
<br>
|
||||
<span ng-class="myCssVar">CSS-Animated Text</span>
|
||||
</p>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.css-class-add, .css-class-remove {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
}
|
||||
|
||||
.css-class,
|
||||
.css-class-add.css-class-add-active {
|
||||
color: red;
|
||||
font-size:3em;
|
||||
}
|
||||
|
||||
.css-class-remove.css-class-remove-active {
|
||||
font-size:1.0em;
|
||||
color:black;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Although the CSS is a little different then what we saw before, the idea is the same.
|
||||
|
||||
## Which directives support animations?
|
||||
|
||||
A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during its life cycle.
|
||||
The table below explains in detail which animation events are triggered
|
||||
|
||||
| Directive | Supported Animations |
|
||||
|-------------------------------------------------------------------------------------|------------------------------------------|
|
||||
| {@link api/ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave, and move |
|
||||
| {@link api/ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
|
||||
| {@link api/ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
|
||||
| {@link api/ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
|
||||
| {@link api/ng.directive:ngIf#usage_animations ngIf} | enter and leave |
|
||||
| {@link api/ng.directive:ngClass#usage_animations ngClass or {{class}}} | add and remove |
|
||||
| {@link api/ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||
|
||||
For a full breakdown of the steps involved during each animation event, refer to the {@link api/ngAnimate.$animate API docs}.
|
||||
|
||||
## How do I use animations in my own directives?
|
||||
|
||||
Animations within custom directives can also be established by injecting `$animate` directly into your directive and
|
||||
making calls to it when needed.
|
||||
|
||||
<pre>
|
||||
myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
return function(element, scope, attrs) {
|
||||
element.bind('click', function() {
|
||||
if(element.hasClass('clicked')) {
|
||||
$animate.removeClass(element, 'clicked');
|
||||
} else {
|
||||
$animate.addClass(element, 'clicked');
|
||||
}
|
||||
});
|
||||
};
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link api/ngAnimate.$animate API documentation}.
|
||||
|
||||
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.
|
||||
|
|
@ -51,6 +51,8 @@ initialization.
|
|||
|
||||
## Automatic Initialization
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png">
|
||||
|
||||
Angular initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is
|
||||
evaluated if at that time `document.readyState` is set to `'complete'`. At this point Angular looks
|
||||
for the {@link api/ng.directive:ngApp `ng-app`} directive which designates your application root.
|
||||
|
|
@ -92,7 +94,8 @@ Here is an example of manually initializing Angular:
|
|||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.bootstrap(document, ['optionalModuleName']);
|
||||
angular.module('myApp', []);
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
@ -100,9 +103,8 @@ Here is an example of manually initializing Angular:
|
|||
</pre>
|
||||
|
||||
Note that we have provided the name of our application module to be loaded into the injector as the second
|
||||
parameter of the {@link api/angular.bootstrap} function. This example is equivalent to using the
|
||||
{@link api/ng.directive:ngApp ng-app} directive, with `ng-app="optionalModuleName"`, as in the automatic
|
||||
initialization example above.
|
||||
parameter of the {@link api/angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
|
||||
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
|
||||
|
||||
This is the sequence that your code should follow:
|
||||
|
||||
|
|
@ -111,3 +113,18 @@ This is the sequence that your code should follow:
|
|||
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the element into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
## Deferred Bootstrap
|
||||
|
||||
This feature enables tools like Batarang and test runners to
|
||||
hook into angular's bootstrap process and sneak in more modules
|
||||
into the DI registry which can replace or augment DI services for
|
||||
the purpose of instrumentation or mocking out heavy dependencies.
|
||||
|
||||
If `window.name` contains prefix `NG_DEFER_BOOTSTRAP!` when
|
||||
{@link api/angular.bootstrap} is called, the bootstrap process will be paused
|
||||
until `angular.resumeBootstrap()` is called.
|
||||
|
||||
`angular.resumeBootstrap()` takes an optional array of modules that
|
||||
should be added to the original list of modules that the app was
|
||||
about to be bootstrapped with.
|
||||
|
|
|
|||
|
|
@ -2,12 +2,21 @@
|
|||
@name Developer Guide: HTML Compiler
|
||||
@description
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics.
|
||||
|
||||
If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
|
||||
If you just want to create custom directives, we recommend the {@link guide/directive directives guide}.
|
||||
If you want a deeper look into Angular's compilation process, you're in the right place.
|
||||
</div>
|
||||
|
||||
|
||||
# Overview
|
||||
|
||||
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
and even create new HTML elements or attributes with custom behavior. Angular calls these behavior
|
||||
extensions {@link api/ng.$compileProvider#directive directives}.
|
||||
extensions {@link api/ng.$compileProvider#methods_directive directives}.
|
||||
|
||||
HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
|
||||
For example if something needs to be centered, there is no need to provide instructions to the
|
||||
|
|
@ -27,9 +36,9 @@ All of this compilation takes place in the web browser; no server side or pre-co
|
|||
involved.
|
||||
|
||||
|
||||
# Compiler
|
||||
## Compiler
|
||||
|
||||
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
|
||||
Compiler is an Angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens in two phases.
|
||||
|
||||
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
|
||||
|
|
@ -44,7 +53,7 @@ for each item in a collection. Having a compile and link phase improves performa
|
|||
cloned template only needs to be compiled once, and then linked once for each clone instance.
|
||||
|
||||
|
||||
# Directive
|
||||
## Directive
|
||||
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered
|
||||
during the compilation process. The directives can be placed in element names, attributes, class
|
||||
|
|
@ -59,7 +68,7 @@ api/ng.directive:ngBind `ng-bind`} directive.
|
|||
</pre>
|
||||
|
||||
A directive is just a function which executes when the compiler encounters it in the DOM. See {@link
|
||||
api/ng.$compileProvider#directive directive API} for in-depth documentation on how
|
||||
api/ng.$compileProvider#methods_directive directive API} for in-depth documentation on how
|
||||
to write directives.
|
||||
|
||||
Here is a directive which makes any element draggable. Notice the `draggable` attribute on the
|
||||
|
|
@ -108,35 +117,265 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
|||
</example>
|
||||
|
||||
|
||||
The presence of the `draggable` attribute on any element gives the element new behavior. The beauty of
|
||||
this approach is that we have taught the browser a new trick. We have extended the vocabulary of
|
||||
what the browser understands in a way which is natural to anyone who is familiar with HTML
|
||||
principles.
|
||||
The presence of the `draggable` attribute on any element gives the element new behavior.
|
||||
We extended the vocabulary of the browser in a way which is natural to anyone who is familiar with the principles of HTML.
|
||||
|
||||
|
||||
# Understanding View
|
||||
## Understanding View
|
||||
|
||||
There are many templating systems out there. Most of them consume a static string template and
|
||||
Most other templating systems consume a static string template and
|
||||
combine it with data, resulting in a new string. The resulting text is then `innerHTML`ed into
|
||||
an element.
|
||||
|
||||
<img src="img/One_Way_Data_Binding.png">
|
||||
|
||||
This means that any changes to the data need to be re-merged with the template and then
|
||||
`innerHTML`ed into the DOM. Some of the issues with this approach are: reading user input and merging it with data,
|
||||
clobbering user input by overwriting it, managing the whole update process, and lack of behavior
|
||||
expressiveness.
|
||||
`innerHTML`ed into the DOM. Some of the issues with this approach are:
|
||||
|
||||
Angular is different. The Angular compiler consumes the DOM with directives, not string templates.
|
||||
1. reading user input and merging it with data
|
||||
2. clobbering user input by overwriting it
|
||||
3. managing the whole update process
|
||||
4. lack of behavior expressiveness
|
||||
|
||||
Angular is different. The Angular compiler consumes the DOM, not string templates.
|
||||
The result is a linking function, which when combined with a scope model results in a live view. The
|
||||
view and scope model bindings are transparent. No action from the developer is needed to update
|
||||
the view. And because no `innerHTML` is used there are no issues of clobbering user input.
|
||||
view and scope model bindings are transparent. The developer does not need to make any special calls to update
|
||||
the view. And because `innerHTML` is not used, you won't accidentally clobber user input.
|
||||
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
|
||||
well.
|
||||
|
||||
<img src="img/Two_Way_Data_Binding.png">
|
||||
|
||||
The Angular approach produces a stable DOM. This means that the DOM element instance bound to a model
|
||||
The Angular approach produces a stable DOM. The DOM element instance bound to a model
|
||||
item instance does not change for the lifetime of the binding. This means that the code can get
|
||||
hold of the elements and register event handlers and know that the reference will not be destroyed
|
||||
by template data merge.
|
||||
|
||||
|
||||
|
||||
## How directives are compiled
|
||||
|
||||
It's important to note that Angular operates on DOM nodes rather than strings. Usually, you don't
|
||||
notice this restriction because when a page loads, the web browser parses HTML into the DOM automatically.
|
||||
|
||||
However it's important to keep this in mind when calling `$compile` yourself, because passing it a string
|
||||
will fail. Instead, use `angular.element` to convert a string to DOM before passing elements into
|
||||
Angular's `$compile` service.
|
||||
|
||||
HTML compilation happens in three phases:
|
||||
|
||||
1. {@link api/ng.$compile `$compile`} traverses the DOM and matches directives.
|
||||
|
||||
If the compiler finds that an element matches a directive, then the directive is added to the list of
|
||||
directives that match the DOM element. A single element may match multiple directives.
|
||||
|
||||
2. Once all directives matching a DOM element have been identified, the compiler sorts the directives
|
||||
by their `priority`.
|
||||
|
||||
Each directive's `compile` functions are executed. Each `compile` function has a chance to
|
||||
modify the DOM. Each `compile` function returns a `link` function. These functions are composed into
|
||||
a "combined" link function, which invokes each directive's returned `link` function.
|
||||
|
||||
3. `$compile` links the template with the scope by calling the combined linking function from the previous step.
|
||||
This in turn will call the linking function of the individual directives, registering listeners on the elements
|
||||
and setting up {@link api/ng.$rootScope.Scope#methods_$watch `$watch`s} with the {@link api/ng.$rootScope.Scope `scope`}
|
||||
as each directive is configured to do.
|
||||
|
||||
The result of this is a live binding between the scope and the DOM. So at this point, a change in
|
||||
a model on the compiled scope will be reflected in the DOM.
|
||||
|
||||
Below is the corresponding code using the `$compile` service.
|
||||
This should help give you an idea of what Angular does internally.
|
||||
|
||||
<pre>
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
var parent = ...; // DOM element where the compiled template can be appended
|
||||
|
||||
var html = '<div ng-bind="exp"></div>';
|
||||
|
||||
// Step 1: parse HTML into DOM element
|
||||
var template = angular.element(html);
|
||||
|
||||
// Step 2: compile the template
|
||||
var linkFn = $compile(template);
|
||||
|
||||
// Step 3: link the compiled template with the scope.
|
||||
var element = linkFn(scope);
|
||||
|
||||
// Step 4: Append to DOM (optional)
|
||||
parent.appendChild(element);
|
||||
</pre>
|
||||
|
||||
### The difference between Compile and Link
|
||||
|
||||
At this point you may wonder why the compile process has separate compile and link phases. The
|
||||
short answer is that compile and link separation is needed any time a change in a model causes
|
||||
a change in the **structure** of the DOM.
|
||||
|
||||
It's rare for directives to have a **compile function**, since most directives are concerned with
|
||||
working with a specific DOM element instance rather than changing its overall structure.
|
||||
|
||||
Directives often have a **link function**. A link function allows the directive to register
|
||||
listeners to the specific cloned DOM element instance as well as to copy content into the DOM
|
||||
from the scope.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** Any operation which can be shared among the instance of directives should be
|
||||
moved to the compile function for performance reasons.
|
||||
</div>
|
||||
|
||||
#### An Example of "Compile" Versus "Link"
|
||||
|
||||
To understand, let's look at a real-world example with `ngRepeat`:
|
||||
|
||||
<pre>
|
||||
Hello {{user}}, you have these actions:
|
||||
<ul>
|
||||
<li ng-repeat="action in user.actions">
|
||||
{{action.description}}
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
When the above example is compiled, the compiler visits every node and looks for directives.
|
||||
|
||||
`{{user}}` matches the {@link api/ng.$interpolate interpolation directive}
|
||||
and `ng-repeat` matches the {@link api/ng.directive:ngRepeat `ngRepeat` directive}.
|
||||
|
||||
But {@link api/ng.directive:ngRepeat ngRepeat} has a dilemma.
|
||||
|
||||
It needs to be able to clone new `<li>` elements for every `action` in `user.actions`.
|
||||
This initially seems trivial, but it becomes more complicated when you consider that `user.actions`
|
||||
might have items added to it later. This means that it needs to save a clean copy of the `<li>`
|
||||
element for cloning purposes.
|
||||
|
||||
As new `action`s are inserted, the template `<li>` element needs to be cloned and inserted into `ul`.
|
||||
But cloning the `<li>` element is not enough. It also needs to compile the `<li>` so that its
|
||||
directives, like `{{action.description}}`, evaluate against the right {@link api/ng.$rootScope.Scope scope}.
|
||||
|
||||
|
||||
A naive approach to solving this problem would be to simply insert a copy of the `<li>` element and
|
||||
then compile it.
|
||||
The problem with this approach is that compiling on every `<li>` element that we clone would duplicate
|
||||
a lot of the work. Specifically, we'd be traversing `<li>` each time before cloning it to find the
|
||||
directives. This would cause the compilation process to be slower, in turn making applications
|
||||
less responsive when inserting new nodes.
|
||||
|
||||
The solution is to break the compilation process into two phases:
|
||||
|
||||
the **compile phase** where all of the directives are identified and sorted by priority,
|
||||
and a **linking phase** where any work which "links" a specific instance of the
|
||||
{@link api/ng.$rootScope.Scope scope} and the specific instance of an `<li>` is performed.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** *Link* means setting up listeners on the DOM and setting up `$watch` on the Scope to
|
||||
keep the two in sync.
|
||||
</div>
|
||||
|
||||
{@link api/ng.directive:ngRepeat `ngRepeat`} works by preventing the compilation process from
|
||||
descending into the `<li>` element so it can make a clone of the original and handle inserting
|
||||
and removing DOM nodes itself.
|
||||
|
||||
Instead the {@link api/ng.directive:ngRepeat `ngRepeat`} directive compiles `<li>` separately.
|
||||
The result of the `<li>` element compilation is a linking function which contains all of the
|
||||
directives contained in the `<li>` element, ready to be attached to a specific clone of the `<li>`
|
||||
element.
|
||||
|
||||
At runtime the {@link api/ng.directive:ngRepeat `ngRepeat`} watches the expression and as items
|
||||
are added to the array it clones the `<li>` element, creates a new
|
||||
{@link api/ng.$rootScope.Scope scope} for the cloned `<li>` element and calls the link function
|
||||
on the cloned `<li>`.
|
||||
|
||||
|
||||
|
||||
### Understanding How Scopes Work with Transcluded Directives
|
||||
|
||||
One of the most common use cases for directives is to create reusable components.
|
||||
|
||||
Below is a pseudo code showing how a simplified dialog component may work.
|
||||
|
||||
<pre>
|
||||
<div>
|
||||
<button ng-click="show=true">show</button>
|
||||
|
||||
<dialog title="Hello {{username}}."
|
||||
visible="show"
|
||||
on-cancel="show = false"
|
||||
on-ok="show = false; doSomething()">
|
||||
Body goes here: {{username}} is {{title}}.
|
||||
</dialog>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
Clicking on the "show" button will open the dialog. The dialog will have a title, which is
|
||||
data bound to `username`, and it will also have a body which we would like to transclude
|
||||
into the dialog.
|
||||
|
||||
Here is an example of what the template definition for the `dialog` widget may look like.
|
||||
|
||||
<pre>
|
||||
<div ng-show="visible">
|
||||
<h3>{{title}}</h3>
|
||||
<div class="body" ng-transclude></div>
|
||||
<div class="footer">
|
||||
<button ng-click="onOk()">Save changes</button>
|
||||
<button ng-click="onCancel()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
This will not render properly, unless we do some scope magic.
|
||||
|
||||
The first issue we have to solve is that the dialog box template expects `title` to be defined.
|
||||
But we would like the template's scope property `title` to be the result of interpolating the
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`. Furthermore, the buttons expect
|
||||
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the `locals` to create local variables which the template
|
||||
expects as follows:
|
||||
|
||||
<pre>
|
||||
scope: {
|
||||
title: '@', // the title uses the data-binding from the parent scope
|
||||
onOk: '&', // create a delegate onOk function
|
||||
onCancel: '&', // create a delegate onCancel function
|
||||
visible: '=' // set up visible to accept data-binding
|
||||
}
|
||||
</pre>
|
||||
|
||||
Creating local properties on widget scope creates two problems:
|
||||
|
||||
1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog
|
||||
template will bind to parent scope property. This is unpredictable and undesirable.
|
||||
|
||||
2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the
|
||||
properties which the transclusion needs for data-binding. In our example the `title`
|
||||
property of the widget clobbers the `title` property of the transclusion.
|
||||
|
||||
|
||||
To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
|
||||
isolated scope does not prototypically inherit from the parent scope, and therefore we don't have
|
||||
to worry about accidentally clobbering any properties.
|
||||
|
||||
However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
|
||||
isolated scope then it will not be able to bind to anything. For this reason the transcluded scope
|
||||
is a child of the original scope, before the widget created an isolated scope for its local
|
||||
variables. This makes the transcluded and widget isolated scope siblings.
|
||||
|
||||
This may seem to be unexpected complexity, but it gives the widget user and developer the least
|
||||
surprise.
|
||||
|
||||
Therefore the final directive definition looks something like this:
|
||||
|
||||
<pre>
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: '@', // the title uses the data-binding from the parent scope
|
||||
onOk: '&', // create a delegate onOk function
|
||||
onCancel: '&', // create a delegate onCancel function
|
||||
visible: '=' // set up visible to accept data-binding
|
||||
},
|
||||
restrict: 'E',
|
||||
replace: true
|
||||
</pre>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,466 +2,383 @@
|
|||
@name Conceptual Overview
|
||||
@description
|
||||
|
||||
# Overview
|
||||
There are some concepts within Angular that you should understand before creating your first application.
|
||||
This section touches all important parts of Angular really quickly using a simple example.
|
||||
However, it won't explain all details.
|
||||
For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
|
||||
|
||||
This document gives a quick overview of the main angular components and how they work together.
|
||||
These are:
|
||||
|
||||
* {@link concepts#startup startup} - bring up hello world
|
||||
* {@link concepts#runtime runtime} - overview of angular runtime
|
||||
* {@link concepts#scope scope} - the glue between the view and the controller
|
||||
* {@link concepts#controller controller} - application behavior
|
||||
* {@link concepts#model model} - your application data
|
||||
* {@link concepts#view view} - what the user sees
|
||||
* {@link concepts#directives directives} - extend HTML vocabulary
|
||||
* {@link concepts#filters filters} - format the data in user locale
|
||||
* {@link concepts#injector injector} - assembles your application
|
||||
* {@link concepts#module module} - configures the injector
|
||||
* {@link concepts#angular_namespace `$`} - angular namespace
|
||||
|
||||
<a name="startup"></a>
|
||||
# Startup
|
||||
|
||||
This is how we get the ball rolling (refer to the diagram and example below):
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png">
|
||||
|
||||
1. The browser loads the HTML and parses it into a DOM
|
||||
2. The browser loads `angular.js` script
|
||||
3. Angular waits for `DOMContentLoaded` event
|
||||
4. Angular looks for {@link api/ng.directive:ngApp ng-app}
|
||||
{@link guide/directive directive}, which designates the application boundary
|
||||
5. The {@link guide/module Module} specified in {@link
|
||||
api/ng.directive:ngApp ng-app} (if any) is used to configure
|
||||
the {@link api/AUTO.$injector $injector}
|
||||
6. The {@link api/AUTO.$injector $injector} is used to create the {@link
|
||||
api/ng.$compile $compile} service as well as {@link
|
||||
api/ng.$rootScope $rootScope}
|
||||
7. The {@link api/ng.$compile $compile} service is used to compile the DOM and link
|
||||
it with {@link api/ng.$rootScope $rootScope}
|
||||
8. The {@link api/ng.directive:ngInit ng-init} {@link
|
||||
guide/directive directive} assigns `World` to the `name` property on the {@link guide/scope
|
||||
scope}
|
||||
9. The `{{name}}` {@link api/ng.$interpolate interpolates} the expression to
|
||||
`Hello World!`
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<p ng-init=" name='World' ">Hello {{name}}!</p>
|
||||
</file>
|
||||
</example>
|
||||
| Concept | Description |
|
||||
|------------------|------------------------------------------|
|
||||
|{@link concepts#template Template} | HTML with additional markup |
|
||||
|{@link concepts#directive Directives} | extend HTML with custom attributes and elements |
|
||||
|{@link concepts#model Model} | the data that is shown to the user and with which the user interacts |
|
||||
|{@link concepts#scope Scope} | context where the model is stored so that controllers, directives and expressions can access it |
|
||||
|{@link concepts#expression Expressions} | access variables and functions from the scope |
|
||||
|{@link concepts#compiler Compiler} | parses the template and instantiates directives and expressions |
|
||||
|{@link concepts#filter Filter} | formats the value of an expression for display to the user |
|
||||
|{@link concepts#view View} | what the user sees (the DOM) |
|
||||
|{@link concepts#databinding Data Binding} | sync data between the model and the view |
|
||||
|{@link concepts#controller Controller} | the business logic behind views |
|
||||
|{@link concepts#di Dependency Injection} | Creates and wires objects / functions |
|
||||
|{@link concepts#injector Injector} | dependency injection container |
|
||||
|{@link concepts#module Module} | configures the Injector |
|
||||
|{@link concepts#service Service} | reusable business logic independent of views |
|
||||
|
||||
|
||||
<a name="runtime"></a>
|
||||
# Runtime
|
||||
# A first example: Data binding
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png">
|
||||
In the following we will build a form to calculate the costs of an invoice in different currencies.
|
||||
|
||||
The diagram and the example below describe how Angular interacts with the browser's event loop.
|
||||
Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:
|
||||
|
||||
1. The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event,
|
||||
or network event (response from a server).
|
||||
2. The event's callback gets executed. This enters the JavaScript context. The callback can
|
||||
modify the DOM structure.
|
||||
3. Once the callback executes, the browser leaves the JavaScript context and
|
||||
re-renders the view based on DOM changes.
|
||||
|
||||
Angular modifies the normal JavaScript flow by providing its own event processing loop. This
|
||||
splits the JavaScript into classical and Angular execution context. Only operations which are
|
||||
applied in Angular execution context will benefit from Angular data-binding, exception handling,
|
||||
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
|
||||
mind that in most places (controllers, services) $apply has already been called for you by the
|
||||
directive which is handling the event. An explicit call to $apply is needed only when
|
||||
implementing custom event callbacks, or when working with third-party library callbacks.
|
||||
|
||||
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
|
||||
api/ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
|
||||
the work you wish to do in Angular execution context.
|
||||
2. Angular executes the `stimulusFn()`, which typically modifies application state.
|
||||
3. Angular enters the {@link api/ng.$rootScope.Scope#$digest $digest} loop. The
|
||||
loop is made up of two smaller loops which process {@link
|
||||
api/ng.$rootScope.Scope#$evalAsync $evalAsync} queue and the {@link
|
||||
api/ng.$rootScope.Scope#$watch $watch} list. The {@link
|
||||
api/ng.$rootScope.Scope#$digest $digest} loop keeps iterating until the model
|
||||
stabilizes, which means that the {@link api/ng.$rootScope.Scope#$evalAsync
|
||||
$evalAsync} queue is empty and the {@link api/ng.$rootScope.Scope#$watch
|
||||
$watch} list does not detect any changes.
|
||||
4. The {@link api/ng.$rootScope.Scope#$evalAsync $evalAsync} queue is used to
|
||||
schedule work which needs to occur outside of current stack frame, but before the browser's
|
||||
view render. This is usually done with `setTimeout(0)`, but the `setTimeout(0)` approach
|
||||
suffers from slowness and may cause view flickering since the browser renders the view after
|
||||
each event.
|
||||
5. The {@link api/ng.$rootScope.Scope#$watch $watch} list is a set of expressions
|
||||
which may have changed since last iteration. If a change is detected then the `$watch`
|
||||
function is called which typically updates the DOM with the new value.
|
||||
6. Once the Angular {@link api/ng.$rootScope.Scope#$digest $digest} loop finishes
|
||||
the execution leaves the Angular and JavaScript context. This is followed by the browser
|
||||
re-rendering the DOM to reflect any changes.
|
||||
|
||||
|
||||
Here is the explanation of how the `Hello world` example achieves the data-binding effect when the
|
||||
user enters text into the text field.
|
||||
|
||||
1. During the compilation phase:
|
||||
1. the {@link api/ng.directive:ngModel ng-model} and {@link
|
||||
api/ng.directive:input input} {@link guide/directive
|
||||
directive} set up a `keydown` listener on the `<input>` control.
|
||||
2. the {@link api/ng.$interpolate {{name}} } interpolation
|
||||
sets up a {@link api/ng.$rootScope.Scope#$watch $watch} to be notified of
|
||||
`name` changes.
|
||||
2. During the runtime phase:
|
||||
1. Pressing an '`X`' key causes the browser to emit a `keydown` event on the input control.
|
||||
2. The {@link api/ng.directive:input input} directive
|
||||
captures the change to the input's value and calls {@link
|
||||
api/ng.$rootScope.Scope#$apply $apply}`("name = 'X';")` to update the
|
||||
application model inside the Angular execution context.
|
||||
3. Angular applies the `name = 'X';` to the model.
|
||||
4. The {@link api/ng.$rootScope.Scope#$digest $digest} loop begins
|
||||
5. The {@link api/ng.$rootScope.Scope#$watch $watch} list detects a change
|
||||
on the `name` property and notifies the {@link api/ng.$interpolate
|
||||
{{name}} } interpolation, which in turn updates the DOM.
|
||||
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
|
||||
the JavaScript execution context.
|
||||
7. The browser re-renders the view with update text.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<input ng-model="name">
|
||||
<p>Hello {{name}}!</p>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<a name="scope"></a>
|
||||
#Scope
|
||||
|
||||
The {@link guide/scope scope} is responsible for detecting changes to the model section and
|
||||
provides the execution context for expressions. The scopes are nested in a hierarchical structure
|
||||
which closely follow the DOM structure. (See individual directive documentation to see which
|
||||
directives cause a creation of new scopes.)
|
||||
|
||||
The following example demonstrates how the `name` {@link guide/expression expression} will evaluate
|
||||
into a different value depending on which scope it is evaluated in. The example is followed by
|
||||
a diagram depicting the scope boundaries.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<div class="show-scope">
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="GreetCtrl">
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
<div ng-controller="ListCtrl">
|
||||
<ol>
|
||||
<li ng-repeat="name in names">{{name}}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function GreetCtrl($scope) {
|
||||
$scope.name = 'World';
|
||||
}
|
||||
|
||||
function ListCtrl($scope) {
|
||||
$scope.names = ['Igor', 'Misko', 'Vojta'];
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.show-scope .doc-example-live.ng-scope,
|
||||
.show-scope .doc-example-live .ng-scope {
|
||||
border: 1px solid red;
|
||||
margin: 3px;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
</div>
|
||||
|
||||
<img class="center" src="img/guide/concepts-scope.png">
|
||||
|
||||
|
||||
<a name="controller"></a>
|
||||
# Controller
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-controller.png">
|
||||
|
||||
A controller is the code behind the view. Its job is to construct the model and publish it to the
|
||||
view along with callback methods. The view is a projection of the scope onto the template (the
|
||||
HTML). The scope is the glue which marshals the model to the view and forwards the events to the
|
||||
controller.
|
||||
|
||||
The separation of the controller and the view is important because:
|
||||
|
||||
* The controller is written in JavaScript. JavaScript is imperative. Imperative is a good fit
|
||||
for specifying application behavior. The controller should not contain any rendering
|
||||
information (DOM references or HTML fragments).
|
||||
* The view template is written in HTML. HTML is declarative. Declarative is a good fit for
|
||||
specifying UI. The View should not contain any behavior.
|
||||
* Since the controller is unaware of the view, there could be many views for the same
|
||||
controller. This is important for re-skinning, device specific views (i.e. mobile vs desktop),
|
||||
and testability.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="MyCtrl">
|
||||
Hello {{name}}!
|
||||
<button ng-click="action()">
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function MyCtrl($scope) {
|
||||
$scope.action = function() {
|
||||
$scope.name = 'OK';
|
||||
}
|
||||
|
||||
$scope.name = 'World';
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
<a name="model"></a>
|
||||
# Model
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png">
|
||||
|
||||
The model is the data which is merged with the template to produce the view. To be able to
|
||||
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
|
||||
other frameworks Angular makes no restrictions or requirements on the model. There are no classes
|
||||
to inherit from or special accessor methods for accessing or changing the model. The model can be
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
|
||||
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
|
||||
<a name="view"></a>
|
||||
# View
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
|
||||
|
||||
The view is what the user sees. The view begins its life as a template, is merged with the
|
||||
model and finally rendered into the browser DOM. Angular takes a very different approach to
|
||||
rendering the view compared to most other templating systems.
|
||||
|
||||
* **Others** - Most templating systems begin as an HTML string with special templating markup.
|
||||
Often the template markup breaks the HTML syntax which means that the template can not be
|
||||
edited by an HTML editor. The template string is then parsed by the template engine, and
|
||||
merged with the data. The result of the merge is an HTML string. The HTML string is then
|
||||
written to the browser using the `.innerHTML`, which causes the browser to render the HTML.
|
||||
When the model changes the whole process needs to be repeated. The granularity of the template
|
||||
is the granularity of the DOM updates. The key here is that the templating system manipulates
|
||||
strings.
|
||||
* **Angular** - Angular is different, since its templating system works on DOM objects not on
|
||||
strings. The template is still written in an HTML string, but it is HTML (not HTML with
|
||||
template sprinkled in.) The browser parses the HTML into the DOM, and the DOM becomes the input to
|
||||
the template engine known as the {@link api/ng.$compile compiler}. The compiler
|
||||
looks for {@link guide/directive directives} which in turn set up {@link
|
||||
api/ng.$rootScope.Scope#$watch watches} on the model. The result is a
|
||||
continuously updating view which does not need template model re-merging. Your model becomes
|
||||
the single source-of-truth for your view.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
<input ng-model="list" ng-list> <br>
|
||||
<input ng-model="list" ng-list> <br>
|
||||
<pre>list={{list}}</pre> <br>
|
||||
<ol>
|
||||
<li ng-repeat="item in list">
|
||||
{{item}}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div ng-init="qty=1;cost=2">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="cost" required >
|
||||
</div>
|
||||
<div>
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Try out the Live Preview above, and then let's walk through the example and describe what's going on.
|
||||
|
||||
<a name="directives"></a>
|
||||
# Directives
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding1.png">
|
||||
|
||||
A directive is a behavior or DOM transformation which is triggered by the presence of a custom attribute,
|
||||
element name, class name or comment. A directive allows you to extend the HTML vocabulary in a
|
||||
declarative fashion. Following is an example which enables data-binding for the `contenteditable`
|
||||
in HTML.
|
||||
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
|
||||
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
|
||||
processes this new markup from the template using the so called <a name="compiler">"{@link compiler compiler}"</a>.
|
||||
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
|
||||
|
||||
<example module="directive">
|
||||
<file name="script.js">
|
||||
angular.module('directive', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// view -> model
|
||||
elm.on('blur', function() {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
});
|
||||
The first kind of new markup are the so called <a name="directive">"{@link directive directives}"</a>.
|
||||
They apply special behavior to attributes or elements in the HTML. In the example above we use the
|
||||
{@link api/ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
|
||||
initializes our application. Angular also defines a directive for the {@link api/ng.directive:input `input`}
|
||||
element that adds extra behavior to the element. E.g. it is able to automatically validate that the entered
|
||||
text is non empty by evaluating the `required` attribute.
|
||||
The {@link api/ng.directive:ngModel `ng-model`} directive stores/updates
|
||||
the value of the input field into/from a variable and shows the validation state of the input field by
|
||||
adding css classes. In the example we use these css classes to mark an empty input field with a red border.
|
||||
|
||||
// model -> view
|
||||
ctrl.$render = function(value) {
|
||||
elm.html(value);
|
||||
};
|
||||
<div class="alert alert-info">
|
||||
**Custom directives to access the DOM**: In Angular, the only place where an application touches the DOM is
|
||||
within directives. This is good as artifacts that access the DOM are hard to test.
|
||||
If you need to access the DOM directly you should write a custom directive for this. The
|
||||
{@link directive directives guide} explains how to do this.
|
||||
</div>
|
||||
|
||||
// load init value from DOM
|
||||
ctrl.$setViewValue(elm.html());
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div contentEditable="true" ng-model="content">Edit Me</div>
|
||||
<pre>model = {{content}}</pre>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
div[contentEditable] {
|
||||
cursor: pointer;
|
||||
background-color: #D0D0D0;
|
||||
margin-bottom: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
The second kind of new markup are the double curly braces `{{ expression | filter }}`:
|
||||
When the compiler encounters this markup, it will replace it with the evaluated value of the markup.
|
||||
An <a name="expression">"{@link expression expression}"</a> in a template is a JavaScript-like code snippet that allows
|
||||
to read and write variables. Note that those variables are not global variables.
|
||||
Just like variables in a JavaScript function live in a scope,
|
||||
Angular provides a <a name="scope">"{@link scope scope}"</a> for the variables accessible to expressions.
|
||||
The values that are stored in variables on the scope are referred to as the <a name="model">"model"</a>
|
||||
in the rest of the documentation.
|
||||
Applied to the example above, the markup directs Angular to "take the data we got from the input widgets
|
||||
and multiply them together".
|
||||
|
||||
<a name="filters"></a>
|
||||
# Filters
|
||||
The example above also contains a <a name="filter">"{@link filter filter}"</a>.
|
||||
A filter formats the value of an expression for display to the user.
|
||||
In the example above, the filter {@link api/ng.filter:currency `currency`} formats a number
|
||||
into an output that looks like money.
|
||||
|
||||
{@link api/ng.$filter Filters} perform data transformation. Typically
|
||||
they are used in conjunction with the locale to format the data in locale specific output.
|
||||
They follow the spirit of UNIX filters and use similar syntax `|` (pipe).
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
Number formatting: {{ 1234567890 | number }} <br>
|
||||
array filtering <input ng-model="predicate">
|
||||
{{ list | filter:predicate | json }}
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
The important thing in the example is that angular provides _live_ bindings:
|
||||
Whenever the input values change, the value of the expressions are automatically
|
||||
recalculated and the DOM is updated with their values.
|
||||
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
|
||||
|
||||
|
||||
<a name="module"></a>
|
||||
<a name="injector"></a>
|
||||
# Modules and the Injector
|
||||
# Adding UI logic: Controllers
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png">
|
||||
Let's add some more logic to the example that allows us to enter and calculate the costs in
|
||||
different currencies and also pay the invoice.
|
||||
|
||||
The {@link api/AUTO.$injector injector} is a service locator. There is a single
|
||||
{@link api/AUTO.$injector injector} per Angular {@link
|
||||
api/ng.directive:ngApp application}. The {@link
|
||||
api/AUTO.$injector injector} provides a way to look up an object instance by its
|
||||
name. The injector keeps an internal cache of all objects so that repeated calls to get the same
|
||||
object name result in the same instance. If the object does not exist, then the {@link
|
||||
api/AUTO.$injector injector} asks the instance factory to create a new instance.
|
||||
<example module="invoice1">
|
||||
<file name="invoice1.js">
|
||||
angular.module('invoice1', [])
|
||||
.controller('InvoiceController', function() {
|
||||
this.qty = 1;
|
||||
this.cost = 2;
|
||||
this.inCurr = 'EUR';
|
||||
this.currencies = ['USD', 'EUR', 'CNY'];
|
||||
this.usdToForeignRates = {
|
||||
USD: 1,
|
||||
EUR: 0.74,
|
||||
CNY: 6.09
|
||||
};
|
||||
|
||||
A {@link api/angular.Module module} is a way to configure the injector's instance factory, known
|
||||
as a {@link api/AUTO.$provide provider}.
|
||||
|
||||
<div class='clear'></div>
|
||||
<pre>
|
||||
// Create a module
|
||||
var myModule = angular.module('myModule', [])
|
||||
|
||||
// Configure the injector
|
||||
myModule.factory('serviceA', function() {
|
||||
return {
|
||||
// instead of {}, put your object creation here
|
||||
};
|
||||
});
|
||||
|
||||
// create an injector and configure it from 'myModule'
|
||||
var $injector = angular.injector(['myModule']);
|
||||
|
||||
// retrieve an object from the injector by name
|
||||
var serviceA = $injector.get('serviceA');
|
||||
|
||||
// always true because of instance cache
|
||||
$injector.get('serviceA') === $injector.get('serviceA');
|
||||
</pre>
|
||||
|
||||
|
||||
But the real magic of the {@link api/AUTO.$injector injector} is that it can be
|
||||
used to {@link api/AUTO.$injector#invoke call} methods and {@link
|
||||
api/AUTO.$injector#instantiate instantiate} types. This subtle feature is what
|
||||
allows the methods and types to ask for their dependencies instead of having to look for them.
|
||||
|
||||
<pre>
|
||||
// You write functions such as this one.
|
||||
function doSomething(serviceA, serviceB) {
|
||||
// do something here.
|
||||
}
|
||||
|
||||
// Angular provides the injector for your application
|
||||
var $injector = ...;
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// the old-school way of getting dependencies.
|
||||
var serviceA = $injector.get('serviceA');
|
||||
var serviceB = $injector.get('serviceB');
|
||||
|
||||
// now call the function
|
||||
doSomething(serviceA, serviceB);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// the cool way of getting dependencies.
|
||||
// the $injector will supply the arguments to the function automatically
|
||||
$injector.invoke(doSomething); // This is how the framework calls your functions
|
||||
</pre>
|
||||
|
||||
Notice that the only thing you needed to write was the function, and list the dependencies in the
|
||||
function arguments. When angular calls the function, it will use the {@link
|
||||
api/AUTO.$injector#invoke call} which will automatically fill the function
|
||||
arguments.
|
||||
|
||||
Examine the `ClockCtrl` below, and notice how it lists the dependencies in the constructor. When the
|
||||
{@link api/ng.directive:ngController ng-controller} instantiates
|
||||
the controller it automatically provides the dependencies. There is no need to create
|
||||
dependencies, look for dependencies, or even get a reference to the injector.
|
||||
|
||||
<example module="timeExampleModule">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ClockCtrl">
|
||||
Current time is: {{ time.now }}
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('timeExampleModule', []).
|
||||
// Declare new object called time,
|
||||
// which will be available for injection
|
||||
factory('time', function($timeout) {
|
||||
var time = {};
|
||||
|
||||
(function tick() {
|
||||
time.now = new Date().toString();
|
||||
$timeout(tick, 1000);
|
||||
})();
|
||||
return time;
|
||||
this.total = function total(outCurr) {
|
||||
return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
|
||||
};
|
||||
this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
|
||||
return amount * this.usdToForeignRates[outCurr] * 1 / this.usdToForeignRates[inCurr];
|
||||
};
|
||||
this.pay = function pay() {
|
||||
window.alert("Thanks!");
|
||||
};
|
||||
});
|
||||
|
||||
// Notice that you can simply ask for time
|
||||
// and it will be provided. No need to look for it.
|
||||
function ClockCtrl($scope, time) {
|
||||
$scope.time = time;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="invoice.cost" required >
|
||||
<select ng-model="invoice.inCurr">
|
||||
<option ng-repeat="c in invoice.currencies">{{c}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<b>Total:</b>
|
||||
<span ng-repeat="c in invoice.currencies">
|
||||
{{invoice.total(c) | currency:c}}
|
||||
</span>
|
||||
<button class="btn" ng-click="invoice.pay()">Pay</button>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
What changed?
|
||||
|
||||
<a name="angular_namespace"></a>
|
||||
# Angular Namespace
|
||||
First, there is a new JavaScript file that contains a so called <a name="controller">"{@link controller controller}"</a>.
|
||||
More exactly, the file contains a constructor function that creates the actual controller instance.
|
||||
The purpose of controllers is to expose variables and functionality to expressions and directives.
|
||||
|
||||
Besides the new file that contains the controller code we also added a
|
||||
{@link api/ng.directive:ngController `ng-controller`} directive to the HTML.
|
||||
This directive tells angular that the new `InvoiceController` is responsible for the element with the directive
|
||||
and all of the element's children.
|
||||
The syntax `InvoiceController as invoice` tells Angular to instantiate the controller
|
||||
and save it in the variable `invoice` in the current scope.
|
||||
|
||||
We also changed all expressions in the page to read and write variables within that
|
||||
controller instance by prefixing them with `invoice.` . The possible currencies are defined in the controller
|
||||
and added to the template using {@link api/ng.directive:ngRepeat `ng-repeat`}.
|
||||
As the controller contains a `total` function
|
||||
we are also able to bind the result of that function to the DOM using `{{ invoice.total(...) }}`.
|
||||
|
||||
Again, this binding is live, i.e. the DOM will be automatically updated
|
||||
whenever the result of the function changes.
|
||||
The button to pay the invoice uses the directive {@link api/ng.directive:ngClick `ngClick`}. This will evaluate the
|
||||
corresponding expression whenever the button is clicked.
|
||||
|
||||
In the new JavaScript file we are also creating a {@link concepts#module module}
|
||||
at which we register the controller. We will talk about modules in the next section.
|
||||
|
||||
The following graphic shows how everything works together after we introduced the controller:
|
||||
|
||||
<img style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding2.png">
|
||||
|
||||
# View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
|
||||
Let's refactor our example and move the currency conversion into a service in another file:
|
||||
|
||||
<example module="invoice2">
|
||||
<file name="finance2.js">
|
||||
angular.module('finance2', [])
|
||||
.factory('currencyConverter', function() {
|
||||
var currencies = ['USD', 'EUR', 'CNY'],
|
||||
usdToForeignRates = {
|
||||
USD: 1,
|
||||
EUR: 0.74,
|
||||
CNY: 6.09
|
||||
};
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert
|
||||
};
|
||||
|
||||
function convert(amount, inCurr, outCurr) {
|
||||
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="invoice2.js">
|
||||
angular.module('invoice2', ['finance2'])
|
||||
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
|
||||
this.qty = 1;
|
||||
this.cost = 2;
|
||||
this.inCurr = 'EUR';
|
||||
this.currencies = currencyConverter.currencies;
|
||||
|
||||
this.total = function total(outCurr) {
|
||||
return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
|
||||
};
|
||||
this.pay = function pay() {
|
||||
window.alert("Thanks!");
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="invoice.cost" required >
|
||||
<select ng-model="invoice.inCurr">
|
||||
<option ng-repeat="c in invoice.currencies">{{c}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<b>Total:</b>
|
||||
<span ng-repeat="c in invoice.currencies">
|
||||
{{invoice.total(c) | currency:c}}
|
||||
</span>
|
||||
<button class="btn" ng-click="invoice.pay()">Pay</button>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-service.png">
|
||||
|
||||
What changed?
|
||||
We moved the `convertCurrency` function and the definition of the existing currencies
|
||||
into the new file `finance.js`. But how does the controller
|
||||
get a hold of the now separated function?
|
||||
|
||||
This is where <a name="di">"{@link di Dependency Injection}"</a> comes into play.
|
||||
Dependency Injection (DI) is a software design pattern that
|
||||
deals with how objects and functions get created and how they get a hold of their dependencies.
|
||||
Everything within Angular (directives, filters, controllers,
|
||||
services, ...) is created and wired using dependency injection. Within Angular,
|
||||
the DI container is called the <a name="injector">"{@link di injector}"</a>.
|
||||
|
||||
To use DI, there needs to be a place where all the things that should work together are registered.
|
||||
In Angular, this is the purpose of the so called <a name="module">"{@link module modules}"</a>.
|
||||
When Angular starts, it will use the configuration of the module with the name defined by the `ng-app` directive,
|
||||
including the configuration of all modules that this module depends on.
|
||||
|
||||
In the example above:
|
||||
The template contains the directive `ng-app="invoice2"`. This tells Angular
|
||||
to use the `invoice` module as the main module for the application.
|
||||
The code snippet `angular.module('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the
|
||||
`finance2` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
|
||||
Now that Angular knows of all the parts of the application, it needs to create them.
|
||||
In the previous section we saw that controllers are created using a factory function.
|
||||
For services there are multiple ways to define their factory
|
||||
(see the {@link dev_guide.services service guide}).
|
||||
In the example above, we are using a function that returns the `currencyConverter` function as the factory
|
||||
for the service.
|
||||
|
||||
Back to the initial question: How does the `InvoiceController` get a reference to the `currencyConverter` function?
|
||||
In Angular, this is done by simply defining arguments on the constructor function. With this, the injector
|
||||
is able to create the objects in the right order and pass the previously created objects into the
|
||||
factories of the objects that depend on them.
|
||||
In our example, the `InvoiceController` has an argument named `currencyConverter`. By this, Angular knows about the
|
||||
dependency between the controller and the service and calls the controller with the service instance as argument.
|
||||
|
||||
The last thing that changed in the example between the previous section and this section is that we
|
||||
now pass an array to the `module.controller` function, instead of a plain function. The array first
|
||||
contains the names of the service dependencies that the controller needs. The last entry
|
||||
in the array is the controller constructor function.
|
||||
Angular uses this array syntax to define the dependencies so that the DI also works after minifying
|
||||
the code, which will most probably rename the argument name of the controller constructor function
|
||||
to something shorter like `a`.
|
||||
|
||||
# Accessing the backend
|
||||
|
||||
Let's finish our example by fetching the exchange rates from the Yahoo Finance API.
|
||||
The following example shows how this is done with Angular:
|
||||
|
||||
<example module="invoice3">
|
||||
<file name="invoice3.js">
|
||||
angular.module('invoice3', ['finance3'])
|
||||
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
|
||||
this.qty = 1;
|
||||
this.cost = 2;
|
||||
this.inCurr = 'EUR';
|
||||
this.currencies = currencyConverter.currencies;
|
||||
|
||||
this.total = function total(outCurr) {
|
||||
return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
|
||||
};
|
||||
this.pay = function pay() {
|
||||
window.alert("Thanks!");
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="finance3.js">
|
||||
angular.module('finance3', [])
|
||||
.factory('currencyConverter', ['$http', function($http) {
|
||||
var YAHOO_FINANCE_URL_PATTERN =
|
||||
'http://query.yahooapis.com/v1/public/yql?q=select * from '+
|
||||
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
|
||||
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK',
|
||||
currencies = ['USD', 'EUR', 'CNY'],
|
||||
usdToForeignRates = {};
|
||||
refresh();
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert,
|
||||
refresh: refresh
|
||||
};
|
||||
|
||||
function convert(amount, inCurr, outCurr) {
|
||||
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
var url = YAHOO_FINANCE_URL_PATTERN.
|
||||
replace('PAIRS', 'USD' + currencies.join('","USD'));
|
||||
return $http.jsonp(url).success(function(data) {
|
||||
var newUsdToForeignRates = {};
|
||||
angular.forEach(data.query.results.rate, function(rate) {
|
||||
var currency = rate.id.substring(3,6);
|
||||
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
|
||||
});
|
||||
usdToForeignRates = newUsdToForeignRates;
|
||||
});
|
||||
}
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="invoice.cost" required >
|
||||
<select ng-model="invoice.inCurr">
|
||||
<option ng-repeat="c in invoice.currencies">{{c}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<b>Total:</b>
|
||||
<span ng-repeat="c in invoice.currencies">
|
||||
{{invoice.total(c) | currency:c}}
|
||||
</span>
|
||||
<button class="btn" ng-click="invoice.pay()">Pay</button>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
What changed?
|
||||
Our `currencyConverter` service of the `finance` module now uses the
|
||||
{@link api/ng.$http $http} service, a builtin service provided by Angular
|
||||
for accessing the backend. It is a wrapper around [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||||
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports. Details can be found in the api docs of that service.
|
||||
|
||||
To prevent accidental name collision, Angular prefixes names of objects which could potentially
|
||||
collide with `$`. Please do not use the `$` prefix in your code as it may accidentally collide
|
||||
with Angular code.
|
||||
|
|
|
|||
340
docs/content/guide/controller.ngdoc
Normal file
340
docs/content/guide/controller.ngdoc
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
@ngdoc overview
|
||||
@name Controllers
|
||||
@description
|
||||
|
||||
# Understanding Controllers
|
||||
|
||||
In Angular, a Controller is a JavaScript **constructor function** that is used to augment the
|
||||
{@link scope Angular Scope}.
|
||||
|
||||
When a Controller is attached to the DOM via the {@link api/ng.directive:ngController ng-controller}
|
||||
directive, Angular will instantiate a new Controller object, using the specified Controller's
|
||||
**constructor function**. A new **child scope** will be available as an injectable parameter to the
|
||||
Controller's constructor function as `$scope`.
|
||||
|
||||
Use Controllers to:
|
||||
|
||||
- Set up the initial state of the `$scope` object.
|
||||
- Add behavior to the `$scope` object.
|
||||
|
||||
# Setting up the initial state of a `$scope` object
|
||||
|
||||
Typically, when you create an application you need to set up the initial state for the Angular
|
||||
`$scope`. You set up the initial state of a scope by attaching properties to the `$scope` object.
|
||||
The properties contain the **view model** (the model that will be presented by the view). All the
|
||||
`$scope` properties will be available to the template at the point in the DOM where the Controller
|
||||
is registered.
|
||||
|
||||
The following example shows a very simple constructor function for a Controller, `GreetingCtrl`,
|
||||
which attaches a `greeting` property containing the string `'Hola!'` to the `$scope`:
|
||||
|
||||
<pre>
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
</pre>
|
||||
|
||||
Once the Controller has been attached to the DOM, the `greeting` property can be data-bound to the
|
||||
template:
|
||||
|
||||
<pre>
|
||||
<div ng-controller="GreetingCtrl">
|
||||
{{ greeting }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
**NOTE**: Although Angular allows you to create Controller functions in the global scope, this is
|
||||
not recommended. In a real application you should use the `.controller` method of your
|
||||
{@link module Angular Module} for your application as follows:
|
||||
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
We have used an **inline injection annotation** to explicitly specify the dependency
|
||||
of the Controller on the `$scope` service provided by Angular. See the guide on
|
||||
{@link http://docs.angularjs.org/guide/di Dependency Injection} for more information.
|
||||
|
||||
|
||||
# Adding Behavior to a Scope Object
|
||||
|
||||
In order to react to events or execute computation in the view we must provide behavior to the
|
||||
scope. We add behavior to the scope by attaching methods to the `$scope` object. These methods are
|
||||
then available to be called from the template/view.
|
||||
|
||||
The following example uses a Controller to add a method to the scope, which doubles a number:
|
||||
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('DoubleCtrl', ['$scope', function($scope) {
|
||||
$scope.double = function(value) { return value * 2; };
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
Once the Controller has been attached to the DOM, the `double` method can be invoked in an Angular
|
||||
expression in the template:
|
||||
|
||||
<pre>
|
||||
<div ng-controller="DoubleCtrl">
|
||||
Two times <input ng-model="num"> equals {{ double(num) }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
As discussed in the {@link concepts Concepts} section of this guide, any
|
||||
objects (or primitives) assigned to the scope become model properties. Any methods assigned to
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and `ng` event handler directives (e.g. {@link api/ng.directive:ngClick ngClick}).
|
||||
|
||||
# Using Controllers Correctly
|
||||
|
||||
In general, a Controller shouldn't try to do too much. It should contain only the business logic
|
||||
needed for a single view.
|
||||
|
||||
The most common way to keep Controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in Controllers via dependency injection.
|
||||
This is discussed in the {@link di Dependency Injection} {@link dev_guide.services
|
||||
Services} sections of this guide.
|
||||
|
||||
Do not use Controllers for:
|
||||
|
||||
- Any kind of DOM manipulation — Controllers should contain only business logic. DOM
|
||||
manipulation (the presentation logic of an application) is well known for being hard to test.
|
||||
Putting any presentation logic into Controllers significantly affects testability of the business
|
||||
logic. Angular offers {@link databinding databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
|
||||
{@link guide/directive directives}.
|
||||
- Input formatting — Use {@link forms angular form controls} instead.
|
||||
- Output filtering — Use {@link filter angular filters} instead.
|
||||
- Sharing stateless or stateful code across Controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
- Managing the life-cycle of other components (for example, to create service instances).
|
||||
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
You can associate Controllers with scope objects implicitly via the {@link api/ng.directive:ngController ngController
|
||||
directive} or {@link api/ngRoute.$route $route service}.
|
||||
|
||||
|
||||
## Simple Spicy Controller Example
|
||||
|
||||
To illustrate further how Controller components work in Angular, let's create a little app with the
|
||||
following components:
|
||||
|
||||
- A {@link templates template} with two buttons and a simple message
|
||||
- A model consisting of a string named `spice`
|
||||
- A Controller with two functions that set the value of `spice`
|
||||
|
||||
The message in our template contains a binding to the `spice` model, which by default is set to the
|
||||
string "very". Depending on which button is clicked, the `spice` model is set to `chili` or
|
||||
`jalapeño`, and the message is automatically updated by data-binding.
|
||||
|
||||
<doc:example module="spicyApp1">
|
||||
<doc:source>
|
||||
<div ng-app="spicyApp1" ng-controller="SpicyCtrl">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</div>
|
||||
<script>
|
||||
var myApp = angular.module('spicyApp1', []);
|
||||
|
||||
myApp.controller('SpicyCtrl', ['$scope', function($scope){
|
||||
$scope.spice = 'very';
|
||||
|
||||
$scope.chiliSpicy = function() {
|
||||
$scope.spice = 'chili';
|
||||
};
|
||||
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
Things to notice in the example above:
|
||||
|
||||
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` Controller.
|
||||
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||
its children).
|
||||
|
||||
## Spicy Arguments Example
|
||||
|
||||
Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.
|
||||
|
||||
<doc:example module="spicyApp2">
|
||||
<doc:source>
|
||||
<div ng-app="spicyApp2" ng-controller="SpicyCtrl">
|
||||
<input ng-model="customSpice">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</div>
|
||||
<script>
|
||||
var myApp = angular.module('spicyApp2', []);
|
||||
|
||||
myApp.controller('SpicyCtrl', ['$scope', function($scope){
|
||||
$scope.customSpice = "wasabi";
|
||||
$scope.spice = 'very';
|
||||
|
||||
$scope.spicy = function(spice){
|
||||
$scope.spice = spice;
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
Notice that the `SpicyCtrl` Controller now defines just one method called `spicy`, which takes one
|
||||
argument called `spice`. The template then refers to this Controller method and passes in a string
|
||||
constant `'chili'` in the binding for the first button and a model property `customSpice` (bound to an
|
||||
input box) in the second button.
|
||||
|
||||
## Scope Inheritance Example
|
||||
|
||||
It is common to attach Controllers at different levels of the DOM hierarchy. Since the
|
||||
{@link api/ng.directive:ngController ng-controller} directive creates a new child scope, we get a
|
||||
hierarchy of scopes that inherit from each other. The `$scope` that each Controller receives will
|
||||
have access to properties and methods defined by Controllers higher up the hierarchy.
|
||||
See {@link https://github.com/angular/angular.js/wiki/Understanding-Scopes Understanding Scopes} for
|
||||
more information about scope inheritance.
|
||||
|
||||
<doc:example module="scopeInheritance">
|
||||
<doc:source>
|
||||
<div ng-app="scopeInheritance" class="spicy">
|
||||
<div ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
||||
<div ng-controller="GrandChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
div.spicy div {
|
||||
padding: 10px;
|
||||
border: solid 2px blue;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var myApp = angular.module('scopeInheritance', []);
|
||||
myApp.controller('MainCtrl', ['$scope', function($scope){
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}]);
|
||||
myApp.controller('ChildCtrl', ['$scope', function($scope){
|
||||
$scope.name = 'Mattie';
|
||||
}]);
|
||||
myApp.controller('GrandChildCtrl', ['$scope', function($scope){
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}]);
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
Notice how we nested three `ng-controller` directives in our template. This will result in four
|
||||
scopes being created for our view:
|
||||
|
||||
- The root scope
|
||||
- The `MainCtrl` scope, which contains `timeOfDay` and `name` properties
|
||||
- The `ChildCtrl` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
|
||||
property from the previous
|
||||
- The `GrandChildCtrl` scope, which overrides (hides) both the `timeOfDay` property defined in `MainCtrl`
|
||||
and the `name` property defined in `ChildCtrl`
|
||||
|
||||
Inheritance works with methods in the same way as it does with properties. So in our previous
|
||||
examples, all of the properties could be replaced with methods that return string values.
|
||||
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
Although there are many ways to test a Controller, one of the best conventions, shown below,
|
||||
involves injecting the {@link api/ng.$rootScope $rootScope} and {@link api/ng.$controller $controller}:
|
||||
|
||||
**Controller Definition:**
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('MyController', function($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
$scope.spice = "habanero";
|
||||
});
|
||||
</pre>
|
||||
|
||||
**Controller Test:**
|
||||
<pre>
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function() {
|
||||
var $scope;
|
||||
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
$scope = $rootScope.$new();
|
||||
$controller('MyController', {$scope: $scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect($scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect($scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
If you need to test a nested Controller you need to create the same scope hierarchy
|
||||
in your test that exists in the DOM:
|
||||
|
||||
<pre>
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, grandChildScope;
|
||||
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
$controller('MainCtrl', {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
$controller('ChildCtrl', {$scope: childScope});
|
||||
grandChildScope = childScope.$new();
|
||||
$controller('GrandChildCtrl', {$scope: grandChildScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
expect(mainScope.timeOfDay).toBe('morning');
|
||||
expect(mainScope.name).toBe('Nikki');
|
||||
expect(childScope.timeOfDay).toBe('morning');
|
||||
expect(childScope.name).toBe('Mattie');
|
||||
expect(grandChildScope.timeOfDay).toBe('evening');
|
||||
expect(grandChildScope.name).toBe('Gingerbreak Baby');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Data Binding in Angular
|
||||
@name Data Binding
|
||||
@description
|
||||
|
||||
Data-binding in Angular web apps is the automatic synchronization of data between the model and view
|
||||
|
|
@ -35,4 +35,4 @@ isolation without the view and the related DOM/browser dependency.
|
|||
## Related Topics
|
||||
|
||||
* {@link scope Angular Scopes}
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link templates Angular Templates}
|
||||
|
|
@ -3,6 +3,10 @@
|
|||
@name Developer Guide: E2E Testing
|
||||
@description
|
||||
|
||||
**If you're starting a new Angular project, you may want to look into
|
||||
using {@link https://github.com/angular/protractor Protractor}, as it is going to
|
||||
replace the current method of E2E Testing in the near future.**
|
||||
|
||||
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
|
||||
verify the correctness of new features, catch bugs and notice regressions.
|
||||
|
||||
|
|
@ -37,10 +41,15 @@ it('should filter results', function() {
|
|||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that
|
||||
[`input('user')`](https://github.com/angular/angular.js/blob/master/docs/content/guide/dev_guide.e2e-testing.ngdoc#L119)
|
||||
finds the `<input>` element with `ng-model="user"` not `name="user"`.
|
||||
|
||||
This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
|
||||
filter the stream of the user. It starts by entering a value in the 'user' input field, clicking
|
||||
filter the stream of the user. It starts by entering a value in the input field with ng-model="user", clicking
|
||||
the only button on the page, and then it verifies that there are 10 items listed. It then enters
|
||||
'Bees' in the 'filterText' input field and verifies that the list is reduced to a single item.
|
||||
'Bees' in the input field with ng-model='filterText' and verifies that the list is reduced to a single item.
|
||||
|
||||
The API section below lists the available commands and expectations for the Runner.
|
||||
|
||||
|
|
@ -134,10 +143,10 @@ Returns an array with the values in the column with the given `binding` in the r
|
|||
the given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## select(name).option(value)
|
||||
Picks the option with the given `value` on the select with the given `name`.
|
||||
Picks the option with the given `value` on the select with the given ng-model `name`.
|
||||
|
||||
## select(name).options(value1, value2...)
|
||||
Picks the options with the given `values` on the multi select with the given `name`.
|
||||
Picks the options with the given `values` on the multi select with the given ng-model `name`.
|
||||
|
||||
## element(selector, label).count()
|
||||
Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
|
||||
|
|
@ -173,11 +182,6 @@ Executes the `method` passing in `key` and `value` on the element matching the g
|
|||
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
|
||||
`label` is used for test output.
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
angular which makes testing your angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
# Matchers
|
||||
|
||||
Matchers are used in combination with the `expect(...)` function as described above and can
|
||||
|
|
@ -275,7 +279,7 @@ beforeEach(function () {
|
|||
|
||||
In order to understand what is happening, we should emphasize that ngScenario calls are not
|
||||
immediately executed, but queued (in ngScenario terms, we would be talking about adding
|
||||
future actions). If we had only one entry in our table, than the following future actions
|
||||
future actions). If we had only one entry in our table, then the following future actions
|
||||
would be queued:
|
||||
|
||||
<pre>
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular
|
||||
@description
|
||||
|
||||
While Model-View-Controller (MVC) has acquired different shades of meaning over the years since it
|
||||
first appeared, Angular incorporates the basic principles behind the original {@link
|
||||
http://en.wikipedia.org/wiki/Model–view–controller MVC} software design pattern into its way of
|
||||
building client-side web applications.
|
||||
|
||||
The MVC pattern summarized:
|
||||
|
||||
* Separate applications into distinct presentation, data, and logic components
|
||||
* Encourage loose coupling between these components
|
||||
|
||||
Along with {@link dev_guide.services services} and {@link di dependency injection}, MVC
|
||||
makes angular applications better structured, easier to maintain and more testable.
|
||||
|
||||
The following topics explain how angular incorporates the MVC pattern into the angular way of
|
||||
developing web applications:
|
||||
|
||||
* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
|
||||
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
|
||||
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
|
||||
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the Controller Component
|
||||
@description
|
||||
|
||||
In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular {@link scope Scope}, excluding the root scope.
|
||||
|
||||
Use controllers to:
|
||||
|
||||
- Set up the initial state of a scope object.
|
||||
- Add behavior to the scope object.
|
||||
|
||||
# Setting up the initial state of a scope object
|
||||
|
||||
Typically, when you create an application you need to set up an initial state for an Angular scope.
|
||||
|
||||
Angular applies (in the sense of JavaScript's `Function#apply`) the controller constructor function
|
||||
to a new Angular scope object, which sets up an initial scope state. This means that Angular never
|
||||
creates instances of the controller type (by invoking the `new` operator on the controller
|
||||
constructor). Constructors are always applied to an existing scope object.
|
||||
|
||||
You set up the initial state of a scope by creating model properties. For example:
|
||||
|
||||
<pre>
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
</pre>
|
||||
|
||||
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
|
||||
|
||||
**NOTE**: Many of the examples in the documentation show the creation of functions
|
||||
in the global scope. This is only for demonstration purposes - in a real
|
||||
application you should use the `.controller` method of your Angular module for
|
||||
your application as follows:
|
||||
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
Note also that we use the array notation to explicitly specify the dependency
|
||||
of the controller on the `$scope` service provided by Angular.
|
||||
|
||||
# Adding Behavior to a Scope Object
|
||||
|
||||
Behavior on an Angular scope object is in the form of scope method properties available to the
|
||||
template/view. This behavior interacts with and modifies the application model.
|
||||
|
||||
As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any
|
||||
objects (or primitives) assigned to the scope become model properties. Any functions assigned to
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and `ng` event handler directives (e.g. {@link api/ng.directive:ngClick ngClick}).
|
||||
|
||||
# Using Controllers Correctly
|
||||
|
||||
In general, a controller shouldn't try to do too much. It should contain only the business logic
|
||||
needed for a single view.
|
||||
|
||||
The most common way to keep controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in controllers via dependency injection.
|
||||
This is discussed in the {@link di Dependency Injection} {@link dev_guide.services
|
||||
Services} sections of this guide.
|
||||
|
||||
Do not use controllers for:
|
||||
|
||||
- Any kind of DOM manipulation — Controllers should contain only business logic. DOM
|
||||
manipulation—the presentation logic of an application—is well known for being hard to test.
|
||||
Putting any presentation logic into controllers significantly affects testability of the business
|
||||
logic. Angular offers {@link dev_guide.templates.databinding databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
|
||||
{@link guide/directive directives}.
|
||||
- Input formatting — Use {@link forms angular form controls} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- Sharing stateless or stateful code across controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
- Managing the life-cycle of other components (for example, to create service instances).
|
||||
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
You can associate controllers with scope objects implicitly via the {@link api/ng.directive:ngController ngController
|
||||
directive} or {@link api/ngRoute.$route $route service}.
|
||||
|
||||
|
||||
## Controller Constructor and Methods Example
|
||||
|
||||
To illustrate how the controller component works in angular, let's create a little app with the
|
||||
following components:
|
||||
|
||||
- A {@link dev_guide.templates template} with two buttons and a simple message
|
||||
- A model consisting of a string named `spice`
|
||||
- A controller with two functions that set the value of `spice`
|
||||
|
||||
The message in our template contains a binding to the `spice` model, which by default is set to the
|
||||
string "very". Depending on which button is clicked, the `spice` model is set to `chili` or
|
||||
`jalapeño`, and the message is automatically updated by data-binding.
|
||||
|
||||
|
||||
## A Spicy Controller Example
|
||||
|
||||
<pre>
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.chiliSpicy = function() {
|
||||
$scope.spice = 'chili';
|
||||
}
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</pre>
|
||||
|
||||
Things to notice in the example above:
|
||||
|
||||
- The `ngController` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` controller.
|
||||
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method)
|
||||
- Both controller methods are available in the template (for the `body` element and and its
|
||||
children).
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) allowed you to use `this` interchangeably with
|
||||
the $scope method, but this is no longer the case. Inside of methods defined on the scope
|
||||
`this` and $scope are interchangeable (angular sets `this` to $scope), but not otherwise
|
||||
inside your controller constructor.
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) added prototype methods into the scope
|
||||
automatically, but this is no longer the case; all methods need to be added manually to
|
||||
the scope.
|
||||
|
||||
|
||||
Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.
|
||||
|
||||
## Controller Method Arguments Example
|
||||
|
||||
<pre>
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<input ng-model="customSpice" value="wasabi">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.spicy = function(spice) {
|
||||
$scope.spice = spice;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Notice that the `SpicyCtrl` controller now defines just one method called `spicy`, which takes one
|
||||
argument called `spice`. The template then refers to this controller method and passes in a string
|
||||
constant `'chili'` in the binding for the first button and a model property `spice` (bound to an
|
||||
input box) in the second button.
|
||||
|
||||
|
||||
## Controller Inheritance Example
|
||||
|
||||
Controller inheritance in Angular is based on {@link api/ng.$rootScope.Scope Scope} inheritance. Let's
|
||||
have a look at an example:
|
||||
|
||||
<pre>
|
||||
<body ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
function MainCtrl($scope) {
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}
|
||||
|
||||
function ChildCtrl($scope) {
|
||||
$scope.name = 'Mattie';
|
||||
}
|
||||
|
||||
function BabyCtrl($scope) {
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}
|
||||
</pre>
|
||||
|
||||
Notice how we nested three `ngController` directives in our template. This template construct will
|
||||
result in 4 scopes being created for our view:
|
||||
|
||||
- The root scope
|
||||
- The `MainCtrl` scope, which contains `timeOfDay` and `name` models
|
||||
- The `ChildCtrl` scope, which shadows the `name` model from the previous scope and inherits the
|
||||
`timeOfDay` model
|
||||
- The `BabyCtrl` scope, which shadows both the `timeOfDay` model defined in `MainCtrl` and `name`
|
||||
model defined in the ChildCtrl
|
||||
|
||||
Inheritance works between controllers in the same way as it does with models. So in our previous
|
||||
examples, all of the models could be replaced with controller methods that return string values.
|
||||
|
||||
Note: Standard prototypical inheritance between two controllers doesn't work as one might expect,
|
||||
because as we mentioned earlier, controllers are not instantiated directly by Angular, but rather
|
||||
are applied to the scope object.
|
||||
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
Although there are many ways to test a controller, one of the best conventions, shown below,
|
||||
involves injecting the `$rootScope` and `$controller`
|
||||
|
||||
Controller Function:
|
||||
<pre>
|
||||
function myController($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
|
||||
$scope.spice = "habanero";
|
||||
}
|
||||
</pre>
|
||||
|
||||
Controller Test:
|
||||
<pre>
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function() {
|
||||
var scope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
var ctrl = $controller(myController, {$scope: scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
If you need to test a nested controller you need to create the same scope hierarchy
|
||||
in your test that exists in the DOM.
|
||||
|
||||
<pre>
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, babyScope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = childScope.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
expect(mainScope.timeOfDay).toBe('morning');
|
||||
expect(mainScope.name).toBe('Nikki');
|
||||
expect(childScope.timeOfDay).toBe('morning');
|
||||
expect(childScope.name).toBe('Mattie');
|
||||
expect(babyScope.timeOfDay).toBe('evening');
|
||||
expect(babyScope.name).toBe('Gingerbreak Baby');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.mvc About MVC in Angular}
|
||||
* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
|
||||
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
|
||||
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the Model Component
|
||||
@description
|
||||
|
||||
Depending on the context of the discussion in the Angular documentation, the term _model_ can refer to
|
||||
either a single object representing one entity (for example, a model called "phones" with its value
|
||||
being an array of phones) or the entire data model for the application (all entities).
|
||||
|
||||
In Angular, a model is any data that is reachable as a property of an angular {@link
|
||||
scope Scope} object. The name of the property is the model identifier and the value is
|
||||
any JavaScript object (including arrays and primitives).
|
||||
|
||||
The only requirement for a JavaScript object to be a model in Angular is that the object must be
|
||||
referenced by an Angular scope as a property of that scope object. This property reference can be
|
||||
created explicitly or implicitly.
|
||||
|
||||
You can create models by explicitly creating scope properties referencing JavaScript objects in the
|
||||
following ways:
|
||||
|
||||
* Make a direct property assignment to the scope object in JavaScript code; this most commonly
|
||||
occurs in controllers:
|
||||
|
||||
function MyCtrl($scope) {
|
||||
// create property 'foo' on the MyCtrl's scope
|
||||
// and assign it an initial value 'bar'
|
||||
$scope.foo = 'bar';
|
||||
}
|
||||
|
||||
* Use an {@link expression angular expression} with an assignment operator in templates:
|
||||
|
||||
<button ng-click="{{foo='bar'}}">Click me</button>
|
||||
|
||||
* Use {@link api/ng.directive:ngInit ngInit directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
<body ng-init=" foo = 'bar' ">
|
||||
|
||||
Angular creates models implicitly (by creating a scope property and assigning it a suitable value)
|
||||
when processing the following template constructs:
|
||||
|
||||
* Form input, select, textarea and other form elements:
|
||||
|
||||
<input ng-model="query" value="fluffy cloud">
|
||||
|
||||
The code above creates a model called "query" on the current scope with the value set to "fluffy
|
||||
cloud".
|
||||
|
||||
* An iterator declaration in {@link api/ng.directive:ngRepeat ngRepeater}:
|
||||
|
||||
<p ng-repeat="phone in phones"></p>
|
||||
|
||||
The code above creates one child scope for each item in the "phones" array and creates a "phone"
|
||||
object (model) on each of these scopes with its value set to the value of "phone" in the array.
|
||||
|
||||
In Angular, a JavaScript object stops being a model when:
|
||||
|
||||
* No Angular scope contains a property that references the object.
|
||||
|
||||
* All Angular scopes that contain a property referencing the object become stale and eligible for
|
||||
garbage collection.
|
||||
|
||||
The following illustration shows a simple data model created implicitly from a simple template:
|
||||
|
||||
<img src="img/guide/about_model_final.png">
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.mvc About MVC in Angular}
|
||||
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
|
||||
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the View Component
|
||||
@description
|
||||
|
||||
In Angular, the view is the DOM loaded and rendered in the browser, after Angular has transformed
|
||||
the DOM based on information in the template, controller and model.
|
||||
|
||||
<img src="img/guide/about_view_final.png">
|
||||
|
||||
In the Angular implementation of MVC, the view has knowledge of both the model and the controller.
|
||||
The view knows about the model where two-way data-binding occurs. The view has knowledge of the
|
||||
controller through Angular directives, such as {@link api/ng.directive:ngController
|
||||
ngController} and {@link api/ngRoute.directive:ngView ngView}, and through bindings of this form:
|
||||
`{{someControllerFunction()}}`. In these ways, the view can call functions in an associated
|
||||
controller function.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.mvc About MVC in Angular}
|
||||
* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
|
||||
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
|
||||
|
|
@ -14,10 +14,10 @@ changes to $location are reflected into the browser address bar.
|
|||
- Exposes the current URL in the browser address bar, so you can
|
||||
- Watch and observe the URL.
|
||||
- Change the URL.
|
||||
- Synchronizes the URL with the browser when the user
|
||||
- Changes the address bar.
|
||||
- Clicks the back or forward button (or clicks a History link).
|
||||
- Clicks on a link.
|
||||
- Maintains synchronization between itself and the browser's URL when the user
|
||||
- Changes the address in the browser's address bar.
|
||||
- Clicks the back or forward button in the browser (or clicks a History link).
|
||||
- Clicks on a link in the page.
|
||||
- Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
|
||||
|
||||
|
||||
|
|
@ -121,6 +121,8 @@ All of the setter methods return the same `$location` object to allow chaining.
|
|||
change multiple segments in one go, chain setters like this:
|
||||
<pre>$location.path('/newValue').search({key: value});</pre>
|
||||
|
||||
## Replace method
|
||||
|
||||
There is a special `replace` method which can be used to tell the $location service that the next
|
||||
time the $location service is synced with the browser, the last history record should be replaced
|
||||
instead of creating a new one. This is useful when you want to implement redirection, which would
|
||||
|
|
@ -215,19 +217,19 @@ it('should show example', inject(
|
|||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
// open http://host.com/base/index.html#!/a
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/a'
|
||||
// open http://example.com/base/index.html#!/a
|
||||
$location.absUrl() == 'http://example.com/base/index.html#!/a'
|
||||
$location.path() == '/a'
|
||||
|
||||
$location.path('/foo')
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/foo'
|
||||
$location.absUrl() == 'http://example.com/base/index.html#!/foo'
|
||||
|
||||
$location.search() == {}
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/foo?a=b&c'
|
||||
$location.absUrl() == 'http://example.com/base/index.html#!/foo?a=b&c'
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/new?x=y'
|
||||
$location.absUrl() == 'http://example.com/base/index.html#!/new?x=y'
|
||||
}
|
||||
));
|
||||
</pre>
|
||||
|
|
@ -265,31 +267,31 @@ it('should show example', inject(
|
|||
},
|
||||
function($location) {
|
||||
// in browser with HTML5 history support:
|
||||
// open http://host.com/#!/a -> rewrite to http://host.com/a
|
||||
// (replacing the http://host.com/#!/a history record)
|
||||
// open http://example.com/#!/a -> rewrite to http://example.com/a
|
||||
// (replacing the http://example.com/#!/a history record)
|
||||
$location.path() == '/a'
|
||||
|
||||
$location.path('/foo');
|
||||
$location.absUrl() == 'http://host.com/foo'
|
||||
$location.absUrl() == 'http://example.com/foo'
|
||||
|
||||
$location.search() == {}
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() == 'http://host.com/foo?a=b&c'
|
||||
$location.absUrl() == 'http://example.com/foo?a=b&c'
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.url() == 'new?x=y'
|
||||
$location.absUrl() == 'http://host.com/new?x=y'
|
||||
$location.absUrl() == 'http://example.com/new?x=y'
|
||||
|
||||
// in browser without html5 history support:
|
||||
// open http://host.com/new?x=y -> redirect to http://host.com/#!/new?x=y
|
||||
// (again replacing the http://host.com/new?x=y history item)
|
||||
// open http://example.com/new?x=y -> redirect to http://example.com/#!/new?x=y
|
||||
// (again replacing the http://example.com/new?x=y history item)
|
||||
$location.path() == '/new'
|
||||
$location.search() == {x: 'y'}
|
||||
|
||||
$location.path('/foo/bar');
|
||||
$location.path() == '/foo/bar'
|
||||
$location.url() == '/foo/bar?x=y'
|
||||
$location.absUrl() == 'http://host.com/#!/foo/bar?x=y'
|
||||
$location.absUrl() == 'http://example.com/#!/foo/bar?x=y'
|
||||
}
|
||||
));
|
||||
</pre>
|
||||
|
|
@ -321,6 +323,14 @@ reload to the original link.
|
|||
Example: `<a href="http://angularjs.org/">link</a>`
|
||||
- Links starting with '/' that lead to a different base path when base is defined<br>
|
||||
Example: `<a href="/not-my-base/link">link</a>`
|
||||
|
||||
When running Angular in the root of a domain, along side perhaps a normal application in the same
|
||||
directory, the "otherwise" route handler will try to handle all the URLs, including ones that map
|
||||
to static files.
|
||||
|
||||
To prevent this, you can set your base href for the app to `<base href=".">` and then prefix links
|
||||
to URLs that should be handled with `.`. Now, links to locations, which are not to be routed by Angular,
|
||||
are not prefixed with `.` and will not be intercepted by the `otherwise` rule in your `$routeProvider`.
|
||||
|
||||
|
||||
### Server side
|
||||
|
|
@ -381,8 +391,8 @@ In this examples we use `<base href="/base/index.html" />`
|
|||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -395,8 +405,8 @@ In this examples we use `<base href="/base/index.html" />`
|
|||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -423,8 +433,8 @@ In this examples we use `<base href="/base/index.html" />`
|
|||
}
|
||||
|
||||
var browsers = {
|
||||
html5: new FakeBrowser('http://www.host.com/base/path?a=b#h', '/base/index.html'),
|
||||
hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
html5: new FakeBrowser('http://www.example.com/base/path?a=b#h', '/base/index.html'),
|
||||
hashbang: new FakeBrowser('http://www.example.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
|
||||
function Html5Cntl($scope, $location) {
|
||||
|
|
@ -437,17 +447,19 @@ In this examples we use `<base href="/base/index.html" />`
|
|||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
// We must kill a link to the injector for this element otherwise angular will
|
||||
// complain that it has been bootstrapped already.
|
||||
root.data('$injector', null);
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$document', root);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
|
||||
$compileProvider.directive('ngAddressBar', function() {
|
||||
return function(scope, elm, attrs) {
|
||||
var browser = browsers[attrs.browser],
|
||||
input = angular.element('<input type="text">').val(browser.url()),
|
||||
input = angular.element('<input type="text" style="width: 400px">').val(browser.url()),
|
||||
delay;
|
||||
|
||||
input.on('keypress keyup keydown', function() {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ testable.
|
|||
To register a service, you must have a module that this service will be part of. Afterwards, you
|
||||
can register the service with the module either via the {@link api/angular.Module Module api} or
|
||||
by using the {@link api/AUTO.$provide $provide} service in the module configuration
|
||||
function.The following pseudo-code shows both approaches:
|
||||
function. The following pseudo-code shows both approaches:
|
||||
|
||||
Using the angular.Module api:
|
||||
<pre>
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ convention they match the service IDs, which has added benefits discussed below.
|
|||
|
||||
<pre>
|
||||
function myController($loc, $log) {
|
||||
this.firstMethod = function() {
|
||||
// use $location service
|
||||
$loc.setHash();
|
||||
};
|
||||
this.secondMethod = function() {
|
||||
// use $log service
|
||||
$log.info('...');
|
||||
};
|
||||
this.firstMethod = function() {
|
||||
// use $location service
|
||||
$loc.setHash();
|
||||
};
|
||||
this.secondMethod = function() {
|
||||
// use $log service
|
||||
$log.info('...');
|
||||
};
|
||||
}
|
||||
// which services to inject ?
|
||||
myController.$inject = ['$location', '$log'];
|
||||
|
|
@ -53,18 +53,19 @@ function myController(scope, notifyService) {
|
|||
myController.$inject = ['$scope','notify'];
|
||||
</script>
|
||||
|
||||
<div ng-controller="myController">
|
||||
<div id="simple" ng-controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message" >
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
<p>(you have to click 3 times to see an alert)</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should test service', function() {
|
||||
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
|
||||
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
|
||||
.toEqual('test');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
## Implicit Dependency Injection
|
||||
|
|
@ -95,7 +96,7 @@ function myController($scope, notify) {
|
|||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="myController">
|
||||
<div id="implicit" ng-controller="myController">
|
||||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message">
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
|
|
|
|||
|
|
@ -50,19 +50,18 @@ of which depend on other services that are provided by the Angular framework:
|
|||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
function batchLogModule($provide){
|
||||
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
|
||||
$provide.factory('batchLog', ['$interval', '$log', function($interval, $log) {
|
||||
var messageQueue = [];
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
$log.log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$timeout(log, 50000);
|
||||
}
|
||||
|
||||
// start periodic checking
|
||||
log();
|
||||
$interval(log, 50000);
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
|
|
@ -82,13 +81,13 @@ of which depend on other services that are provided by the Angular framework:
|
|||
}]);
|
||||
}
|
||||
|
||||
// get the main service to kick of the application
|
||||
// get the main service to kick off the application
|
||||
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
||||
</pre>
|
||||
|
||||
Things to notice in this example:
|
||||
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$timeout $timeout} and
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$interval $interval} and
|
||||
{@link api/ng.$log $log} services, and allows messages to be logged into the
|
||||
`console.log` in batches.
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link api/ngRoute.$route
|
||||
|
|
|
|||
|
|
@ -2,26 +2,50 @@
|
|||
@name Developer Guide: Angular Services: Understanding Angular Services
|
||||
@description
|
||||
|
||||
Angular services are singletons that carry out specific tasks common to web apps, such as the
|
||||
{@link api/ng.$http $http service} that provides low level access to the browser's
|
||||
`XMLHttpRequest` object.
|
||||
## What are Angular Services?
|
||||
|
||||
To use an Angular service, you identify it as a dependency for the dependent (a controller, or
|
||||
another service) that depends on the service. Angular's dependency injection subsystem takes care
|
||||
of the rest. The Angular injector subsystem is in charge of service instantiation, resolution of
|
||||
dependencies, and provision of dependencies to factory functions as requested.
|
||||
|
||||
Angular injects dependencies using "constructor" injection (the service is passed in via a factory
|
||||
function). Because JavaScript is a dynamically typed language, Angular's dependency injection
|
||||
subsystem cannot use static types to identify service dependencies. For this reason a dependent
|
||||
must explicitly define its dependencies by using the `$inject` property. For example:
|
||||
|
||||
myController.$inject = ['$location'];
|
||||
|
||||
The Angular web framework provides a set of services for common operations. Like other core Angular
|
||||
variables and identifiers, the built-in services always start with `$` (such as `$http` mentioned
|
||||
Angular services are singletons objects or functions that carry out specific tasks common to web apps.
|
||||
Angular has a number of built in services, such as the {@link api/ng.$http $http service}, which
|
||||
provides access to the browser's `XMLHttpRequest` object for making requests to a server. Like other core
|
||||
Angular variables and identifiers, the built-in services always start with `$` (such as `$http` mentioned
|
||||
above). You can also create your own custom services.
|
||||
|
||||
## Using a Service
|
||||
|
||||
To use an Angular service, you identify it as a dependency for the component (controller, service,
|
||||
filter or directive) that depends on the service. Angular's dependency injection subsystem takes
|
||||
care of the rest. The Angular injector subsystem is in charge of service instantiation, resolution
|
||||
of dependencies, and provision of dependencies to components as requested.
|
||||
|
||||
Angular injects dependencies using
|
||||
{@link http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/ "constructor" injection}.
|
||||
The dependency is passed to the component's factory/constructor function. Because JavaScript is a dynamically
|
||||
typed language, Angular's dependency injection subsystem cannot use static types to identify service
|
||||
dependencies. For this reason a component must, explicitly, define its dependencies by using one of the
|
||||
{@link di injection annotation} methods. For example, by providing a `$inject` property:
|
||||
|
||||
var MyController = function($location) { ... };
|
||||
MyController.$inject = ['$location'];
|
||||
myModule.controller('MyController', MyController);
|
||||
|
||||
Or by providing an "inline" injection annotation:
|
||||
|
||||
var myService = function($http) { ... };
|
||||
myModule.factory('myService', ['$http', myService]);
|
||||
|
||||
## Defining a Service
|
||||
|
||||
Application developers are free to define their own services by registering their name, and **service
|
||||
factory function**, in Angular modules.
|
||||
|
||||
The purpose of the **service factory function** is to generate the single object, or function, that
|
||||
represents the service to the rest of the application. That object, or function, will then be
|
||||
injected into any component (controller, service, filter or directive) that specifies a dependency
|
||||
on the service.
|
||||
|
||||
Angular factory functions are executed lazily. That is, they are only executed when needed
|
||||
to satisfy a dependency, and are then executed exactly once for each service. Everything that is
|
||||
dependent on this service gets a reference to the single instance generated by the service factory.
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,17 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
|||
|
||||
# CSS classes used by angular
|
||||
|
||||
* `ng-scope`
|
||||
- **Usage:** angular applies this class to any element that where a new {@link api/ng.$rootScope.Scope scope}
|
||||
is defined. (see {@link guide/scope scope} guide for more information about scopes)
|
||||
|
||||
* `ng-binding`
|
||||
- **Usage:** angular applies this class to any element that is attached to a data binding, via `ng-bind` or
|
||||
`{{}}` curly braces, for example. (see {@link guide/databinding databinding} guide)
|
||||
|
||||
* `ng-invalid`, `ng-valid`
|
||||
- **Usage:** angular applies this class to an input widget element if that element's input does
|
||||
not pass validation. (see {@link api/ng.directive:input input} directive).
|
||||
not pass validation. (see {@link api/ng.directive:input input} directive)
|
||||
|
||||
* `ng-pristine`, `ng-dirty`
|
||||
- **Usage:** angular {@link api/ng.directive:input input} directive applies `ng-pristine` class
|
||||
|
|
@ -19,5 +27,5 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
|||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link templates Angular Templates}
|
||||
* {@link forms Angular Forms}
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Filters: Creating Angular Filters
|
||||
@description
|
||||
|
||||
Writing your own filter is very easy: just register a new filter (injectable) factory function with
|
||||
your module. This factory function should return a new filter function which takes the input value
|
||||
as the first argument. Any filter arguments are passed in as additional arguments to the filter
|
||||
function.
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case.
|
||||
|
||||
<doc:example module="MyReverseModule">
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.module('MyReverseModule', []).
|
||||
filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
// conditional based on optional argument
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
function Ctrl($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div ng-controller="Ctrl">
|
||||
<input ng-model="greeting" type="greeting"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should reverse greeting', function() {
|
||||
expect(binding('greeting|reverse')).toEqual('olleh');
|
||||
input('greeting').enter('ABC');
|
||||
expect(binding('greeting|reverse')).toEqual('CBA');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link compiler Angular HTML Compiler}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/ng.$filter Angular Filter API}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
|
||||
Angular filters format data for display to the user.
|
||||
|
||||
For example, you might have a data object that needs to be formatted according to the locale before
|
||||
displaying it to the user. You can pass expressions through a chain of filters like this:
|
||||
|
||||
name | uppercase
|
||||
|
||||
The expression evaluator simply passes the value of name to
|
||||
{@link api/ng.filter:uppercase uppercase filter}.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters.using_filters Using Angular Filters}
|
||||
* {@link dev_guide.templates.filters.creating_filters Creating Angular Filters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/ng.$filter Angular Filter API}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Filters: Using Angular Filters
|
||||
@description
|
||||
|
||||
Filters can be part of any {@link api/ng.$rootScope.Scope} evaluation but are typically used to format
|
||||
expressions in bindings in your templates:
|
||||
|
||||
{{ expression | filter }}
|
||||
|
||||
Filters typically transform the data to a new data type, formatting the data in the process.
|
||||
Filters can also be chained, and can take optional arguments.
|
||||
|
||||
You can chain filters using this syntax:
|
||||
|
||||
{{ expression | filter1 | filter2 }}
|
||||
|
||||
You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
|
||||
2 decimal points:
|
||||
|
||||
123 | number:2
|
||||
|
||||
Use the same syntax for multiple arguments:
|
||||
|
||||
myArray | orderBy:'timestamp':true
|
||||
|
||||
Here are some examples that show values before and after applying different filters to an
|
||||
expression in a binding:
|
||||
|
||||
* No filter: `{{1234.5678}}` => `1234.5678`
|
||||
* Number filter: `{{1234.5678|number}}` => `1,234.57`. Notice the "," and rounding to two
|
||||
significant digits.
|
||||
* Filter with arguments: `{{1234.5678|number:5}}` => `1,234.56780`. Filters can take optional
|
||||
arguments, separated by colons in a binding. For example, the "number" filter takes a number
|
||||
argument that specifies how many digits to display to the right of the decimal point.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.templates.filters.creating_filters Creating Angular Filters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/ng.$filter Angular Filter API}
|
||||
|
|
@ -13,45 +13,44 @@ Unit testing as the name implies is about testing individual units of code. Unit
|
|||
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list
|
||||
in the right order?"
|
||||
|
||||
In order to answer such question it is very important that we can isolate the unit of code under test.
|
||||
In order to answer such a question it is very important that we can isolate the unit of code under test.
|
||||
That is because when we are testing the sort function we don't want to be forced into creating
|
||||
related pieces such as the DOM elements, or making any XHR calls in getting the data to sort.
|
||||
|
||||
While
|
||||
this may seem obvious it usually is very difficult to be able to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns, and they end up with a
|
||||
piece of code which does everything. It reads the data from XHR, it sorts it and then it
|
||||
While this may seem obvious it can be very difficult to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns resulting in a
|
||||
piece of code which does everything. It makes an XHR request, it sorts the response data and then it
|
||||
manipulates the DOM.
|
||||
|
||||
With Angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstraction which
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstractions which
|
||||
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
|
||||
it is easy to write a sort function which sorts some data, so that your test can create a data set,
|
||||
apply the function, and assert that the resulting model is in the correct order. The test does not
|
||||
have to wait for XHR, or create the right kind of DOM, or assert that your function has mutated the
|
||||
DOM in the right way.
|
||||
have to wait for the XHR response to arrive, create the right kind of test DOM, nor assert that your
|
||||
function has mutated the DOM in the right way.
|
||||
|
||||
## With great power comes great responsibility
|
||||
|
||||
Angular is written with testability in mind, but it still requires that you
|
||||
do the right thing. We tried to make the right thing easy, but Angular is not magic, which means if
|
||||
you don't follow these guidelines you may very well end up with an untestable application.
|
||||
Angular is written with testability in mind, but it still requires that you do the right thing.
|
||||
We tried to make the right thing easy, but Angular is not magic. If you don't follow these guidelines
|
||||
you may very well end up with an untestable application.
|
||||
|
||||
## Dependency Injection
|
||||
There are several ways in which you can get a hold of a dependency:
|
||||
1. You could create it using the `new` operator.
|
||||
2. You could look for it in a well known place, also known as global singleton.
|
||||
3. You could ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
the registry? Most likely by looking it up in a well known place. See #2)
|
||||
4. You could expect that it be handed to you.
|
||||
There are several ways in which you can get a hold of a dependency. You can:
|
||||
1. Create it using the `new` operator.
|
||||
2. Look for it in a well-known place, also known as a global singleton.
|
||||
3. Ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
the registry? Most likely by looking it up in a well known place. See #2.)
|
||||
4. Expect it to be handed to you.
|
||||
|
||||
Out of the four options in the list above, only the last one is testable. Let's look at why:
|
||||
|
||||
### Using the `new` operator
|
||||
|
||||
While there is nothing wrong with the `new` operator fundamentally the issue is that calling a new
|
||||
on a constructor permanently binds the call site to the type. For example lets say that we are
|
||||
trying to instantiate an `XHR` so that we can get some data from the server.
|
||||
While there is nothing wrong with the `new` operator fundamentally, a problem arises when calling `new`
|
||||
on a constructor. This permanently binds the call site to the type. For example, lets say that we try to
|
||||
instantiate an `XHR` that will retrieve data from the server.
|
||||
|
||||
<pre>
|
||||
function MyClass() {
|
||||
|
|
@ -64,12 +63,12 @@ function MyClass() {
|
|||
}
|
||||
</pre>
|
||||
|
||||
The issue becomes that in tests, we would very much like to instantiate a `MockXHR` which would
|
||||
A problem surfaces in tests when we would like to instantiate a `MockXHR` that would
|
||||
allow us to return fake data and simulate network failures. By calling `new XHR()` we are
|
||||
permanently bound to the actual XHR, and there is no good way to replace it. Yes there is monkey
|
||||
patching. That is a bad idea for many reasons which are outside the scope of this document.
|
||||
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
|
||||
patch, but that is a bad idea for many reasons which are outside the scope of this document.
|
||||
|
||||
The class above is hard to test since we have to resort to monkey patching:
|
||||
Here's an example of how the class above becomes hard to test when resorting to monkey patching:
|
||||
<pre>
|
||||
var oldXHR = XHR;
|
||||
XHR = function MockXHR() {};
|
||||
|
|
@ -81,7 +80,7 @@ XHR = oldXHR; // if you forget this bad things will happen
|
|||
|
||||
|
||||
### Global look-up:
|
||||
Another way to approach the problem is to look for the service in a well known location.
|
||||
Another way to approach the problem is to look for the service in a well-known location.
|
||||
|
||||
<pre>
|
||||
function MyClass() {
|
||||
|
|
@ -95,14 +94,14 @@ function MyClass() {
|
|||
}
|
||||
</pre>
|
||||
|
||||
While no new instance of the dependency is being created, it is fundamentally the same as `new`, in
|
||||
that there is no good way to intercept the call to `global.xhr` for testing purposes, other then
|
||||
While no new dependency instance is created, it is fundamentally the same as `new` in
|
||||
that no way exists to intercept the call to `global.xhr` for testing purposes, other then
|
||||
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
|
||||
order to replace it with call to a mock method. For further explanation why this is bad see: {@link
|
||||
order to replace it with call to a mock method. For further explanation of why this is bad see: {@link
|
||||
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
|
||||
State & Singletons}
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
The class above is hard to test since we have to change the global state:
|
||||
<pre>
|
||||
var oldXHR = global.xhr;
|
||||
global.xhr = function mockXHR() {};
|
||||
|
|
@ -115,7 +114,7 @@ global.xhr = oldXHR; // if you forget this bad things will happen
|
|||
|
||||
### Service Registry:
|
||||
|
||||
It may seem as that this can be solved by having a registry for all of the services, and then
|
||||
It may seem that this can be solved by having a registry of all the services and then
|
||||
having the tests replace the services as needed.
|
||||
|
||||
<pre>
|
||||
|
|
@ -131,12 +130,12 @@ function MyClass() {
|
|||
}
|
||||
</pre>
|
||||
|
||||
However, where does the serviceRegistry come from? if it is:
|
||||
* `new`-ed up, the the test has no chance to reset the services for testing
|
||||
* global look-up, then the service returned is global as well (but resetting is easier, since
|
||||
there is only one global variable to be reset).
|
||||
However, where does the serviceRegistry come from? If it is:
|
||||
* `new`-ed up, the test has no chance to reset the services for testing.
|
||||
* a global look-up then the service returned is global as well (but resetting is easier, since
|
||||
only one global variable exists to be reset).
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
The class above is hard to test since we have to change the global state:
|
||||
<pre>
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
global.serviceLocator.set('xhr', function mockXHR() {});
|
||||
|
|
@ -148,7 +147,7 @@ global.serviceLocator = oldServiceLocator; // if you forget this bad things will
|
|||
|
||||
|
||||
### Passing in Dependencies:
|
||||
Lastly the dependency can be passed in.
|
||||
Last, the dependency can be passed in.
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
|
|
@ -161,12 +160,12 @@ function MyClass(xhr) {
|
|||
}
|
||||
</pre>
|
||||
|
||||
This is the preferred way since the code makes no assumptions as to where the `xhr` comes from,
|
||||
rather that whoever created the class was responsible for passing it in. Since the creator of the
|
||||
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
|
||||
instead about whoever created the class responsible for passing it in. Since the creator of the
|
||||
class should be different code than the user of the class, it separates the responsibility of
|
||||
creation from the logic, and that is what dependency-injection is in a nutshell.
|
||||
creation from the logic. This is dependency-injection in a nutshell.
|
||||
|
||||
The class above is very testable, since in the test we can write:
|
||||
The class above is testable, since in the test we can write:
|
||||
<pre>
|
||||
function xhrMock(args) {...}
|
||||
var myClass = new MyClass(xhrMock);
|
||||
|
|
@ -176,12 +175,12 @@ myClass.doWork();
|
|||
|
||||
Notice that no global variables were harmed in the writing of this test.
|
||||
|
||||
Angular comes with {@link di dependency injection} built in which makes the right thing
|
||||
Angular comes with {@link di dependency injection} built-in, making the right thing
|
||||
easy to do, but you still need to do it if you wish to take advantage of the testability story.
|
||||
|
||||
## Controllers
|
||||
What makes each application unique is its logic, which is what we would like to test. If the logic
|
||||
for your application is mixed in with DOM manipulation, it will be hard to test as in the example
|
||||
What makes each application unique is its logic, and the logic is what we would like to test. If the logic
|
||||
for your application contains DOM manipulation, it will be hard to test. See the example
|
||||
below:
|
||||
|
||||
<pre>
|
||||
|
|
@ -209,7 +208,7 @@ function PasswordCtrl() {
|
|||
}
|
||||
</pre>
|
||||
|
||||
The code above is problematic from a testability point of view, since it requires your test to have the right kind
|
||||
The code above is problematic from a testability point of view since it requires your test to have the right kind
|
||||
of DOM present when the code executes. The test would look like this:
|
||||
|
||||
<pre>
|
||||
|
|
@ -223,11 +222,11 @@ var pc = new PasswordCtrl();
|
|||
input.val('abc');
|
||||
pc.grade();
|
||||
expect(span.text()).toEqual('weak');
|
||||
$('body').html('');
|
||||
$('body').empty();
|
||||
</pre>
|
||||
|
||||
In angular the controllers are strictly separated from the DOM manipulation logic which results in
|
||||
a much easier testability story as can be seen in this example:
|
||||
In angular the controllers are strictly separated from the DOM manipulation logic and this results in
|
||||
a much easier testability story as the following example shows:
|
||||
|
||||
<pre>
|
||||
function PasswordCtrl($scope) {
|
||||
|
|
@ -245,7 +244,7 @@ function PasswordCtrl($scope) {
|
|||
}
|
||||
</pre>
|
||||
|
||||
and the test is straight forward
|
||||
and the test is straight forward:
|
||||
|
||||
<pre>
|
||||
var $scope = {};
|
||||
|
|
@ -255,11 +254,11 @@ $scope.grade();
|
|||
expect($scope.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
Notice that the test is not only much shorter but it is easier to follow what is going on. We say
|
||||
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.
|
||||
|
||||
## Filters
|
||||
{@link api/ng.$filter Filters} are functions which transform the data into user readable
|
||||
{@link api/ng.$filterProvider Filters} are functions which transform the data into a user readable
|
||||
format. They are important because they remove the formatting responsibility from the application
|
||||
logic, further simplifying the application logic.
|
||||
|
||||
|
|
@ -282,7 +281,7 @@ you create with directives may be used throughout your application and in many d
|
|||
|
||||
### Simple HTML Element Directive
|
||||
|
||||
Lets start with an angular app with no dependencies.
|
||||
Let's start with an angular app with no dependencies.
|
||||
|
||||
<pre>
|
||||
var app = angular.module('myApp', []);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ is simply handed the `greeter` at runtime.
|
|||
This is desirable, but it puts the responsibility of getting hold of the dependency on the
|
||||
code that constructs `SomeClass`.
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png">
|
||||
|
||||
To manage the responsibility of dependency creation, each Angular application has an {@link
|
||||
api/angular.injector injector}. The injector is a service locator that is responsible for
|
||||
construction and lookup of dependencies.
|
||||
|
|
@ -144,9 +146,11 @@ of service names to inject.
|
|||
var MyController = function(renamed$scope, renamedGreeter) {
|
||||
...
|
||||
}
|
||||
MyController.$inject = ['$scope', 'greeter'];
|
||||
MyController['$inject'] = ['$scope', 'greeter'];
|
||||
</pre>
|
||||
|
||||
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
|
||||
Using above code snippet as an example, '$scope' will be injected into 'renamed$scope' and 'greeter' into 'renamedGreeter'.
|
||||
Care must be taken that the `$inject` annotation is kept in sync with the actual arguments in the
|
||||
function declaration.
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,12 +4,11 @@
|
|||
|
||||
Expressions are JavaScript-like code snippets that are usually placed in bindings such as `{{
|
||||
expression }}`. Expressions are processed by the {@link api/ng.$parse $parse}
|
||||
service.
|
||||
service. Expressions are often post processed using {@link filter filters} to create a more user-friendly format.
|
||||
|
||||
For example, these are all valid expressions in angular:
|
||||
|
||||
* `1+2`
|
||||
* `3*10 | currency`
|
||||
* `user.name`
|
||||
|
||||
|
||||
|
|
@ -19,7 +18,7 @@ It might be tempting to think of Angular view expressions as JavaScript expressi
|
|||
not entirely correct, since Angular does not use a JavaScript `eval()` to evaluate expressions.
|
||||
You can think of Angular expressions as JavaScript expressions with following differences:
|
||||
|
||||
* **Attribute Evaluation:** evaluation of all properties are against the scope, doing the
|
||||
* **Attribute Evaluation:** evaluation of all properties are against the scope doing the
|
||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||
`window`.
|
||||
|
||||
|
|
@ -29,23 +28,20 @@ You can think of Angular expressions as JavaScript expressions with following di
|
|||
* **No Control Flow Statements:** you cannot do any of the following in angular expression:
|
||||
conditionals, loops, or throw.
|
||||
|
||||
* **Filters:** you can pass result of expression evaluations through filter chains. For example
|
||||
to convert date object into a local specific human-readable format.
|
||||
|
||||
If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a
|
||||
controller method and call the method. If you want to `eval()` an angular expression from
|
||||
JavaScript, use the {@link api/ng.$rootScope.Scope#$eval `$eval()`} method.
|
||||
JavaScript, use the {@link api/ng.$rootScope.Scope#methods_$eval `$eval()`} method.
|
||||
|
||||
## Example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
1+2={{1+2}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should calculate expression in binding', function() {
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
You can try evaluating different expressions here:
|
||||
|
|
@ -70,21 +66,21 @@ You can try evaluating different expressions here:
|
|||
<input type='text' ng-model="expr" size="80"/>
|
||||
<button ng-click="addExp(expr)">Evaluate</button>
|
||||
<ul>
|
||||
<li ng-repeat="expr in exprs">
|
||||
<li ng-repeat="expr in exprs track by $index">
|
||||
[ <a href="" ng-click="removeExp($index)">X</a> ]
|
||||
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should allow user expression testing', function() {
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
||||
element(by.css('.expressions button')).click();
|
||||
var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
|
||||
expect(lis.count()).toBe(1);
|
||||
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
|
@ -103,7 +99,7 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
|||
$scope.name = 'World';
|
||||
|
||||
$scope.greet = function() {
|
||||
($window.mockWindow || $window).alert('Hello ' + $scope.name);
|
||||
$window.alert('Hello ' + $scope.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -112,21 +108,17 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
|||
<button ng-click="greet()">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function() {
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
alert: function(text){ alertText = text; }
|
||||
};
|
||||
done();
|
||||
});
|
||||
element(':button:contains(Greet)').click();
|
||||
expect(this.addFuture('alert text', function(done) {
|
||||
done(null, alertText);
|
||||
})).toBe('Hello World');
|
||||
});
|
||||
</doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should calculate expression in binding', function() {
|
||||
element(by.css('[ng-click="greet()"]')).click();
|
||||
|
||||
var alertDialog = browser.switchTo().alert();
|
||||
|
||||
expect(alertDialog.getText()).toEqual('Hello World');
|
||||
|
||||
alertDialog.accept();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
## Forgiving
|
||||
|
|
@ -150,37 +142,3 @@ You cannot write a control flow statement in an expression. The reason behind th
|
|||
Angular philosophy that application logic should be in controllers, not in the view. If you need a
|
||||
conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.
|
||||
|
||||
|
||||
## Filters
|
||||
|
||||
When presenting data to the user, you might need to convert the data from its raw format to a
|
||||
user-friendly format. For example, you might have a data object that needs to be formatted
|
||||
according to the locale before displaying it to the user. You can pass expressions through a chain
|
||||
of filters like this:
|
||||
|
||||
name | uppercase
|
||||
|
||||
The expression evaluator simply passes the value of name to {@link
|
||||
api/ng.filter:uppercase `uppercase`} filter.
|
||||
|
||||
Chain filters using this syntax:
|
||||
|
||||
value | filter1 | filter2
|
||||
|
||||
You can also pass colon-delimited arguments to filters, for example, to display the number 123
|
||||
with 2 decimal points:
|
||||
|
||||
123 | number:2
|
||||
|
||||
# The $
|
||||
|
||||
You might be wondering, what is the significance of the $ prefix? It is simply a prefix that
|
||||
angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating
|
||||
`a.length()` would return undefined because neither a nor angular define such a property.
|
||||
|
||||
Consider that in a future version of Angular we might choose to add a length method, in which case
|
||||
the behavior of the expression would change. Worse yet, you, the developer, could create a length
|
||||
property and then we would have a collision. This problem exists because Angular augments existing
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
so that angular developers and developers who use Angular can develop in harmony without collisions.
|
||||
|
||||
|
|
|
|||
126
docs/content/guide/filter.ngdoc
Normal file
126
docs/content/guide/filter.ngdoc
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
@ngdoc overview
|
||||
@name Filters
|
||||
@description
|
||||
|
||||
A filter formats the value of an expression for display to the user. They can be used in view templates,
|
||||
controllers or services and it is easy to define your own filter.
|
||||
|
||||
The underlying API is the {@link api/ng.$filterProvider filterProvider}.
|
||||
|
||||
## Using filters in view templates
|
||||
|
||||
Filters can be applied to expressions in view templates using the following syntax:
|
||||
|
||||
{{ expression | filter }}
|
||||
|
||||
E.g. the markup `{{ 12 | currency }}` formats the number 12 as a currency using the {@link api/ng.filter:currency `currency`}
|
||||
filter. The resulting value is `$12.00`.
|
||||
|
||||
Filters can be applied to the result of another filter. This is called "chaining" and uses
|
||||
the following syntax:
|
||||
|
||||
{{ expression | filter1 | filter2 | ... }}
|
||||
|
||||
Filters may have arguments. The syntax for this is
|
||||
|
||||
{{ expression | filter:argument1:argument2:... }}
|
||||
|
||||
E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal points using the
|
||||
{@link api/ng.filter:number `number`} filter. The resulting value is `1,234.00`.
|
||||
|
||||
|
||||
## Using filters in controllers and services
|
||||
|
||||
You can also use filters in controllers and services. For this, add a dependency with the name `<filterName>Filter`
|
||||
to your controller or service. E.g. using the dependency `numberFilter` will inject the number filter.
|
||||
The injected argument is a function that takes the value to format as first argument and filter parameters
|
||||
starting with the second argument.
|
||||
|
||||
The example below uses the filter called {@link api/ng.filter:filter `filter`}.
|
||||
This filter reduces arrays into sub arrays based on
|
||||
conditions. The filter can be applied in the view template with markup like
|
||||
`{{ctrl.array | filter:'a'}}`, which would do a fulltext search for "a".
|
||||
However, using a filter in a view template will reevaluate the filter on
|
||||
every digest, which can be costly if the array is big.
|
||||
|
||||
The example below therefore calls the filter directly in the controller.
|
||||
By this, the controller is able to call the filter only when needed (e.g. when the data is loaded from the backend
|
||||
or the filter expression is changed).
|
||||
|
||||
<doc:example module="FilterInControllerModule">
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.module('FilterInControllerModule', []).
|
||||
controller('FilterController', ['filterFilter', function(filterFilter) {
|
||||
this.array = [
|
||||
{name: 'Tobias'},
|
||||
{name: 'Jeff'},
|
||||
{name: 'Brian'},
|
||||
{name: 'Igor'},
|
||||
{name: 'James'},
|
||||
{name: 'Brad'}
|
||||
];
|
||||
this.filteredArray = filterFilter(this.array, 'a');
|
||||
}]);
|
||||
</script>
|
||||
|
||||
<div ng-controller="FilterController as ctrl">
|
||||
<div>
|
||||
All entries:
|
||||
<span ng-repeat="entry in ctrl.array">{{entry.name}} </span>
|
||||
</div>
|
||||
<div>
|
||||
Entries that contain an "a":
|
||||
<span ng-repeat="entry in ctrl.filteredArray">{{entry.name}} </span>
|
||||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
## Creating custom filters
|
||||
|
||||
Writing your own filter is very easy: just register a new filter factory function with
|
||||
your module. Internally, this uses the {@link api/ng.$filterProvider `filterProvider`}.
|
||||
This factory function should return a new filter function which takes the input value
|
||||
as the first argument. Any filter arguments are passed in as additional arguments to the filter
|
||||
function.
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case.
|
||||
|
||||
<doc:example module="MyReverseModule">
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.module('MyReverseModule', []).
|
||||
filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
// conditional based on optional argument
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
function Ctrl($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div ng-controller="Ctrl">
|
||||
<input ng-model="greeting" type="greeting"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
## Testing custom filters
|
||||
|
||||
See the {@link http://docs.angularjs.org/tutorial/step_09#test phonecat tutorial} for an example.
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
@name Forms
|
||||
@description
|
||||
|
||||
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
|
||||
Form is a collection of controls for the purpose of grouping related controls together.
|
||||
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
|
||||
A Form is a collection of controls for the purpose of grouping related controls together.
|
||||
|
||||
Form and controls provide validation services, so that the user can be notified of invalid input.
|
||||
This provides a better user experience, because the user gets instant feedback on how to correct the error.
|
||||
|
|
@ -33,10 +33,10 @@ In addition it provides an {@link api/ng.directive:ngModel.NgModelController API
|
|||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
|
|
@ -115,9 +115,14 @@ This ensures that the user is not distracted with an error until after interacti
|
|||
|
||||
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
Similarly control is an instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
The control instance can similarly be published into the form instance using the `name` attribute.
|
||||
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.
|
||||
|
||||
Similarly, an input control that has the {@link api/ng.directive:ngModel ngModel} directive holds an
|
||||
instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
Such a control instance can be published as a property of the form instance using the `name` attribute
|
||||
on the input control. The name attribute specifies the name of the property on the form instance.
|
||||
|
||||
This implies that the internal state of both the form and the control is available for binding in
|
||||
the view using the standard binding primitives.
|
||||
|
||||
This allows us to extend the above example with these features:
|
||||
|
||||
|
|
@ -154,10 +159,10 @@ This allows us to extend the above example with these features:
|
|||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
|
|
@ -186,11 +191,11 @@ To get a hold of the controller the directive specifies a dependency as shown in
|
|||
The validation can occur in two places:
|
||||
|
||||
* **Model to View update** -
|
||||
Whenever the bound model changes, all functions in {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters} array are pipe-lined, so that each of these functions has an opportunity to format the value and change validity state of the form control through {@link api/ng.directive:ngModel.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
Whenever the bound model changes, all functions in {@link api/ng.directive:ngModel.NgModelController#properties_$formatters NgModelController#$formatters} array are pipe-lined, so that each of these functions has an opportunity to format the value and change validity state of the form control through {@link api/ng.directive:ngModel.NgModelController#methods_$setValidity NgModelController#$setValidity}.
|
||||
|
||||
* **View to Model update** -
|
||||
In a similar way, whenever a user interacts with a control it calls {@link api/ng.directive:ngModel.NgModelController#$setViewValue NgModelController#$setViewValue}.
|
||||
This in turn pipelines all functions in the {@link api/ng.directive:ngModel.NgModelController#$parsers NgModelController#$parsers} array, so that each of these functions has an opportunity to convert the value and change validity state of the form control through {@link api/ng.directive:ngModel.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
In a similar way, whenever a user interacts with a control it calls {@link api/ng.directive:ngModel.NgModelController#methods_$setViewValue NgModelController#$setViewValue}.
|
||||
This in turn pipelines all functions in the {@link api/ng.directive:ngModel.NgModelController#properties_$parsers NgModelController#$parsers} array, so that each of these functions has an opportunity to convert the value and change validity state of the form control through {@link api/ng.directive:ngModel.NgModelController#methods_$setValidity NgModelController#$setValidity}.
|
||||
|
||||
In the following example we create two directives.
|
||||
|
||||
|
|
@ -230,7 +235,7 @@ In the following example we create two directives.
|
|||
<script>
|
||||
var app = angular.module('form-example1', []);
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
var INTEGER_REGEXP = /^\-?\d+$/;
|
||||
app.directive('integer', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
|
|
@ -278,7 +283,7 @@ However, if you need more flexibility, you can write your own form control as a
|
|||
|
||||
In order for custom control to work with `ngModel` and to achieve two-way data-binding it needs to:
|
||||
|
||||
- implement `$render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- implement `$render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#properties_$formatters NgModelController#$formatters},
|
||||
- call `$setViewValue` method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.
|
||||
|
||||
See {@link guide/directive $compileProvider.directive} for more info.
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ http://userguide.icu-project.org/locale ICU } website for more information about
|
|||
**Supported locales in Angular**
|
||||
Angular separates number and datetime format rule sets into different files, each file for a
|
||||
particular locale. You can find a list of currently supported locales {@link
|
||||
https://github.com/angular/angular.js/tree/master/i18n/locale here}
|
||||
https://github.com/angular/angular.js/tree/master/src/ngLocale here}
|
||||
# Providing locale rules to Angular
|
||||
|
||||
There are two approaches to providing locale rules to Angular:
|
||||
|
|
@ -56,22 +56,22 @@ locale-specific file to the end of `angular.js` or `angular.min.js` file.
|
|||
For example on *nix, to create an angular.js file that contains localization rules for german
|
||||
locale, you can do the following:
|
||||
|
||||
`cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js`
|
||||
`cat angular.js i18n/angular-locale_de-de.js > angular_de-de.js`
|
||||
|
||||
When the application containing `angular_de-ge.js` script instead of the generic angular.js script
|
||||
When the application containing `angular_de-de.js` script instead of the generic angular.js script
|
||||
starts, Angular is automatically pre-configured with localization rules for the german locale.
|
||||
|
||||
**2. Including locale js script in index.html page**
|
||||
|
||||
You can also include the locale specific js file in the index.html page. For example, if one client
|
||||
requires German locale, you would serve index_de-ge.html which will look something like this:
|
||||
requires German locale, you would serve index_de-de.html which will look something like this:
|
||||
|
||||
<pre>
|
||||
<html ng-app>
|
||||
<head>
|
||||
….
|
||||
<script src="angular.js"></script>
|
||||
<script src="i18n/angular-locale_de-ge.js"></script>
|
||||
<script src="i18n/angular-locale_de-de.js"></script>
|
||||
….
|
||||
</head>
|
||||
</html>
|
||||
|
|
@ -97,9 +97,9 @@ locale, it is fine to rely on the default currency symbol. However, if you antic
|
|||
in other locales might use your app, you should provide your own currency symbol to make sure the
|
||||
actual value is understood.
|
||||
|
||||
For example, if you want to display account balance of 1000 dollars with the following binding
|
||||
For example, if you want to display an account balance of 1000 dollars with the following binding
|
||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,22 @@ This document describes the Internet Explorer (IE) idiosyncrasies when dealing w
|
|||
attributes and tags. Read this document if you are planning on deploying your Angular application
|
||||
on IE v8.0 or earlier.
|
||||
|
||||
The project currently supports and will attempt to fix bugs for IE8 and above. The continuous
|
||||
integration server runs all the tests against IE8. See http://ci.angularjs.org.
|
||||
|
||||
IE7 and below are not tested and the project makes no guarantee that Angular will work on it.
|
||||
A subset of the AngularJS functionality may work. It is up to you to test and decide whether
|
||||
it works for your particular app.
|
||||
|
||||
It is very unlikely that issues specific to IE7 or earlier will be given any time by the core team.
|
||||
[GitHub](https://github.com/angular/angular.js/issues/4974)
|
||||
|
||||
|
||||
# Short Version
|
||||
|
||||
To make your Angular application work on IE please make sure that:
|
||||
|
||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
||||
1. You polyfill JSON.stringify for IE7 and below. You can use
|
||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
<pre>
|
||||
|
|
@ -40,7 +51,7 @@ To make your Angular application work on IE please make sure that:
|
|||
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
`<div ng-view>` instead), or
|
||||
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
|
|
@ -80,7 +91,7 @@ The **important** parts are:
|
|||
IE has issues with element tag names which are not standard HTML tag names. These fall into two
|
||||
categories, and each category has its own fix.
|
||||
|
||||
* If the tag name starts with `my:` prefix than it is considered an XML namespace and must
|
||||
* If the tag name starts with `my:` prefix then it is considered an XML namespace and must
|
||||
have corresponding namespace declaration on `<html xmlns:my="ignored">`
|
||||
|
||||
* If the tag has no `:` but it is not a standard HTML tag, then it must be pre-created using
|
||||
|
|
@ -164,7 +175,7 @@ To make CSS selectors work with custom elements, the custom element name must be
|
|||
</script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
ng\\:view {
|
||||
ng\:view {
|
||||
display: block;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,142 @@
|
|||
@name Developer Guide
|
||||
@description
|
||||
|
||||
Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
|
||||
to develop web apps, you've come to the right place.
|
||||
# Guide to AngularJS Documentation
|
||||
|
||||
If you are completely or relatively unfamiliar with angular, you may want to check out one or both
|
||||
of the following documents before returning here to the Developer Guide:
|
||||
Everything you need to know about AngularJS
|
||||
|
||||
* {@link guide/introduction What is AngularJS?}
|
||||
* {@link guide/concepts Conceptual Overview}
|
||||
|
||||
## Tutorials
|
||||
|
||||
* {@link tutorial/index Official AngularJS Tutorial}
|
||||
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
||||
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
||||
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
||||
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Templates
|
||||
|
||||
In Angular applications, you move the job of filling page templates with data from the server to the client. The result is a system better structured for dynamic page updates. Below are the core features you'll use.
|
||||
|
||||
* {@link guide/databinding Data binding}
|
||||
* {@link guide/expression Expressions}
|
||||
* {@link guide/directive Directives}
|
||||
* {@link api/ngRoute.$route Views and routes (see the example)}
|
||||
* {@link guide/filter Filters}
|
||||
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
|
||||
|
||||
### Application Structure
|
||||
|
||||
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
|
||||
* **App wiring:** {@link guide/di Dependency injection}
|
||||
* **Exposing model to templates:** {@link guide/scope Scopes}
|
||||
* **Communicating with servers:** {@link api/ng.$http $http}, {@link api/ngResource.$resource $resource}
|
||||
|
||||
### Other AngularJS Features
|
||||
|
||||
* **Animation:** {@link guide/animations Core concepts}, {@link api/ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
|
||||
* **Security:** {@link api/ng.$sce Strict Contextual Escaping}, {@link api/ng.directive:ngCsp Content Security Policy}, {@link api/ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
|
||||
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link api/ng.filter:date date filter}, {@link api/ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
|
||||
* **Mobile:** {@link api/ngTouch Touch events}
|
||||
|
||||
### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||
|
||||
## Specific Topics
|
||||
|
||||
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
|
||||
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
|
||||
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
||||
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
||||
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
||||
|
||||
## Tools
|
||||
|
||||
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
|
||||
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
|
||||
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
|
||||
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
|
||||
|
||||
## Complementary Libraries
|
||||
|
||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
||||
|
||||
## Deployment
|
||||
|
||||
### General
|
||||
|
||||
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
|
||||
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
||||
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
||||
|
||||
### Server-Specific
|
||||
|
||||
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html)
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
||||
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
||||
|
||||
## Learning Resources
|
||||
|
||||
###Books
|
||||
* [AngularJS](http://www.amazon.com/AngularJS-Brad-Green/dp/1449344852) by Brad Green and Shyam Seshadri
|
||||
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
|
||||
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
|
||||
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
|
||||
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
|
||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||
|
||||
###Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||
|
||||
### Courses
|
||||
* **Free online:**
|
||||
[thinkster.io](http://thinkster.io),
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
|
||||
* **Paid online:**
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
|
||||
* **Paid onsite:**
|
||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||
|
||||
## Getting Help
|
||||
|
||||
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
|
||||
|
||||
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
|
||||
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
|
||||
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
|
||||
|
||||
## Social Channels
|
||||
|
||||
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
|
||||
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
|
||||
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
|
||||
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
|
||||
|
||||
## Contributing to AngularJS
|
||||
|
||||
Though we have a core group of core contributors at Google, Angular is an open source project with hundreds of contributors. We'd love you to be one of them. When you're ready, please read the {@link misc/contribute Guide for contributing to AngularJS}.
|
||||
|
||||
## Final Bits
|
||||
|
||||
Didn't find what you're looking for here? Check out [AngularJS-Learning](https://github.com/jmcunningham/AngularJS-Learning) for an even more comprehensive list of links to videos, tutorials, and blog posts.
|
||||
|
||||
If you have awesome AngularJS resources that belong on this page, please tell us about them on [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs).
|
||||
|
||||
* {@link misc/started Getting Started}
|
||||
* {@link tutorial/index Angular Tutorial}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,108 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Introduction
|
||||
@name Introduction
|
||||
@description
|
||||
|
||||
Angular is pure client-side technology, written entirely in JavaScript. It works with the
|
||||
long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of
|
||||
web apps easier and faster than ever before.
|
||||
|
||||
One important way that Angular simplifies web development is by increasing the level of abstraction
|
||||
between the developer and most low-level web app development tasks. Angular automatically takes
|
||||
care of many of these tasks, including:
|
||||
# What Is Angular?
|
||||
|
||||
* DOM Manipulation
|
||||
* Setting Up Listeners and Notifiers
|
||||
* Input Validation
|
||||
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
|
||||
language and lets you extend HTML's syntax to express your application's components clearly and
|
||||
succinctly. Out of the box, it eliminates much of the code you currently write through data
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser, making it
|
||||
an ideal partner with any server technology.
|
||||
|
||||
Because Angular handles much of the work involved in these tasks, developers can concentrate more
|
||||
on application logic and less on repetitive, error-prone, lower-level coding.
|
||||
Angular is what HTML would have been had it been designed for applications. HTML is a great
|
||||
declarative language for static documents. It does not contain much in the way of creating
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do
|
||||
to trick the browser into doing what I want.*
|
||||
|
||||
At the same time that Angular simplifies the development of web apps, it brings relatively
|
||||
sophisticated techniques to the client-side, including:
|
||||
The impedance mismatch between dynamic applications and static documents is often solved with:
|
||||
|
||||
* Separation of data, application logic, and presentation components
|
||||
* Data Binding between data and presentation components
|
||||
* Services (common web app operations, implemented as substitutable objects)
|
||||
* Dependency Injection (used primarily for wiring together services)
|
||||
* An extensible HTML compiler (written entirely in JavaScript)
|
||||
* Ease of Testing
|
||||
* **a library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
in charge and it calls into the library when it sees fit. E.g., `jQuery`.
|
||||
* **frameworks** - a particular implementation of a web application, where your code fills in
|
||||
the details. The framework is in charge and it calls into your code when it needs something
|
||||
app specific. E.g., `knockout`, `ember`, etc.
|
||||
|
||||
These techniques have been for the most part absent from the client-side for far too long.
|
||||
|
||||
## Single-page / Round-trip Applications
|
||||
Angular takes another approach. It attempts to minimize the impedance mismatch between document
|
||||
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
|
||||
browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
You can use Angular to develop both single-page and round-trip apps, but Angular is designed
|
||||
primarily for developing single-page apps. Angular supports browser history, forward and back
|
||||
buttons, and bookmarking in single-page apps.
|
||||
* Data binding, as in `{{}}`.
|
||||
* DOM control structures for repeating/hiding DOM fragments.
|
||||
* Support for forms and form validation.
|
||||
* Attaching code-behind to DOM elements.
|
||||
* Grouping of HTML into reusable components.
|
||||
|
||||
|
||||
|
||||
## A complete client-side solution
|
||||
|
||||
Angular is not a single piece in the overall puzzle of building the client-side of a web
|
||||
application. It handles all of the DOM and AJAX glue code you once wrote by hand and puts it in a
|
||||
well-defined structure. This makes Angular opinionated about how a CRUD application should be
|
||||
built. But while it is opinionated, it also tries to make sure that its opinion is just a
|
||||
starting point you can easily change. Angular comes with the following out-of-the-box:
|
||||
|
||||
* Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
|
||||
directives, form validation, routing, deep-linking, reusable components, dependency injection.
|
||||
* Testability story: unit-testing, end-to-end testing, mocks, test harnesses.
|
||||
* Seed application with directory layout and test scripts as a starting point.
|
||||
|
||||
|
||||
## Angular Sweet Spot
|
||||
|
||||
Angular simplifies application development by presenting a higher level of abstraction to the
|
||||
developer. Like any abstraction, it comes at a cost of flexibility. In other words not every app
|
||||
is a good fit for Angular. Angular was built with the CRUD application in mind. Luckily CRUD
|
||||
applications represent the majority of web applications. To understand what Angular is
|
||||
good at, though, it helps to understand when an app is not a good fit for Angular.
|
||||
|
||||
Games and GUI editors are examples of applications with intensive and tricky DOM manipulation.
|
||||
These kinds of apps are different from CRUD apps, and as a result are probably not a good fit for Angular.
|
||||
In these cases it may be better to use a library with a lower level of abstraction, such as `jQuery`.
|
||||
|
||||
# The Zen of Angular
|
||||
|
||||
Angular is built around the belief that declarative code is better than imperative when it comes
|
||||
to building UIs and wiring software components together, while imperative code is excellent for
|
||||
expressing business logic.
|
||||
|
||||
|
||||
* It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
|
||||
the testability of the code.
|
||||
* It is a really, _really_ good idea to regard app testing as equal in importance to app
|
||||
writing. Testing difficulty is dramatically affected by the way the code is structured.
|
||||
* It is an excellent idea to decouple the client side of an app from the server side. This
|
||||
allows development work to progress in parallel, and allows for reuse of both sides.
|
||||
* It is very helpful indeed if the framework guides developers through the entire journey of
|
||||
building an app: from designing the UI, through writing the business logic, to testing.
|
||||
* It is always good to make common tasks trivial and difficult tasks possible.
|
||||
|
||||
|
||||
|
||||
Angular frees you from the following pains:
|
||||
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the
|
||||
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It
|
||||
vastly reduces the amount of JavaScript coding _you_ have to do, and it makes it easier to see
|
||||
what your application does.
|
||||
* **Manipulating HTML DOM programmatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it's cumbersome and error-prone. By declaratively describing how the UI
|
||||
should change as your application state changes, you are freed from low-level DOM manipulation
|
||||
tasks. Most applications written with Angular never have to programmatically manipulate the
|
||||
DOM, although you can if you want to.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of AJAX
|
||||
applications' tasks. The flow of marshaling data from the server to an internal object to an HTML
|
||||
form, allowing users to modify the form, validating the form, displaying validation errors,
|
||||
returning to an internal model, and then back to the server, creates a lot of boilerplate
|
||||
code. Angular eliminates almost all of this boilerplate, leaving code that describes the
|
||||
overall flow of the application rather than all of the implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot
|
||||
of plumbing just to get a basic "Hello World" AJAX app working. With Angular you can bootstrap
|
||||
your app easily using services, which are auto-injected into your application in a {@link
|
||||
http://code.google.com/p/google-guice/ Guice}-like dependency-injection style. This allows you
|
||||
to get started developing features quickly. As a bonus, you get full control over the
|
||||
initialization process in automated tests.
|
||||
|
||||
You normally wouldn't want to load Angular with every page change, as would be the case with using
|
||||
Angular in a round-trip app. However, it would make sense to do so if you were adding a subset of
|
||||
Angular's features (for example, templates to leverage angular's data-binding feature) to an
|
||||
existing round-trip app. You might follow this course of action if you were migrating an older app
|
||||
to a single-page Angular app.
|
||||
|
|
|
|||
655
docs/content/guide/migration.ngdoc
Normal file
655
docs/content/guide/migration.ngdoc
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
@ngdoc overview
|
||||
@name Migrating from 1.0 to 1.2
|
||||
@description
|
||||
|
||||
|
||||
AngularJS version 1.2 introduces several breaking changes that may require changes to your
|
||||
application's source code.
|
||||
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
|
||||
AngularJS 1.2 has undergone a thourough security review to make applications safer by default,
|
||||
which has driven many of these changes. Several new features, especially animations, would not
|
||||
be possible without a few changes. Finally, some outstanding bugs were best fixed by changing
|
||||
an existing API.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<p>**Note:** AngularJS versions 1.1.x are considered "experimental" with breaking changes between minor releases.
|
||||
Version 1.2 is the result of several versions on the 1.1 branch, and has a stable API.</p>
|
||||
|
||||
<p>If you have an application on 1.1 and want to migrate it to 1.2, everything in the guide
|
||||
below should still apply, but you may want to consult the
|
||||
[changelog](https://github.com/angular/angular.js/blob/master/CHANGELOG.md) as well.</p>
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Summary of Breaking Changes</li>
|
||||
<li>{@link guide/migration#ngroute-has-been-moved-into-its-own-module ngRoute has been moved into its own module}</li>
|
||||
<li>{@link guide/migration#templates-no-longer-automatically-unwrap-promises Templates no longer automatically unwrap promises}</li>
|
||||
<li>{@link guide/migration#syntax-for-named-wildcard-parameters-changed-in Syntax for named wildcard parameters changed in <code>$route</code>}</li>
|
||||
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code> or <code>*[ng-src]</code>}</li>
|
||||
<li>{@link guide/migration#interpolations-inside-dom-event-handlers-are-now-disallowed Interpolations inside DOM event handlers are now disallowed}</li>
|
||||
<li>{@link guide/migration#directives-cannot-end-with--start-or--end Directives cannot end with -start or -end}</li>
|
||||
<li>{@link guide/migration#in-$q,-promisealways-has-been-renamed-promisefinally In $q, promise.always has been renamed promise.finally}</li>
|
||||
<li>{@link guide/migration#ngmobile-is-now-ngtouch ngMobile is now ngTouch}</li>
|
||||
<li>{@link guide/migration#resource$then-has-been-removed resource.$then has been removed}</li>
|
||||
<li>{@link guide/migration#resource-methods-return-the-promise Resource methods return the promise}</li>
|
||||
<li>{@link guide/migration#resource-promises-are-resolved-with-the-resource-instance Resource promises are resolved with the resource instance}</li>
|
||||
<li>{@link guide/migration#$locationsearch-supports-multiple-keys $location.search supports multiple keys}</li>
|
||||
<li>{@link guide/migration#ngbindhtmlunsafe-has-been-removed-and-replaced-by-ngbindhtml ngBindHtmlUnsafe has been removed and replaced by ngBindHtml}</li>
|
||||
<li>{@link guide/migration#form-names-that-are-expressions-are-evaluated Form names that are expressions are evaluated}</li>
|
||||
<li>{@link guide/migration#hasownproperty-disallowed-as-an-input-name hasOwnProperty disallowed as an input name}</li>
|
||||
<li>{@link guide/migration#directives-order-of-postlink-functions-reversed Directives: Order of postLink functions reversed}</li>
|
||||
<li>{@link guide/migration#directive-priority Directive priority}</li>
|
||||
<li>{@link guide/migration#ngscenario ngScenario}</li>
|
||||
<li>{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}</li>
|
||||
<li>{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}</li>
|
||||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||||
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
|
||||
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
|
||||
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
|
||||
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
|
||||
</ul>
|
||||
|
||||
|
||||
## ngRoute has been moved into its own module
|
||||
|
||||
Just like `ngResource`, `ngRoute` is now its own module.
|
||||
|
||||
Applications that use `$route`, `ngView`, and/or `$routeParams` will now need to load an
|
||||
`angular-route.js` file and have their application's module dependency on the `ngRoute` module.
|
||||
|
||||
Before:
|
||||
|
||||
```html
|
||||
<script src="angular.js"></script>
|
||||
```
|
||||
|
||||
```javascript
|
||||
var myApp = angular.module('myApp', ['someOtherModule']);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```html
|
||||
<script src="angular.js"></script>
|
||||
<script src="angular-route.js"></script>
|
||||
```
|
||||
|
||||
```javascript
|
||||
var myApp = angular.module('myApp', ['ngRoute', 'someOtherModule']);
|
||||
```
|
||||
|
||||
See [5599b55b](https://github.com/angular/angular.js/commit/5599b55b04788c2e327d7551a4a699d75516dd21).
|
||||
|
||||
|
||||
## Templates no longer automatically unwrap promises
|
||||
|
||||
`$parse` and templates in general will no longer automatically unwrap promises.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
$scope.foo = $http({method: 'GET', url: '/someUrl'});
|
||||
```
|
||||
|
||||
```html
|
||||
<p>{{foo}}</p>
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
$http({method: 'GET', url: '/someUrl'})
|
||||
.success(function(data) {
|
||||
$scope.foo = data;
|
||||
});
|
||||
```
|
||||
|
||||
```html
|
||||
<p>{{foo}}</p>
|
||||
```
|
||||
|
||||
This feature has been deprecated. If absolutely needed, it can be reenabled for now via the
|
||||
`$parseProvider.unwrapPromises(true)` API.
|
||||
|
||||
See [5dc35b52](https://github.com/angular/angular.js/commit/5dc35b527b3c99f6544b8cb52e93c6510d3ac577),
|
||||
[b6a37d11](https://github.com/angular/angular.js/commit/b6a37d112b3e1478f4d14a5f82faabf700443748).
|
||||
|
||||
|
||||
## Syntax for named wildcard parameters changed in `$route`
|
||||
|
||||
To migrate the code, follow the example below. Here, `*highlight` becomes `:highlight*`
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
$routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
|
||||
{controller: noop, templateUrl: 'Chapter.html'});
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
$routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
|
||||
{controller: noop, templateUrl: 'Chapter.html'});
|
||||
```
|
||||
|
||||
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
|
||||
|
||||
|
||||
## You can only bind one expression to `*[src]` or `*[ng-src]`
|
||||
|
||||
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
|
||||
`src` attribute of elements.
|
||||
|
||||
This is one of several improvements to security introduces by Angular 1.2.
|
||||
|
||||
Concatenating expressions makes it hard to understand whether some combination of concatenated
|
||||
values are unsafe to use and potentially subject to XSS vulnerabilities. To simplify the task of
|
||||
auditing for XSS issues, we now require that a single expression be used for `*[src/ng-src]`
|
||||
bindings such as bindings for `iframe[src]`, `object[src]`, etc.
|
||||
|
||||
<table class="table table-bordered code-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Examples</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code><img src="{{a}}/{{b}}"></code></td>
|
||||
<td class="success">ok</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><iframe src="{{a}}/{{b}}"></iframe></code></td>
|
||||
<td class="error">bad</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><iframe src="{{a}}"></iframe></code></td>
|
||||
<td class="success">ok</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
To migrate your code, you can combine multiple expressions using a method attached to your scope.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
scope.baseUrl = 'page';
|
||||
scope.a = 1;
|
||||
scope.b = 2;
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Are a and b properly escaped here? Is baseUrl controlled by user? -->
|
||||
<iframe src="{{baseUrl}}?a={{a}&b={{b}}">
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
var baseUrl = "page";
|
||||
scope.getIframeSrc = function() {
|
||||
|
||||
// One should think about their particular case and sanitize accordingly
|
||||
var qs = ["a", "b"].map(function(value, name) {
|
||||
return encodeURIComponent(name) + "=" +
|
||||
encodeURIComponent(value);
|
||||
}).join("&");
|
||||
|
||||
// `baseUrl` isn't exposed to a user's control, so we don't have to worry about escaping it.
|
||||
return baseUrl + "?" + qs;
|
||||
};
|
||||
```
|
||||
|
||||
```html
|
||||
<iframe src="{{getIframeSrc()}}">
|
||||
```
|
||||
|
||||
See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262bb43f26d47245f6c2739).
|
||||
|
||||
|
||||
## Interpolations inside DOM event handlers are now disallowed
|
||||
|
||||
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers
|
||||
means that the interpolated value is a JS string that is evaluated. Storing or generating such
|
||||
strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other
|
||||
Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which
|
||||
makes them much safer.
|
||||
|
||||
To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
JS: scope.foo = 'alert(1)';
|
||||
HTML: <div onclick="{{foo}}">
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
JS: scope.foo = function() { alert(1); }
|
||||
HTML: <div ng-click="foo()">
|
||||
```
|
||||
|
||||
See [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56).
|
||||
|
||||
|
||||
## Directives cannot end with -start or -end
|
||||
|
||||
This change was necessary to enable multi-element directives. The best fix is to rename existing
|
||||
directives so that they don't end with these suffixes.
|
||||
|
||||
See [e46100f7](https://github.com/angular/angular.js/commit/e46100f7097d9a8f174bdb9e15d4c6098395c3f2).
|
||||
|
||||
|
||||
## In $q, promise.always has been renamed promise.finally
|
||||
|
||||
The reason for this change is to align `$q` with the [Q promise
|
||||
library](https://github.com/kriskowal/q), despite the fact that this makes it a bit more difficult
|
||||
to use with non-ES5 browsers, like IE8.
|
||||
|
||||
`finally` also goes well together with the `catch` API that was added to `$q` recently and is part
|
||||
of the [DOM promises standard](http://dom.spec.whatwg.org/).
|
||||
|
||||
To migrate the code follow the example below.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
$http.get('/foo').always(doSomething);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
$http.get('/foo').finally(doSomething);
|
||||
```
|
||||
|
||||
Or for IE8-compatible code:
|
||||
|
||||
```javascript
|
||||
$http.get('/foo')['finally'](doSomething);
|
||||
```
|
||||
|
||||
See [f078762d](https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c).
|
||||
|
||||
|
||||
## ngMobile is now ngTouch
|
||||
|
||||
Many touch-enabled devices are not mobile devices, so we decided to rename this module to better
|
||||
reflect its concerns.
|
||||
|
||||
To migrate, replace all references to `ngMobile` with `ngTouch` and `angular-mobile.js` with
|
||||
`angular-touch.js`.
|
||||
|
||||
See [94ec84e7](https://github.com/angular/angular.js/commit/94ec84e7b9c89358dc00e4039009af9e287bbd05).
|
||||
|
||||
|
||||
## resource.$then has been removed
|
||||
|
||||
Resource instances do not have a `$then` function anymore. Use the `$promise.then` instead.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
Resource.query().$then(callback);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
Resource.query().$promise.then(callback);
|
||||
```
|
||||
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
## Resource methods return the promise
|
||||
|
||||
Methods of a resource instance return the promise rather than the instance itself.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
resource.$save().chaining = true;
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
resource.$save();
|
||||
resource.chaining = true;
|
||||
```
|
||||
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
## Resource promises are resolved with the resource instance
|
||||
|
||||
On success, the resource promise is resolved with the resource instance rather than HTTP response object.
|
||||
|
||||
Use interceptor API to access the HTTP response object.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
Resource.query().$then(function(response) {...});
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
var Resource = $resource('/url', {}, {
|
||||
get: {
|
||||
method: 'get',
|
||||
interceptor: {
|
||||
response: function(response) {
|
||||
// expose response
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
## $location.search supports multiple keys
|
||||
|
||||
{@link api/ng.$location#methods_search `$location.search`} now supports multiple keys with the
|
||||
same value provided that the values are stored in an array.
|
||||
|
||||
Before this change:
|
||||
|
||||
* `parseKeyValue` only took the last key overwriting all the previous keys.
|
||||
* `toKeyValue` joined the keys together in a comma delimited string.
|
||||
|
||||
This was deemed buggy behavior. If your server relied on this behavior then either the server
|
||||
should be fixed, or a simple serialization of the array should be done on the client before
|
||||
passing it to `$location`.
|
||||
|
||||
See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe).
|
||||
|
||||
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngBindHtml` which has been moved from `ngSanitize` module to the core `ng` module.
|
||||
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
|
||||
`$sanitize` before being innerHTML'd. If the `$sanitize` service isn't available (`ngSanitize`
|
||||
module is not loaded) and the bound expression evaluates to a value that is not trusted an
|
||||
exception is thrown.
|
||||
|
||||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||||
|
||||
|
||||
## Form names that are expressions are evaluated
|
||||
|
||||
If you have form names that will evaluate as an expression:
|
||||
|
||||
```
|
||||
<form name="ctrl.form">
|
||||
```
|
||||
|
||||
And if you are accessing the form from your controller:
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
function($scope) {
|
||||
$scope['ctrl.form'] // form controller instance
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
function($scope) {
|
||||
$scope.ctrl.form // form controller instance
|
||||
}
|
||||
```
|
||||
|
||||
This makes it possible to access a form from a controller using the new "controller as" syntax.
|
||||
Supporting the previous behavior offers no benefit.
|
||||
|
||||
See [8ea802a1](https://github.com/angular/angular.js/commit/8ea802a1d23ad8ecacab892a3a451a308d9c39d7).
|
||||
|
||||
|
||||
## hasOwnProperty disallowed as an input name
|
||||
|
||||
Inputs with name equal to `hasOwnProperty` are not allowed inside form or ngForm directives.
|
||||
|
||||
Before, inputs whose name was "hasOwnProperty" were quietly ignored and not added to the scope.
|
||||
Now a badname exception is thrown. Using "hasOwnProperty" for an input name would be very unusual
|
||||
and bad practice. To migrate, change your input name.
|
||||
|
||||
See [7a586e5c](https://github.com/angular/angular.js/commit/7a586e5c19f3d1ecc3fefef084ce992072ee7f60).
|
||||
|
||||
|
||||
## Directives: Order of postLink functions reversed
|
||||
|
||||
The order of postLink fn is now mirror opposite of the order in which corresponding preLinking and compile functions execute.
|
||||
|
||||
Previously the compile/link fns executed in order, sorted by priority:
|
||||
|
||||
<table class="table table-bordered table-striped code-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Step</th>
|
||||
<th align="center">Old Sort Order</th>
|
||||
<th align="center">New Sort Order</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Compile Fns</td>
|
||||
<td align="center" colspan="2">High → Low</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td colspan="3">Compile child nodes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>PreLink Fns</td>
|
||||
<td align="center" colspan="2">High → Low</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td colspan="3">Link child nodes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>PostLink Fns</td>
|
||||
<td align="center">High → Low</td>
|
||||
<td align="center">**Low → High**</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<small>"High → Low" here refers to the `priority` option of a directive.</small>
|
||||
|
||||
Very few directives in practice rely on the order of postLinking functions (unlike on the order
|
||||
of compile functions), so in the rare case of this change affecting an existing directive, it might
|
||||
be necessary to convert it to a preLinking function or give it negative priority.
|
||||
|
||||
You can look at [the diff of this
|
||||
commit](https://github.com/angular/angular.js/commit/31f190d4d53921d32253ba80d9ebe57d6c1de82b) to see how an internal
|
||||
attribute interpolation directive was adjusted.
|
||||
|
||||
See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253ba80d9ebe57d6c1de82b).
|
||||
|
||||
|
||||
## Directive priority
|
||||
|
||||
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
|
||||
|
||||
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precendence:
|
||||
|
||||
|
||||
Directive | Old Priority | New Priority
|
||||
-----------------|--------------|-------------
|
||||
ngRepeat | 1000 | 1000
|
||||
ngSwitchWhen | 500 | 800
|
||||
ngIf | 1000 | 600
|
||||
ngInclude | 1000 | 400
|
||||
ngView | 1000 | 400
|
||||
|
||||
See [b7af76b4](https://github.com/angular/angular.js/commit/b7af76b4c5aa77648cc1bfd49935b48583419023).
|
||||
|
||||
|
||||
## ngScenario
|
||||
|
||||
browserTrigger now uses an eventData object instead of direct parameters for mouse events.
|
||||
To migrate, place the `keys`,`x` and `y` parameters inside of an object and place that as the
|
||||
third parameter for the browserTrigger function.
|
||||
|
||||
See [28f56a38](https://github.com/angular/angular.js/commit/28f56a383e9d1ff378e3568a3039e941c7ffb1d8).
|
||||
|
||||
|
||||
## ngInclude and ngView replace its entire element on update
|
||||
|
||||
Previously `ngInclude` and `ngView` only updated its element's content. Now these directives will
|
||||
recreate the element every time a new content is included.
|
||||
|
||||
This ensures that a single rootElement for all the included contents always exists, which makes
|
||||
definition of css styles for animations much easier.
|
||||
|
||||
See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d6fe57a6c45561a05b182),
|
||||
[aa2133ad](https://github.com/angular/angular.js/commit/aa2133ad818d2e5c27cbd3933061797096356c8a).
|
||||
|
||||
|
||||
## URLs are now sanitized against a whitelist
|
||||
|
||||
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
|
||||
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
|
||||
This change sholdn't impact apps that don't contain malicious image links.
|
||||
|
||||
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
|
||||
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
|
||||
|
||||
|
||||
## Isolate scope only exposed to directives with `scope` property
|
||||
|
||||
Directives without isolate scope do not get the isolate scope from an isolate directive on the
|
||||
same element. If your code depends on this behavior (non-isolate directive needs to access state
|
||||
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly.
|
||||
|
||||
**Before**
|
||||
|
||||
```
|
||||
<input ng-model="$parent.value" ng-isolate>
|
||||
|
||||
.directive('ngIsolate', function() {
|
||||
return {
|
||||
scope: {},
|
||||
template: '{{value}}'
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**After**
|
||||
|
||||
```
|
||||
<input ng-model="value" ng-isolate>
|
||||
|
||||
.directive('ngIsolate', function() {
|
||||
return {
|
||||
scope: {value: '=ngModel'},
|
||||
template: '{{value}}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
See [909cabd3](https://github.com/angular/angular.js/commit/909cabd36d779598763cc358979ecd85bb40d4d7),
|
||||
[#1924](https://github.com/angular/angular.js/issues/1924) and
|
||||
[#2500](https://github.com/angular/angular.js/issues/2500).
|
||||
|
||||
|
||||
## Change to interpolation priority
|
||||
|
||||
Previously, the interpolation priority was `-100` in 1.2.0-rc.2, and `100` before 1.2.0-rc.2.
|
||||
Before this change the binding was setup in the post-linking phase.
|
||||
|
||||
Now the attribute interpolation (binding) executes as a directive with priority 100 and the
|
||||
binding is set up in the pre-linking phase.
|
||||
|
||||
See [79223eae](https://github.com/angular/angular.js/commit/79223eae5022838893342c42dacad5eca83fabe8),
|
||||
[#4525](https://github.com/angular/angular.js/issues/4525),
|
||||
[#4528](https://github.com/angular/angular.js/issues/4528), and
|
||||
[#4649](https://github.com/angular/angular.js/issues/4649)
|
||||
|
||||
## Underscore-prefixed/suffixed properties are non-bindable
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>**Reverted**: This breaking change has been reverted in 1.2.1, and so can be ignored if you're using **version 1.2.1 or higher**</p>
|
||||
</div>
|
||||
|
||||
This change introduces the notion of "private" properties (properties
|
||||
whose names begin and/or end with an underscore) on the scope chain.
|
||||
These properties will not be available to Angular expressions (i.e. {{
|
||||
}} interpolation in templates and strings passed to `$parse`) They are
|
||||
freely available to JavaScript code (as before).
|
||||
|
||||
**Motivation**
|
||||
|
||||
Angular expressions execute in a limited context. They do not have
|
||||
direct access to the global scope, `window`, `document` or the Function
|
||||
constructor. However, they have direct access to names/properties on
|
||||
the scope chain. It has been a long standing best practice to keep
|
||||
sensitive APIs outside of the scope chain (in a closure or your
|
||||
controller.) That's easier said that done for two reasons:
|
||||
|
||||
1. JavaScript does not have a notion of private properties so if you need
|
||||
someone on the scope chain for JavaScript use, you also expose it to
|
||||
Angular expressions
|
||||
2. The new `controller as` syntax that's now in increased usage exposes the
|
||||
entire controller on the scope chain greatly increaing the exposed surface.
|
||||
|
||||
Though Angular expressions are written and controlled by the developer, they:
|
||||
|
||||
1. Typically deal with user input
|
||||
2. Don't get the kind of test coverage that JavaScript code would
|
||||
|
||||
This commit provides a way, via a naming convention, to allow restricting properties from
|
||||
controllers/scopes. This means Angular expressions can access only those properties that
|
||||
are actually needed by the expressions.
|
||||
|
||||
See [3d6a89e8](https://github.com/angular/angular.js/commit/3d6a89e8888b14ae5cb5640464e12b7811853c7e).
|
||||
|
||||
|
||||
## You cannot bind to select[multiple]
|
||||
|
||||
Switching between `select[single]` and `select[multiple]` has always been odd due to browser quirks.
|
||||
This feature never worked with two-way data-binding so it's not expected that anyone is using it.
|
||||
|
||||
If you are interested in properly adding this feature, please submit a pull request on Github.
|
||||
|
||||
See [d87fa004](https://github.com/angular/angular.js/commit/d87fa0042375b025b98c40bff05e5f42c00af114).
|
||||
|
||||
|
||||
## Uncommon region-specific local files were removed from i18n
|
||||
|
||||
AngularJS uses the Google Closure library's locale files. The following locales were removed from
|
||||
Closure, so Angular is not able to continue to support them:
|
||||
|
||||
`chr`, `cy`, `el-polyton`, `en-zz`, `fr-rw`, `fr-sn`, `fr-td`, `fr-tg`, `haw`, `it-ch`, `ln-cg`,
|
||||
`mo`, `ms-bn`, `nl-aw`, `nl-be`, `pt-ao`, `pt-gw`, `pt-mz`, `pt-st`, `ro-md`, `ru-md`, `ru-ua`,
|
||||
`sr-cyrl-ba`, `sr-cyrl-me`, `sr-cyrl`, `sr-latn-ba`, `sr-latn-me`, `sr-latn`, `sr-rs`, `sv-fi`,
|
||||
`sw-ke`, `ta-lk`, `tl-ph`, `ur-in`, `zh-hans-hk`, `zh-hans-mo`, `zh-hans-sg`, `zh-hans`,
|
||||
`zh-hant-hk`, `zh-hant-mo`, `zh-hant-tw`, `zh-hant`
|
||||
|
||||
Although these locales were removed from the official AngularJS repository, you can continue to
|
||||
load and use your copy of the locale file provided that you maintain it yourself.
|
||||
|
||||
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
|
||||
|
||||
|
|
@ -191,6 +191,24 @@ scripts into a VM. There are existing projects which deal with script loading, w
|
|||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||||
|
||||
## Creation versus Retrieval
|
||||
|
||||
Beware that using `angular.module('myModule', [])` will create the module `myModule` and overwrite any
|
||||
existing module named `myModule`. Use `angular.module('myModule')` to retrieve an existing module.
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module('myModule', []);
|
||||
|
||||
// add some directives and services
|
||||
myModule.service('myService', ...);
|
||||
myModule.directive('myDirective', ...);
|
||||
|
||||
// overwrites both myService and myDirective by creating a new module
|
||||
var myModule = angular.module('myModule', []);
|
||||
|
||||
// throws an error because myOtherModule has yet to be defined
|
||||
var myModule = angular.module('myOtherModule');
|
||||
</pre>
|
||||
|
||||
# Unit Testing
|
||||
|
||||
|
|
|
|||
|
|
@ -1,207 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Overview
|
||||
@description
|
||||
|
||||
|
||||
# What Is Angular?
|
||||
|
||||
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
|
||||
language and lets you extend HTML's syntax to express your application's components clearly and
|
||||
succinctly. Out of the box, it eliminates much of the code you currently write through data
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser, making it
|
||||
an ideal partner with any server technology.
|
||||
|
||||
Angular is what HTML would have been had it been designed for applications. HTML is a great
|
||||
declarative language for static documents. It does not contain much in the way of creating
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do
|
||||
to trick the browser into doing what I want.*
|
||||
|
||||
The impedance mismatch between dynamic applications and static documents is often solved with:
|
||||
|
||||
* **a library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
in charge and it calls into the library when it sees fit. E.g., `jQuery`.
|
||||
* **frameworks** - a particular implementation of a web application, where your code fills in
|
||||
the details. The framework is in charge and it calls into your code when it needs something
|
||||
app specific. E.g., `knockout`, `ember`, etc.
|
||||
|
||||
|
||||
Angular takes another approach. It attempts to minimize the impedance mismatch between document
|
||||
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
|
||||
browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
* Data binding, as in `{{}}`.
|
||||
* DOM control structures for repeating/hiding DOM fragments.
|
||||
* Support for forms and form validation.
|
||||
* Attaching code-behind to DOM elements.
|
||||
* Grouping of HTML into reusable components.
|
||||
|
||||
|
||||
|
||||
## A complete client-side solution
|
||||
|
||||
Angular is not a single piece in the overall puzzle of building the client-side of a web
|
||||
application. It handles all of the DOM and AJAX glue code you once wrote by hand and puts it in a
|
||||
well-defined structure. This makes Angular opinionated about how a CRUD application should be
|
||||
built. But while it is opinionated, it also tries to make sure that its opinion is just a
|
||||
starting point you can easily change. Angular comes with the following out-of-the-box:
|
||||
|
||||
* Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
|
||||
directives, form validation, routing, deep-linking, reusable components, dependency injection.
|
||||
* Testability story: unit-testing, end-to-end testing, mocks, test harnesses.
|
||||
* Seed application with directory layout and test scripts as a starting point.
|
||||
|
||||
|
||||
## Angular Sweet Spot
|
||||
|
||||
Angular simplifies application development by presenting a higher level of abstraction to the
|
||||
developer. Like any abstraction, it comes at a cost of flexibility. In other words not every app
|
||||
is a good fit for Angular. Angular was built for the CRUD application in mind. Luckily CRUD
|
||||
applications represent at least 90% of the web applications. But to understand what Angular is
|
||||
good at one also has to understand when an app is not a good fit for Angular.
|
||||
|
||||
Games, and GUI editors are examples of very intensive and tricky DOM manipulation. These kinds of
|
||||
apps are different from CRUD apps, and as a result are not a good fit for Angular. In these cases
|
||||
using something closer to bare metal such as `jQuery` may be a better fit.
|
||||
|
||||
|
||||
# An Introductory Angular Example
|
||||
|
||||
Below is a typical CRUD application which contains a form. The form values are validated, and
|
||||
are used to compute the total, which is formatted to a particular locale. These are some common
|
||||
concepts which the application developer may face:
|
||||
|
||||
* attaching data-model to the UI.
|
||||
* writing, reading and validating user input.
|
||||
* computing new values based on the model.
|
||||
* formatting output in a user specific locale.
|
||||
|
||||
<example>
|
||||
<file name="script.js">
|
||||
function InvoiceCntl($scope) {
|
||||
$scope.qty = 1;
|
||||
$scope.cost = 19.95;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceCntl">
|
||||
<b>Invoice:</b>
|
||||
<br>
|
||||
<br>
|
||||
<table>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-model="cost" required ></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should show of angular binding', function() {
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Try out the Live Preview above, and then let's walk through the example and describe what's going
|
||||
on.
|
||||
|
||||
In the `<html>` tag, we specify that it is an Angular
|
||||
application with the `ng-app` directive. The `ng-app` will cause Angular to {@link
|
||||
bootstrap auto initialize} your application.
|
||||
|
||||
<html ng-app>
|
||||
|
||||
We load Angular using the `<script>` tag:
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/?.?.?/angular.min.js"></script>
|
||||
|
||||
From the `ng-model` attribute of the `<input>` tags, Angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
Quantity: <input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required >
|
||||
Cost: <input type="number" ng-model="cost" required >
|
||||
|
||||
These input widgets look normal enough, but consider these points:
|
||||
|
||||
* When this page loaded, Angular bound the names of the input widgets (`qty` and `cost`) to
|
||||
variables of the same name. Think of those variables as the "Model" component of the
|
||||
Model-View-Controller design pattern.
|
||||
* Note that the HTML widget {@link api/ng.directive:input input}
|
||||
has special powers. The input invalidates itself by turning red when you enter invalid data or
|
||||
leave the input fields blank. These new widget behaviors make it easier to implement field
|
||||
validation common in CRUD applications.
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
|
||||
Total: {{qty * cost | currency}}
|
||||
|
||||
This notation, `{{ _expression_ }}`, is Angular markup for data-binding. The expression itself can
|
||||
be a combination of both an expression and a {@link dev_guide.templates.filters filter}: `{{
|
||||
expression | filter }}`. Angular provides filters for formatting display data.
|
||||
|
||||
In the example above, the expression in double-curly braces directs Angular to "bind the data we
|
||||
got from the input widgets to the display, multiply them together, and format the resulting number
|
||||
into output that looks like money."
|
||||
|
||||
Notice that we achieved this application behavior not by calling Angular methods, nor by
|
||||
implementing application specific behavior as a framework. We achieved the behavior because the
|
||||
browser behaved more in line with what is needed for a dynamic web application rather then what is
|
||||
needed for a static document. Angular has lowered the impedance mismatch to the point where no
|
||||
library/framework calls are needed.
|
||||
|
||||
|
||||
# The Zen of Angular
|
||||
|
||||
Angular is built around the belief that declarative code is better than imperative when it comes
|
||||
to building UIs and wiring software components together, while imperative code is excellent for
|
||||
expressing business logic.
|
||||
|
||||
|
||||
* It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
|
||||
the testability of the code.
|
||||
* It is a really, _really_ good idea to regard app testing as equal in importance to app
|
||||
writing. Testing difficulty is dramatically affected by the way the code is structured.
|
||||
* It is an excellent idea to decouple the client side of an app from the server side. This
|
||||
allows development work to progress in parallel, and allows for reuse of both sides.
|
||||
* It is very helpful indeed if the framework guides developers through the entire journey of
|
||||
building an app: from designing the UI, through writing the business logic, to testing.
|
||||
* It is always good to make common tasks trivial and difficult tasks possible.
|
||||
|
||||
|
||||
|
||||
Angular frees you from the following pain:
|
||||
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the
|
||||
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It
|
||||
vastly reduces the amount of JavaScript coding _you_ have to do, and it makes it easier to see
|
||||
what your application does.
|
||||
* **Manipulating HTML DOM programmatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it's cumbersome and error-prone. By declaratively describing how the UI
|
||||
should change as your application state changes, you are freed from low level DOM manipulation
|
||||
tasks. Most applications written with Angular never have to programmatically manipulate the
|
||||
DOM, although you can if you want to.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to an HTML
|
||||
form, allowing users to modify the form, validating the form, displaying validation errors,
|
||||
returning to an internal model, and then back to the server, creates a lot of boilerplate
|
||||
code. Angular eliminates almost all of this boilerplate, leaving code that describes the
|
||||
overall flow of the application rather than all of the implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot
|
||||
of plumbing just to get a basic "Hello World" AJAX app working. With Angular you can bootstrap
|
||||
your app easily using services, which are auto-injected into your application in a {@link
|
||||
http://code.google.com/p/google-guice/ Guice}-like dependency-injection style. This allows you
|
||||
to get started developing features quickly. As a bonus, you get full control over the
|
||||
initialization process in automated tests.
|
||||
|
||||
|
||||
# Watch a Presentation About Angular
|
||||
|
||||
Here is a presentation on Angular from May 2012. The {@link http://mhevery.github.io/angular-demo-slides/index.html#/list corresponding slides} are also available.
|
||||
|
||||
<iframe width="560" height="315" src="http://www.youtube.com/embed/bfrn5VNpwsg" frameborder="0" allowfullscreen></iframe>
|
||||
425
docs/content/guide/providers.ngdoc
Normal file
425
docs/content/guide/providers.ngdoc
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Providers
|
||||
@description
|
||||
|
||||
# Providers
|
||||
|
||||
Each web application you build is composed of objects that collaborate to get stuff done. These
|
||||
objects need to be instantiated and wired together for the app to work. In Angular apps most of
|
||||
these objects are instantiated and wired together automatically by the {@link api/AUTO.$injector
|
||||
injector service}.
|
||||
|
||||
The injector creates two types of objects, **services** and **specialized objects**.
|
||||
|
||||
Services are objects whose API is defined by the developer writing the service.
|
||||
|
||||
Specialized objects conform to a specific Angular framework API. These objects are one of
|
||||
controllers, directives, filters or animations.
|
||||
|
||||
The injector needs to know how to create these objects. You tell it by registering a "recipe" for
|
||||
creating your object with the injector. There are five recipe types.
|
||||
|
||||
The most verbose, but also the most comprehensive one is a Provider recipe. The remaining four
|
||||
recipe types — Value, Factory, Service and Constant — are just syntactic sugar on top of a provider
|
||||
recipe.
|
||||
|
||||
Let's take a look at the different scenarios for creating and using services via various recipe
|
||||
types. We'll start with the simplest case possible where various places in your code need a shared
|
||||
string and we'll accomplish this via Value recipe.
|
||||
|
||||
|
||||
## Note: A Word on Modules
|
||||
|
||||
In order for the injector to know how to create and wire together all of these objects, it needs
|
||||
a registry of "recipes". Each recipe has an identifier of the object and the description of how to
|
||||
create this object.
|
||||
|
||||
Each recipe belongs to an {@link api/angular.Module Angular module}. An Angular module is a bag
|
||||
that holds one or more recipes. And since manually keeping track of module dependencies is no fun,
|
||||
a module can contain information about dependencies on other modules as well.
|
||||
|
||||
When an Angular application starts with a given application module, Angular creates a new instance
|
||||
of injector, which in turn creates a registry of recipes as a union of all recipes defined in the
|
||||
core "ng" module, application module and its dependencies. The injector then consults the recipe
|
||||
registry when it needs to create an object for your application.
|
||||
|
||||
|
||||
## Value Recipe
|
||||
|
||||
Let's say that we want to have a very simple service called "clientId" that provides a string
|
||||
representing an authentication id used for some remote API. You would define it like this:
|
||||
|
||||
```javascript
|
||||
var myApp = angular.module('myApp', []);
|
||||
myApp.value('clientId', 'a12345654321x');
|
||||
```
|
||||
|
||||
Notice how we created an Angular module called `myApp`, and specified that this module definition
|
||||
contains a "recipe" for constructing the `clientId` service, which is a simple string in this case.
|
||||
|
||||
And this is how you would display it via Angular's data-binding:
|
||||
|
||||
|
||||
```javascript
|
||||
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
|
||||
this.clientId = clientId;
|
||||
}]);
|
||||
```
|
||||
|
||||
```html
|
||||
<html ng-app="myApp">
|
||||
<body ng-controller="DemoController as demo">
|
||||
Client ID: {{demo.clientId}}
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
In this example, we've used the Value recipe to define the value to provide when `DemoController`
|
||||
asks for the service with id "clientId".
|
||||
|
||||
On to more complex examples!
|
||||
|
||||
|
||||
## Factory Recipe
|
||||
|
||||
The Value recipe is very simple to write, but lacks some important features we often need when
|
||||
creating services. Let's now look at the Value recipe's more powerful sibling, the Factory.The
|
||||
Factory recipe adds the following abilities:
|
||||
|
||||
* ability to use other services (have dependencies)
|
||||
* service initialization
|
||||
* delayed/lazy initialization
|
||||
|
||||
The Factory recipe constructs a new service using a function with zero or more arguments (these
|
||||
are dependencies on other services). The return value of this function is the service instance
|
||||
created by this recipe.
|
||||
|
||||
Note: All services in Angular are singletons. That means that the injector uses each recipe at most
|
||||
once to create the object. The injector then caches the reference for all future needs.
|
||||
|
||||
Since Factory is more powerful version of Value recipe, you can construct the same service with it.
|
||||
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
|
||||
this:
|
||||
|
||||
```javascript
|
||||
myApp.factory('clientId', function clientIdFactory() {
|
||||
return 'a12345654321x';
|
||||
});
|
||||
```
|
||||
|
||||
But given that the token is just a string literal, sticking with the Value recipe is still more
|
||||
appropriate as it makes the code easier to follow.
|
||||
|
||||
Let's say, however, that we would also like to create a service that computes a token used for
|
||||
authentication against a remote API. This token will be called 'apiToken' and will be computed
|
||||
based on the `clientId` value and a secret stored in browser's local storage:
|
||||
|
||||
```javascript
|
||||
myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
|
||||
var encrypt = function(data1, data2) {
|
||||
// NSA-proof encryption algorithm:
|
||||
return (data1 + ':' + data2).toUpperCase();
|
||||
};
|
||||
|
||||
var secret = window.localStorage.getItem('myApp.secret');
|
||||
var apiToken = encrypt(clientId, secret);
|
||||
|
||||
return apiToken;
|
||||
}]);
|
||||
```
|
||||
|
||||
In the code above, we see how the `apiToken` service is defined via the Factory recipe that depends
|
||||
on `clientId` service. The factory service then uses NSA-proof encryption to produce an authentication
|
||||
token.
|
||||
|
||||
Note: It is a best practice to name the factory functions as "<serviceId>Factory"
|
||||
(e.g. apiTokenFactory). While this names are not required, they help when navigating the code base
|
||||
or looking at stack traces in the debugger.
|
||||
|
||||
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
|
||||
primitive, object literal, function, or even an instance of a custom type.
|
||||
|
||||
|
||||
## Service Recipe
|
||||
|
||||
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
|
||||
could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
|
||||
custom type:
|
||||
|
||||
```javascript
|
||||
function UnicornLauncher(apiToken) {
|
||||
|
||||
this.launchedCount = 0;
|
||||
this.launch() {
|
||||
// make a request to the remote api and include the apiToken
|
||||
...
|
||||
this.launchedCount++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We are now ready to launch unicorns, but notice that UnicornLauncher depends on our `apiToken`.
|
||||
We can satisfy this dependency on `apiToken` using the Factory recipe:
|
||||
|
||||
```javascript
|
||||
myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
|
||||
return new UnicornLauncher(apiToken);
|
||||
}]);
|
||||
```
|
||||
|
||||
|
||||
This is, however, exactly the use-case that Service recipe is the most suitable for.
|
||||
|
||||
The Service recipe produces a service just like the Value or Factory recipes, but it does so by
|
||||
*invoking a constructor with the `new` operator*. The constructor can take zero or more arguments,
|
||||
which represent dependencies needed by the instance of this type.
|
||||
|
||||
Note: Service recipes follow a design pattern called [constructor
|
||||
injection](http://www.martinfowler.com/articles/injection.html#ConstructorInjectionWithPicocontainer).
|
||||
|
||||
Since we already have a constructor for our UnicornLauncher type, we can replace the Factory recipe
|
||||
above with a Service recipe like this:
|
||||
|
||||
```javascript
|
||||
myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
|
||||
```
|
||||
|
||||
Much simpler!
|
||||
|
||||
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
|
||||
be somehow punished for our mis-deed. It's like we named one of our offspring 'Children'. Boy,
|
||||
that would mess with the teachers.
|
||||
|
||||
|
||||
## Provider Recipe
|
||||
|
||||
There are two more recipe types left to cover. They are both fairly specialized and are used
|
||||
infrequently. As already mentioned in the intro, the Provider recipe is the core recipe type and
|
||||
all the other recipe types are just syntactic sugar on top of it. It is the most verbose recipe
|
||||
with the most abilities, but for most services it's overkill.
|
||||
|
||||
Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
|
||||
method is a factory function just like the one we use in Factory recipe. In fact, if you define
|
||||
a Factory recipe, an empty Provider type with the `$get` method set to your factory function is
|
||||
automatically created under the hood.
|
||||
|
||||
You should use the Provider recipe only when you want to expose an API for application-wide
|
||||
configuration that must be made before the application starts. This is usually interesting only
|
||||
for reusable services whose behavior might need to vary slightly between applications.
|
||||
|
||||
Let's say that our `unicornLauncher` service is so awesome that many apps use it. By default the
|
||||
launcher shoots unicorns into space without any protective shielding. But on some planets the
|
||||
atmosphere is so thick that we must wrap every unicorn in tinfoil before sending it on its
|
||||
intergalactic trip, otherwise they would burn while passing through the atmosphere. It would then
|
||||
be great if we could configure the launcher to use the tinfoil shielding for each launch in apps
|
||||
that need it. We can make it configurable it like so:
|
||||
|
||||
|
||||
```javascript
|
||||
myApp.provider('unicornLauncher', function UnicornLauncherProvider() {
|
||||
var useTinfoilShielding = false;
|
||||
|
||||
this.useTinfoilShielding = function(value) {
|
||||
useTinfoilShielding = !!value;
|
||||
};
|
||||
|
||||
this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {
|
||||
|
||||
// let's assume that the UnicornLauncher constructor was also changed to
|
||||
// accept and use the useTinfoilShielding argument
|
||||
return new UnicornLauncher(apiToken, useTinfoilShielding);
|
||||
}];
|
||||
});
|
||||
```
|
||||
|
||||
To turn the tinfoil shielding on in our app, we need to create a config function via the module
|
||||
API and have the UnicornLauncherProvider injected into it:
|
||||
|
||||
```javascript
|
||||
myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) {
|
||||
unicornLauncherProvider.useTinfoilShielding(true);
|
||||
}]);
|
||||
```
|
||||
|
||||
Notice that the unicorn provider is injected into the config function. This injection is done by a
|
||||
provider injector which is different from the regular instance injector, in that it instantiates
|
||||
and wires (injects) all provider instances only.
|
||||
|
||||
During application bootstrap, before Angular goes off creating all services, it configures and
|
||||
instantiates all providers. We call this the configuration phase of the application life-cycle.
|
||||
During this phase services aren't accessible because they haven't been created yet.
|
||||
|
||||
Once the configuration phase is over, interaction with providers is disallowed and the process of
|
||||
creating services starts. We call this part of the application life-cycle the run phase.
|
||||
|
||||
|
||||
## Constant Recipe
|
||||
|
||||
We've just learned how Angular splits the life-cycle into configuration phase and run phase and how
|
||||
you can provide configuration to your application via the config function. Since the config
|
||||
function runs in the configuration phase when no services are available, it doesn't have access
|
||||
even to simple value objects created via Value recipe.
|
||||
|
||||
Since simple values, like url prefix, don't have dependencies or configuration, it is often handy
|
||||
to make them available in both the configuration and run phases. This is what the Constant recipe
|
||||
is for.
|
||||
|
||||
Let's say that our `unicornLauncher` service can stamp a unicorn with the planet name it's being
|
||||
launched from if this name was provided during the configuration phase. The planet name is
|
||||
application specific and is used also by various controllers during the runtime of the application.
|
||||
We can then define the planet name as a constant like this:
|
||||
|
||||
```javascript
|
||||
myApp.constant('planetName', 'Greasy Giant');
|
||||
```
|
||||
|
||||
We could then configure the unicornLauncherProvider like this:
|
||||
|
||||
```javascript
|
||||
myApp.config(['unicornLauncherProvider', 'planetName', function(unicornLauncherProvider, planetName) {
|
||||
unicornLauncherProvider.useTinfoilShielding(true);
|
||||
unicornLauncherProvider.stampText(planetName);
|
||||
}]);
|
||||
```
|
||||
|
||||
And since Constant recipe makes the value also available at runtime just like the Value recipe, we
|
||||
can also use it in our controller and template:
|
||||
|
||||
```javascript
|
||||
myApp.controller('DemoController', ["clientId", "planetName", function DemoController(clientId, planetName) {
|
||||
this.clientId = clientId;
|
||||
this.planetName = planetName;
|
||||
}]);
|
||||
```
|
||||
|
||||
```html
|
||||
<html ng-app="myApp">
|
||||
<body ng-controller="DemoController as demo">
|
||||
Client ID: {{demo.clientId}}
|
||||
<br>
|
||||
Planet Name: {{demo.planetName}}
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
## Special Purpose Objects
|
||||
|
||||
Earlier we mentioned that we also have special purpose objects that are different from services.
|
||||
These objects extend the framework as plugins and therefore must implement interfaces specified by
|
||||
Angular. These interfaces are Controller, Directive, Filter and Animation.
|
||||
|
||||
The instructions for the injector to create these special objects (with the exception of the
|
||||
Controller objects) use the Factory recipe behind the scenes.
|
||||
|
||||
Let's take a look at how we would create a very simple component via the directive api that depends
|
||||
on the `planetName` constant we've just defined and displays the planet name, in our case:
|
||||
"Planet Name: Greasy Giant".
|
||||
|
||||
Since the directives are registered via Factory recipe, we can use the same syntax as with factories.
|
||||
|
||||
```javascript
|
||||
myApp.directive('myPlanet', ['planetName', function myPlanetDirectiveFactory(planetName) {
|
||||
// directive definition object
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {},
|
||||
link: function($scope, $element) { $element.text('Planet: ' + planetName); }
|
||||
}
|
||||
}]);
|
||||
```
|
||||
|
||||
We can then use the component like this:
|
||||
|
||||
```html
|
||||
<html ng-app="myApp">
|
||||
<body>
|
||||
<my-planet></my-planet>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Using Factory recipes you can also define Angular's filters and animations, but the controllers
|
||||
are a bit special. You create a controller as a custom type that declares its dependencies as
|
||||
arguments for its constructor function. This constructor is then registered with a module. Let's
|
||||
take a look at the `DemoController`, created in one of the early examples:
|
||||
|
||||
```javascript
|
||||
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
|
||||
this.clientId = clientId;
|
||||
}]);
|
||||
```
|
||||
|
||||
The DemoController is instantiated via its constructor every time the app needs an instance of
|
||||
DemoController (in our simple app it's just once). So unlike services, controllers are not
|
||||
singletons. The constructor is called with all the requested services, in our case the `clientId`
|
||||
service.
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
To wrap it up, let's summarize the most important points:
|
||||
|
||||
- The injector uses recipes to create two types of objects: services and special purpose objects
|
||||
- There are five recipe types that define how to create objects: Value, Factory, Service, Provider
|
||||
and Constant.
|
||||
- Factory and Service are the most commonly used recipes. The only difference between them is that
|
||||
Service recipe works better for objects of custom type, while Factory can produce JavaScript
|
||||
primitives and functions.
|
||||
- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
|
||||
- Provider is the most complex recipe type. You don't need it unless you are building a reusable
|
||||
piece of code that needs global configuration.
|
||||
- All special purpose objects except for Controller are defined via Factory recipes.
|
||||
|
||||
<table class="table table-bordered code-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Features / Recipe type</th>
|
||||
<th>Factory</th>
|
||||
<th>Service</th>
|
||||
<th>Value</th>
|
||||
<th>Constant</th>
|
||||
<th>Provider</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>can have dependencies</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="error">no</td>
|
||||
<td class="error">no</td>
|
||||
<td class="success">yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>uses type friendly injection</td>
|
||||
<td class="error">no</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes\*</td>
|
||||
<td class="success">yes\*</td>
|
||||
<td class="error">no</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>object available in config phase</td>
|
||||
<td class="error">no</td>
|
||||
<td class="error">no</td>
|
||||
<td class="error">no</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes\*\*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>can create functions/primitives</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="error">no</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
\* at the cost of eager initialization by using `new` operator directly
|
||||
|
||||
\*\* the service object is not available during the config phase, but the provider instance is
|
||||
(see the `unicornLauncherProvider` example above).
|
||||
|
||||
|
|
@ -11,15 +11,19 @@ watch {@link guide/expression expressions} and propagate events.
|
|||
|
||||
## Scope characteristics
|
||||
|
||||
- Scopes provide APIs ({@link api/ng.$rootScope.Scope#$watch $watch}) to observe
|
||||
- Scopes provide APIs ({@link api/ng.$rootScope.Scope#methods_$watch $watch}) to observe
|
||||
model mutations.
|
||||
|
||||
- Scopes provide APIs ({@link api/ng.$rootScope.Scope#$apply $apply}) to
|
||||
- Scopes provide APIs ({@link api/ng.$rootScope.Scope#methods_$apply $apply}) to
|
||||
propagate any model changes through the system into the view from outside of the "Angular
|
||||
realm" (controllers, services, Angular event handlers).
|
||||
|
||||
- Scopes can be nested to isolate application components while providing access to shared model
|
||||
properties. A scope (prototypically) inherits properties from its parent scope.
|
||||
- Scopes can be nested to limit access to the properties of application components while providing
|
||||
access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes".
|
||||
A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope"
|
||||
does not. See {@link
|
||||
guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive isolated
|
||||
scopes} for more information.
|
||||
|
||||
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
|
||||
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
|
||||
|
|
@ -28,8 +32,8 @@ watch {@link guide/expression expressions} and propagate events.
|
|||
## Scope as Data-Model
|
||||
|
||||
Scope is the glue between application controller and the view. During the template {@link compiler
|
||||
linking} phase the {@link api/ng.$compileProvider#directive directives} set up
|
||||
{@link api/ng.$rootScope.Scope#$watch `$watch`} expressions on the scope. The
|
||||
linking} phase the {@link api/ng.$compileProvider#methods_directive directives} set up
|
||||
{@link api/ng.$rootScope.Scope#methods_$watch `$watch`} expressions on the scope. The
|
||||
`$watch` allows the directives to be notified of property changes, which allows the directive to
|
||||
render the updated value to the DOM.
|
||||
|
||||
|
|
@ -110,50 +114,51 @@ new child scopes (refer to directive documentation to see which directives creat
|
|||
When new scopes are created, they are added as children of their parent scope. This creates a tree
|
||||
structure which parallels the DOM where they're attached
|
||||
|
||||
When Angular evaluates `{{username}}`, it first looks at the scope associated with the given
|
||||
element for the `username` property. If no such property is found, it searches the parent scope
|
||||
When Angular evaluates `{{name}}`, it first looks at the scope associated with the given
|
||||
element for the `name` property. If no such property is found, it searches the parent scope
|
||||
and so on until the root scope is reached. In JavaScript this behavior is known as prototypical
|
||||
inheritance, and child scopes prototypically inherit from their parents.
|
||||
|
||||
This example illustrates scopes in application, and prototypical inheritance of properties.
|
||||
This example illustrates scopes in application, and prototypical inheritance of properties. The example is followed by
|
||||
a diagram depicting the scope boundaries.
|
||||
|
||||
<example>
|
||||
<file name="style.css">
|
||||
/* remove .doc-example-live in jsfiddle */
|
||||
.doc-example-live .ng-scope {
|
||||
border: 1px dashed red;
|
||||
}
|
||||
<file name="index.html">
|
||||
<div class="show-scope-demo">
|
||||
<div ng-controller="GreetCtrl">
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
<div ng-controller="ListCtrl">
|
||||
<ol>
|
||||
<li ng-repeat="name in names">{{name}} from {{department}}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function EmployeeController($scope) {
|
||||
$scope.department = 'Engineering';
|
||||
$scope.employee = {
|
||||
name: 'Joe the Manager',
|
||||
reports: [
|
||||
{name: 'John Smith'},
|
||||
{name: 'Mary Run'}
|
||||
]
|
||||
};
|
||||
function GreetCtrl($scope, $rootScope) {
|
||||
$scope.name = 'World';
|
||||
$rootScope.department = 'Angular';
|
||||
}
|
||||
|
||||
function ListCtrl($scope) {
|
||||
$scope.names = ['Igor', 'Misko', 'Vojta'];
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="EmployeeController">
|
||||
Manager: {{employee.name}} [ {{department}} ]<br>
|
||||
Reports:
|
||||
<ul>
|
||||
<li ng-repeat="employee in employee.reports">
|
||||
{{employee.name}} [ {{department}} ]
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
{{greeting}}
|
||||
</div>
|
||||
<file name="style.css">
|
||||
.show-scope-demo.ng-scope,
|
||||
.show-scope-demo .ng-scope {
|
||||
border: 1px solid red;
|
||||
margin: 3px;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<img class="center" src="img/guide/concepts-scope.png">
|
||||
|
||||
Notice that Angular automatically places `ng-scope` class on elements where scopes are
|
||||
attached. The `<style>` definition in this example highlights in red the new scope locations. The
|
||||
child scopes are necessary because the repeater evaluates `{{employee.name}}` expression, but
|
||||
child scopes are necessary because the repeater evaluates `{{name}}` expression, but
|
||||
depending on which scope the expression is evaluated it produces different result. Similarly the
|
||||
evaluation of `{{department}}` prototypically inherits from root scope, as it is the only place
|
||||
where the `department` property is defined.
|
||||
|
|
@ -176,14 +181,14 @@ To examine the scope in the debugger:
|
|||
2. The debugger allows you to access the currently selected element in the console as `$0`
|
||||
variable.
|
||||
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()`
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()` or just type $scope
|
||||
|
||||
|
||||
## Scope Events Propagation
|
||||
|
||||
Scopes can propagate events in similar fashion to DOM events. The event can be {@link
|
||||
api/ng.$rootScope.Scope#$broadcast broadcasted} to the scope children or {@link
|
||||
api/ng.$rootScope.Scope#$emit emitted} to scope parents.
|
||||
api/ng.$rootScope.Scope#methods_$broadcast broadcasted} to the scope children or {@link
|
||||
api/ng.$rootScope.Scope#methods_$emit emitted} to scope parents.
|
||||
|
||||
<example>
|
||||
<file name="script.js">
|
||||
|
|
@ -225,14 +230,14 @@ more events.
|
|||
When the browser calls into JavaScript the code executes outside the Angular execution context,
|
||||
which means that Angular is unaware of model modifications. To properly process model
|
||||
modifications the execution has to enter the Angular execution context using the {@link
|
||||
api/ng.$rootScope.Scope#$apply `$apply`} method. Only model modifications which
|
||||
api/ng.$rootScope.Scope#methods_$apply `$apply`} method. Only model modifications which
|
||||
execute inside the `$apply` method will be properly accounted for by Angular. For example if a
|
||||
directive listens on DOM events, such as {@link
|
||||
api/ng.directive:ngClick `ng-click`} it must evaluate the
|
||||
expression inside the `$apply` method.
|
||||
|
||||
After evaluating the expression, the `$apply` method performs a {@link
|
||||
api/ng.$rootScope.Scope#$digest `$digest`}. In the $digest phase the scope examines all
|
||||
api/ng.$rootScope.Scope#methods_$digest `$digest`}. In the $digest phase the scope examines all
|
||||
of the `$watch` expressions and compares them with the previous value. This dirty checking is done
|
||||
asynchronously. This means that assignment such as `$scope.username="angular"` will not
|
||||
immediately cause a `$watch` to be notified, instead the `$watch` notification is delayed until
|
||||
|
|
@ -250,20 +255,20 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
|||
2. **Watcher registration**
|
||||
|
||||
During template linking directives register {@link
|
||||
api/ng.$rootScope.Scope#$watch watches} on the scope. These watches will be
|
||||
api/ng.$rootScope.Scope#methods_$watch watches} on the scope. These watches will be
|
||||
used to propagate model values to the DOM.
|
||||
|
||||
3. **Model mutation**
|
||||
|
||||
For mutations to be properly observed, you should make them only within the {@link
|
||||
api/ng.$rootScope.Scope#$apply scope.$apply()}. (Angular APIs do this
|
||||
api/ng.$rootScope.Scope#methods_$apply scope.$apply()}. (Angular APIs do this
|
||||
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
|
||||
or asynchronous work with {@link api/ng.$http $http} or {@link
|
||||
api/ng.$timeout $timeout} services.
|
||||
or asynchronous work with {@link api/ng.$http $http}, {@link api/ng.$timeout $timeout}
|
||||
or {@link api/ng.$interval $interval} services.
|
||||
|
||||
4. **Mutation observation**
|
||||
|
||||
At the end `$apply`, Angular performs a {@link api/ng.$rootScope.Scope#$digest
|
||||
At the end `$apply`, Angular performs a {@link api/ng.$rootScope.Scope#methods_$digest
|
||||
$digest} cycle on the root scope, which then propagates throughout all child scopes. During
|
||||
the `$digest` cycle, all `$watch`ed expressions or functions are checked for model mutation
|
||||
and if a mutation is detected, the `$watch` listener is called.
|
||||
|
|
@ -271,7 +276,7 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
|||
5. **Scope destruction**
|
||||
|
||||
When child scopes are no longer needed, it is the responsibility of the child scope creator
|
||||
to destroy them via {@link api/ng.$rootScope.Scope#$destroy scope.$destroy()}
|
||||
to destroy them via {@link api/ng.$rootScope.Scope#methods_$destroy scope.$destroy()}
|
||||
API. This will stop propagation of `$digest` calls into the child scope and allow for memory
|
||||
used by the child scope models to be reclaimed by the garbage collector.
|
||||
|
||||
|
|
@ -279,32 +284,34 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
|||
### Scopes and Directives
|
||||
|
||||
During the compilation phase, the {@link compiler compiler} matches {@link
|
||||
api/ng.$compileProvider#directive directives} against the DOM template. The directives
|
||||
api/ng.$compileProvider#methods_directive directives} against the DOM template. The directives
|
||||
usually fall into one of two categories:
|
||||
|
||||
- Observing {@link api/ng.$compileProvider#directive directives}, such as
|
||||
- Observing {@link api/ng.$compileProvider#methods_directive directives}, such as
|
||||
double-curly expressions `{{expression}}`, register listeners using the {@link
|
||||
api/ng.$rootScope.Scope#$watch $watch()} method. This type of directive needs
|
||||
api/ng.$rootScope.Scope#methods_$watch $watch()} method. This type of directive needs
|
||||
to be notified whenever the expression changes so that it can update the view.
|
||||
|
||||
- Listener directives, such as {@link api/ng.directive:ngClick
|
||||
ng-click}, register a listener with the DOM. When the DOM listener fires, the directive
|
||||
executes the associated expression and updates the view using the {@link
|
||||
api/ng.$rootScope.Scope#$apply $apply()} method.
|
||||
api/ng.$rootScope.Scope#methods_$apply $apply()} method.
|
||||
|
||||
When an external event (such as a user action, timer or XHR) is received, the associated {@link
|
||||
expression expression} must be applied to the scope through the {@link
|
||||
api/ng.$rootScope.Scope#$apply $apply()} method so that all listeners are updated
|
||||
api/ng.$rootScope.Scope#methods_$apply $apply()} method so that all listeners are updated
|
||||
correctly.
|
||||
|
||||
### Directives that Create Scopes
|
||||
|
||||
In most cases, {@link api/ng.$compileProvider#directive directives} and scopes interact
|
||||
In most cases, {@link api/ng.$compileProvider#methods_directive directives} and scopes interact
|
||||
but do not create new instances of scope. However, some directives, such as {@link
|
||||
api/ng.directive:ngController ng-controller} and {@link
|
||||
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
|
||||
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
||||
element by using an `angular.element(aDomElement).scope()` method call.
|
||||
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||
directives guide} for more information about isolate scopes.
|
||||
|
||||
### Controllers and Scopes
|
||||
|
||||
|
|
@ -315,7 +322,7 @@ Scopes and controllers interact with each other in the following situations:
|
|||
|
||||
- Controllers define methods (behavior) that can mutate the model (properties on the scope).
|
||||
|
||||
- Controllers may register {@link api/ng.$rootScope.Scope#$watch watches} on
|
||||
- Controllers may register {@link api/ng.$rootScope.Scope#methods_$watch watches} on
|
||||
the model. These watches execute immediately after the controller behavior executes.
|
||||
|
||||
See the {@link api/ng.directive:ngController ng-controller} for more
|
||||
|
|
@ -329,3 +336,72 @@ the dirty checking function must be efficient. Care should be taken that the dir
|
|||
function does not do any DOM access, as DOM access is orders of magnitude slower then property
|
||||
access on JavaScript object.
|
||||
|
||||
## Integration with the browser event loop
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png">
|
||||
|
||||
The diagram and the example below describe how Angular interacts with the browser's event loop.
|
||||
|
||||
1. The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event,
|
||||
or network event (response from a server).
|
||||
2. The event's callback gets executed. This enters the JavaScript context. The callback can
|
||||
modify the DOM structure.
|
||||
3. Once the callback executes, the browser leaves the JavaScript context and
|
||||
re-renders the view based on DOM changes.
|
||||
|
||||
Angular modifies the normal JavaScript flow by providing its own event processing loop. This
|
||||
splits the JavaScript into classical and Angular execution context. Only operations which are
|
||||
applied in Angular execution context will benefit from Angular data-binding, exception handling,
|
||||
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
|
||||
mind that in most places (controllers, services) $apply has already been called for you by the
|
||||
directive which is handling the event. An explicit call to $apply is needed only when
|
||||
implementing custom event callbacks, or when working with third-party library callbacks.
|
||||
|
||||
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
|
||||
api/ng.$rootScope.Scope#methods_$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
|
||||
the work you wish to do in Angular execution context.
|
||||
2. Angular executes the `stimulusFn()`, which typically modifies application state.
|
||||
3. Angular enters the {@link api/ng.$rootScope.Scope#methods_$digest $digest} loop. The
|
||||
loop is made up of two smaller loops which process {@link
|
||||
api/ng.$rootScope.Scope#methods_$evalAsync $evalAsync} queue and the {@link
|
||||
api/ng.$rootScope.Scope#methods_$watch $watch} list. The {@link
|
||||
api/ng.$rootScope.Scope#methods_$digest $digest} loop keeps iterating until the model
|
||||
stabilizes, which means that the {@link api/ng.$rootScope.Scope#methods_$evalAsync
|
||||
$evalAsync} queue is empty and the {@link api/ng.$rootScope.Scope#methods_$watch
|
||||
$watch} list does not detect any changes.
|
||||
4. The {@link api/ng.$rootScope.Scope#methods_$evalAsync $evalAsync} queue is used to
|
||||
schedule work which needs to occur outside of current stack frame, but before the browser's
|
||||
view render. This is usually done with `setTimeout(0)`, but the `setTimeout(0)` approach
|
||||
suffers from slowness and may cause view flickering since the browser renders the view after
|
||||
each event.
|
||||
5. The {@link api/ng.$rootScope.Scope#methods_$watch $watch} list is a set of expressions
|
||||
which may have changed since last iteration. If a change is detected then the `$watch`
|
||||
function is called which typically updates the DOM with the new value.
|
||||
6. Once the Angular {@link api/ng.$rootScope.Scope#methods_$digest $digest} loop finishes
|
||||
the execution leaves the Angular and JavaScript context. This is followed by the browser
|
||||
re-rendering the DOM to reflect any changes.
|
||||
|
||||
|
||||
Here is the explanation of how the `Hello world` example achieves the data-binding effect when the
|
||||
user enters text into the text field.
|
||||
|
||||
1. During the compilation phase:
|
||||
1. the {@link api/ng.directive:ngModel ng-model} and {@link
|
||||
api/ng.directive:input input} {@link guide/directive
|
||||
directive} set up a `keydown` listener on the `<input>` control.
|
||||
2. the {@link api/ng.$interpolate {{name}} } interpolation
|
||||
sets up a {@link api/ng.$rootScope.Scope#methods_$watch $watch} to be notified of
|
||||
`name` changes.
|
||||
2. During the runtime phase:
|
||||
1. Pressing an '`X`' key causes the browser to emit a `keydown` event on the input control.
|
||||
2. The {@link api/ng.directive:input input} directive
|
||||
captures the change to the input's value and calls {@link
|
||||
api/ng.$rootScope.Scope#methods_$apply $apply}`("name = 'X';")` to update the
|
||||
application model inside the Angular execution context.
|
||||
3. Angular applies the `name = 'X';` to the model.
|
||||
4. The {@link api/ng.$rootScope.Scope#methods_$digest $digest} loop begins
|
||||
5. The {@link api/ng.$rootScope.Scope#methods_$watch $watch} list detects a change
|
||||
on the `name` property and notifies the {@link api/ng.$interpolate
|
||||
{{name}} } interpolation, which in turn updates the DOM.
|
||||
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
|
||||
the JavaScript execution context.
|
||||
7. The browser re-renders the view with update text.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Understanding Angular Templates
|
||||
@name Templates
|
||||
@description
|
||||
|
||||
An Angular template is the declarative specification that, along with information from the model
|
||||
|
|
@ -14,7 +14,7 @@ These are the types of Angular elements and element attributes you can use in a
|
|||
augments an existing DOM element or represents a reusable DOM component - a widget.
|
||||
* {@link api/ng.$interpolate Markup} — The double
|
||||
curly brace notation `{{ }}` to bind expressions to elements is built-in angular markup.
|
||||
* {@link dev_guide.templates.filters Filter} — Formats your data for display to the user.
|
||||
* {@link filter Filter} — Formats your data for display to the user.
|
||||
* {@link forms Form controls} — Lets you validate user input.
|
||||
|
||||
Note: In addition to declaring the elements above in templates, you can also access these elements
|
||||
|
|
@ -49,7 +49,7 @@ eight.
|
|||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link filter Angular Filters}
|
||||
* {@link forms Angular Forms}
|
||||
|
||||
## Related API
|
||||
|
|
@ -1,157 +1,106 @@
|
|||
@ngdoc overview
|
||||
@name Contributing
|
||||
@name Develop
|
||||
@description
|
||||
|
||||
# Building and Testing AngularJS
|
||||
|
||||
* <a href="#H1_1">License</a>
|
||||
* <a href="#H1_2">Contributing to Source Code</a>
|
||||
* <a href="#H1_3">Applying Code Standards</a>
|
||||
* <a href="#H1_4">Checking Out and Building `Angular`</a>
|
||||
* <a href="#H1_5">Submitting Your Changes</a>
|
||||
This document describes how to set up your development environment to build and test AngularJS, and
|
||||
explains the basic mechanics of using `git`, `node`, `npm`, `grunt`, and `bower`.
|
||||
|
||||
See the [contributing guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
for how to contribute your own code to AngularJS.
|
||||
|
||||
|
||||
<a name="H1_1"></a>
|
||||
# License
|
||||
1. {@link #building-and-testing-angularjs_installing-dependencies Installing Dependencies}
|
||||
2. {@link #building-and-testing-angularjs_forking-angular-on-github Forking Angular on Github}
|
||||
3. {@link #building-and-testing-angularjs_building-angularjs Building AngularJS}
|
||||
4. {@link #building-and-testing-angularjs_running-a-local-development-web-server Running a Local Development Web Server}
|
||||
5. {@link #building-and-testing-angularjs_running-the-unit-test-suite Running the Unit Test Suite}
|
||||
6. {@link #building-and-testing-angularjs_running-the-end-to-end-test-suite Running the End-to-end Test Suite}
|
||||
|
||||
AngularJS is an open source project licensed under the {@link
|
||||
http://github.com/angular/angular.js/blob/master/LICENSE MIT license}. Your contributions are
|
||||
always welcome. When working with AngularJS code base, please follow the guidelines provided on
|
||||
this page.
|
||||
## Installing Dependencies
|
||||
|
||||
|
||||
<a name="H1_2"></a>
|
||||
# Contributing to Source Code
|
||||
|
||||
We'd love for you to contribute to our source code and to make AngularJS even better than it is
|
||||
today! Here are the guidelines we'd like you to follow:
|
||||
|
||||
* Major changes that you intend to contribute to the project should be discussed first on our {@link
|
||||
https://groups.google.com/forum/?hl=en#!forum/angular mailing list} so that we can better
|
||||
coordinate our efforts, prevent duplication of work, and help you to craft the change so that it
|
||||
is successfully accepted upstream.
|
||||
|
||||
* Small changes and bug fixes can be crafted and submitted to Github as a <a href="#H1_5">pull
|
||||
request</a>.
|
||||
|
||||
|
||||
|
||||
<a name="H1_3"></a>
|
||||
# Applying Code Standards
|
||||
|
||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||
|
||||
* All features or bug fixes must be tested by one or more <a href="#unit-tests">specs</a>.
|
||||
|
||||
* All public API methods must be documented with ngdoc, an extended version of jsdoc (we added
|
||||
support for markdown and templating via `@ngdoc` tag). To see how we document our APIs, please
|
||||
check out the existing ngdocs.
|
||||
|
||||
* With the exceptions listed below, we follow the rules contained in {@link
|
||||
http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml Google's JavaScript Style
|
||||
Guide}:
|
||||
|
||||
* Do not use namespaces: Instead, we wrap the entire `angular` code base in an anonymous closure
|
||||
and export our API explicitly rather than implicitly.
|
||||
|
||||
* Wrap all code at 100 characters.
|
||||
|
||||
* Instead of complex inheritance hierarchies, we prefer simple objects. We use prototypical
|
||||
inheritance only when absolutely necessary.
|
||||
|
||||
* We love functions and closures and, whenever possible, prefer them over objects.
|
||||
|
||||
* To write concise code that can be better minified, internally we use aliases that map to the
|
||||
external API. See our existing code to see what we mean.
|
||||
|
||||
* We don't go crazy with type annotations for private internal APIs unless it's an internal API
|
||||
that is used throughout AngularJS. The best guidance is to do what makes the most sense.
|
||||
|
||||
|
||||
<a name="H1_4"></a>
|
||||
# Checking Out and Building Angular
|
||||
|
||||
The AngularJS source code is hosted at {@link http://github.com Github}, which we also use to
|
||||
accept code contributions. The AngularJS repository can be found at **<https://github.com/angular/angular.js>**.
|
||||
|
||||
Several steps are needed to check out and build AngularJS:
|
||||
|
||||
|
||||
## Installation Dependencies
|
||||
|
||||
Before you can build AngularJS, you must install or configure the following dependencies on your
|
||||
Before you can build AngularJS, you must install and configure the following dependencies on your
|
||||
machine:
|
||||
|
||||
* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is
|
||||
quite a good source for information on Git.
|
||||
* {@link http://git-scm.com/ Git}: The {@link https://help.github.com/articles/set-up-git Github Guide to
|
||||
Installing Git} is a good source of information.
|
||||
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
||||
development web server, run tests, and generate a build. Depending on your system, you can install Node either from source or as a
|
||||
development web server, run tests, and generate distributable files. Depending on your system, you can install Node either from source or as a
|
||||
pre-packaged bundle.
|
||||
|
||||
* {@link http://www.java.com Java}: JavaScript is minified using
|
||||
* {@link http://www.java.com Java}: We minify JavaScript using our
|
||||
{@link https://developers.google.com/closure/ Closure Tools} jar. Make sure you have Java (version 6 or higher) installed
|
||||
and included in your {@link http://docs.oracle.com/javase/tutorial/essential/environment/paths.html PATH} variable.
|
||||
|
||||
Once installed, you'll also need several npms (node packages), which you can install once you checked out a local copy
|
||||
of the Angular repository (see below) with:
|
||||
|
||||
* `cd angular.js`
|
||||
* `npm install`
|
||||
* `bower install`
|
||||
|
||||
* {@link http://gruntjs.com Grunt}: We use Grunt as our build system. Install the grunt command-line tool globally with:
|
||||
|
||||
* `sudo npm install -g grunt-cli`
|
||||
```shell
|
||||
npm install -g grunt-cli
|
||||
```
|
||||
|
||||
* {@link http://bower.io/ Bower}: Bower is used to manage packages for the docs. Install the bower tool globally with:
|
||||
* {@link http://bower.io/ Bower}: We use Bower to manage client-side packages for the docs. Install the `bower` command-line tool globally with:
|
||||
|
||||
* `sudo npm install -g bower`
|
||||
```shell
|
||||
npm install -g bower
|
||||
```
|
||||
|
||||
**Note:** You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Grunt &
|
||||
Bower globally.
|
||||
|
||||
## Creating a Github Account and Forking Angular
|
||||
## Forking Angular on Github
|
||||
|
||||
To create a Github account, follow the instructions {@link https://github.com/signup/free here}.
|
||||
Afterwards, go ahead and {@link http://help.github.com/forking fork} the {@link
|
||||
https://github.com/angular/angular.js main angular repository}.
|
||||
https://github.com/angular/angular.js main AngularJS repository}.
|
||||
|
||||
|
||||
## Building AngularJS
|
||||
|
||||
To build AngularJS, you check out the source code and use Grunt to generate the non-minified and
|
||||
To build AngularJS, you clone the source code repository and use Grunt to generate the non-minified and
|
||||
minified AngularJS files:
|
||||
|
||||
1. To clone your Github repository, run:
|
||||
```shell
|
||||
# Clone your Github repository:
|
||||
git clone git@github.com:<github username>/angular.js.git
|
||||
|
||||
git clone git@github.com:<github username>/angular.js.git
|
||||
# Go to the AngularJS directory:
|
||||
cd angular.js
|
||||
|
||||
2. To go to the AngularJS directory, run:
|
||||
# Add the main AngularJS repository as an upstream remote to your repository:
|
||||
git remote add upstream https://github.com/angular/angular.js.git
|
||||
|
||||
cd angular.js
|
||||
# Install node.js dependencies:
|
||||
npm install
|
||||
|
||||
3. To add the main AngularJS repository as an upstream remote to your repository, run:
|
||||
# Install bower components:
|
||||
bower install
|
||||
|
||||
git remote add upstream https://github.com/angular/angular.js.git
|
||||
# Build AngularJS:
|
||||
grunt package
|
||||
```
|
||||
|
||||
4. To add node.js dependencies
|
||||
|
||||
npm install
|
||||
<div class="alert alert-warning">
|
||||
**Note:** If you're using Windows, you must use an elevated command prompt (right click, run as
|
||||
Administrator). This is because `grunt package` creates some symbolic links.
|
||||
</div>
|
||||
|
||||
5. To add docs components
|
||||
|
||||
bower install
|
||||
|
||||
6. To build AngularJS, run:
|
||||
|
||||
grunt package
|
||||
|
||||
NOTE: If you're using Windows you must run your command line with administrative privileges (right click, run as
|
||||
Administrator).
|
||||
<div class="alert alert-warning">
|
||||
**Note:** If you're using Linux, and npm install fails with the message
|
||||
'Please try running this command again as root/Administrator.', you may need to globally install grunt and bower:
|
||||
<ul>
|
||||
<li>sudo npm install -g grunt-cli</li>
|
||||
<li>sudo npm install -g bower</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
|
||||
* `angular-<version>.zip` — This is the complete zip file, which contains all of the release build
|
||||
* `angular-<version>.zip` — The complete zip file, containing all of the release build
|
||||
artifacts.
|
||||
|
||||
* `angular.js` — The non-minified `angular` script.
|
||||
|
|
@ -167,34 +116,37 @@ artifacts.
|
|||
* `docs/docs-scenario.html` — The End2End test runner for the documentation application.
|
||||
|
||||
|
||||
<a name="webserver"></a>
|
||||
## Running a Local Development Web Server
|
||||
|
||||
To debug code and run end-to-end tests, it is often useful to have a local HTTP server. For this purpose, we have
|
||||
made available a local web server based on Node.js.
|
||||
|
||||
1. To start the web server, run:
|
||||
```shell
|
||||
grunt webserver
|
||||
```
|
||||
|
||||
grunt webserver
|
||||
|
||||
2. To access the local server, go to this website:
|
||||
|
||||
http://localhost:8000/
|
||||
|
||||
2. To access the local server, enter the following URL into your web browser:
|
||||
```
|
||||
http://localhost:8000/
|
||||
```
|
||||
By default, it serves the contents of the AngularJS project directory.
|
||||
|
||||
|
||||
<a name="unit-tests"></a>
|
||||
## Running the Unit Test Suite
|
||||
|
||||
Our unit and integration tests are written with Jasmine and executed with Karma. To run all of the
|
||||
We write unit and integration tests with Jasmine and execute them with Karma. To run all of the
|
||||
tests once on Chrome run:
|
||||
|
||||
grunt test:unit
|
||||
```shell
|
||||
grunt test:unit
|
||||
```
|
||||
|
||||
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
|
||||
|
||||
grunt test:unit --browsers Opera,Firefox
|
||||
```shell
|
||||
grunt test:unit --browsers Opera,Firefox
|
||||
```
|
||||
|
||||
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
|
||||
|
||||
|
|
@ -203,96 +155,34 @@ change. To execute tests in this mode run:
|
|||
|
||||
1. To start the Karma server, capture Chrome browser and run unit tests, run:
|
||||
|
||||
grunt autotest:jqlite
|
||||
```shell
|
||||
grunt autotest
|
||||
```
|
||||
|
||||
2. To capture more browsers, open this url in the desired browser (url might be different if you have multiple instance
|
||||
of Karma running, read Karma's console output for the correct url):
|
||||
2. To capture more browsers, open this URL in the desired browser (URL might be different if you have multiple instance
|
||||
of Karma running, read Karma's console output for the correct URL):
|
||||
|
||||
http://localhost:9876/
|
||||
```shell
|
||||
http://localhost:9876/
|
||||
```
|
||||
|
||||
3. To re-run tests just change any source or test file.
|
||||
|
||||
|
||||
To learn more about all of the preconfigured Grunt tasks run:
|
||||
|
||||
grunt --help
|
||||
```shell
|
||||
grunt --help
|
||||
```
|
||||
|
||||
|
||||
## Running the end-to-end Test Suite
|
||||
## Running the End-to-end Test Suite
|
||||
|
||||
To run the E2E test suite:
|
||||
Simply run:
|
||||
|
||||
1. Start the local web server if it's not running already.
|
||||
```shell
|
||||
grunt test:e2e
|
||||
```
|
||||
|
||||
grunt webserver
|
||||
This will start the webserver and run the tests.
|
||||
|
||||
2. In a browser, go to:
|
||||
|
||||
http://localhost:8000/build/docs/docs-scenario.html
|
||||
|
||||
or in terminal run:
|
||||
|
||||
grunt test:end2end
|
||||
|
||||
For convenience you can also simply run:
|
||||
|
||||
grunt test:e2e
|
||||
|
||||
This will start the webserver for you and run the tests.
|
||||
|
||||
|
||||
|
||||
<a name="H1_5"></a>
|
||||
# Submitting Your Changes
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
1. <a name="CLA"></a>
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
For individuals we have a [simple click-through form](http://code.google.com/legal/individual-cla-v1.0.html). For
|
||||
corporations we'll need you to
|
||||
[print, sign and one of scan+email, fax or mail the form](http://code.google.com/legal/corporate-cla-v1.0.html).
|
||||
|
||||
|
||||
2. Create and checkout a new branch off the master branch for your changes:
|
||||
|
||||
git checkout -b my-fix-branch master
|
||||
|
||||
3. Create your patch, make sure to have plenty of tests (that pass).
|
||||
|
||||
4. Commit your changes and create a descriptive commit message (the commit message is used to generate release notes,
|
||||
please check out our
|
||||
[commit message conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#)
|
||||
and our commit message presubmit hook `validate-commit-msg.js`):
|
||||
|
||||
git commit -a
|
||||
|
||||
5. Push your branch to Github:
|
||||
|
||||
git push origin my-fix-branch
|
||||
|
||||
6. In Github, send a pull request to `angular:master`.
|
||||
|
||||
|
||||
7. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
1. To delete the branch in Github, run:
|
||||
|
||||
git push origin :my-fix-branch
|
||||
|
||||
2. To check out the master branch, run:
|
||||
|
||||
git checkout master
|
||||
|
||||
3. To delete a local branch, run:
|
||||
|
||||
git branch -D my-fix-branch
|
||||
|
||||
4. To update your master with the latest upstream version, run:
|
||||
|
||||
git pull --ff upstream master
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
|
|
|||
|
|
@ -9,20 +9,20 @@ This way, you don't have to download anything or maintain a local copy.
|
|||
There are two types of angular script URLs you can point to, one for development and one for
|
||||
production:
|
||||
|
||||
* __angular-<version>.js__ — This is the human-readable, non-minified version, suitable for web
|
||||
* __angular.js__ — This is the human-readable, non-minified version, suitable for web
|
||||
development.
|
||||
* __angular-<version>.min.js__ — This is the minified version, which we strongly suggest you use in
|
||||
* __angular.min.js__ — This is the minified version, which we strongly suggest you use in
|
||||
production.
|
||||
|
||||
To point your code to an angular script on the Google CDN server, use the following template. This
|
||||
example points to the minified version 1.0.2:
|
||||
example points to the minified version 1.2.0:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<title>My Angular App</title>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
|
@ -58,7 +58,7 @@ for this angular version. Use this file to get everything in a single download.
|
|||
|
||||
* __`angular-mocks.js`__ — This file contains an implementation of mocks that makes
|
||||
testing angular apps even easier. Your unit/integration test harness should load this file after
|
||||
`angular-<version>.js` is loaded.
|
||||
`angular.js` is loaded.
|
||||
|
||||
* __`angular-scenario.js`__ — This file is a very nifty JavaScript file that allows you
|
||||
to write and execute end-to-end tests for angular applications.
|
||||
|
|
@ -68,8 +68,19 @@ to write and execute end-to-end tests for angular applications.
|
|||
contents of this file are copy&pasted into the `index.html` to avoid even the initial request to `angular-loader.min.js`.
|
||||
See [angular-seed](https://github.com/angular/angular-seed/blob/master/app/index-async.html) for an example of usage.
|
||||
|
||||
* __`angular-resource.js`__, __`angular-cookies.js`__, etc - extra Angular modules with additional functionality.
|
||||
* __Additional Angular modules:__ optional modules with additional functionality. These files should be loaded
|
||||
after the core `angular.js` file:
|
||||
* __`angular-animate.js`__ - Enable animation support
|
||||
* __`angular-cookies.js`__ - A convenient wrapper for reading and writing browser cookies
|
||||
* __`angular-resource.js`__ - Interaction support with RESTful services via the $resource service
|
||||
* __`angular-route.js`__ - Routing and deeplinking services and directives for angular apps
|
||||
* __`angular-sanitize.js`__ - Functionality to sanitize HTML
|
||||
* __`angular-touch.js`__ - Touch events and other helpers for touch-enabled devices
|
||||
|
||||
|
||||
* __`docs`__ — this directory contains all the files that compose the
|
||||
<http://docs.angularjs.org/> documentation app. These files are handy to see the older version of
|
||||
our docs, or even more importantly, view the docs offline.
|
||||
|
||||
* __`i18n`__ - this directory contains locale specific `ngLocale` angular modules to override the defaults
|
||||
defined in the `ng` module.
|
||||
|
|
@ -67,7 +67,7 @@ illustration we typically build snappy apps with hundreds or thousands of active
|
|||
|
||||
### How big is the angular.js file that I need to include?
|
||||
|
||||
The size of the file is < 29KB compressed and minified.
|
||||
The size of the file is < 36KB compressed and minified.
|
||||
|
||||
|
||||
### Can I use the open-source Closure Library with Angular?
|
||||
|
|
@ -81,6 +81,9 @@ Yes, Angular can use {@link http://jquery.com/ jQuery} if it's present in your a
|
|||
application is being bootstrapped. If jQuery is not present in your script path, Angular falls back
|
||||
to its own implementation of the subset of jQuery that we call {@link api/angular.element jQLite}.
|
||||
|
||||
Due to a change to use `on()`/`off()` rather than `bind()`/`unbind()`, Angular 1.2 only operates with
|
||||
jQuery 1.7.1 or above.
|
||||
|
||||
|
||||
### What is testability like in Angular?
|
||||
|
||||
|
|
@ -113,11 +116,12 @@ make our schwag will be happy to do a custom run for you, based on our existing
|
|||
they'll waive the setup costs, and you can order any quantity you need.
|
||||
|
||||
**Stickers**
|
||||
Contact Tom Witting (or anyone in sales) via email at tom@stickergiant.com, and tell him you want to order some AngularJS
|
||||
For orders of 250 stickers or more within Canada or the United States, contact Tom Witting (or anyone in sales) via email at tom@stickergiant.com, and tell him you want to order some AngularJS
|
||||
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
|
||||
|
||||
As long as the design stays exactly the same, {@link http://www.stickergiant.com StickerGiant} will give you a reorder discount.
|
||||
|
||||
For a smaller order, or for other countries, we suggest downloading the logo artwork and making your own.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
|
|
|||
|
|
@ -54,35 +54,37 @@ and follow the instructions for setting up your computer.
|
|||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g karma</pre>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<li><p>You'll need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<li><p>Clone the angular-phonecat repository located at
|
||||
<a href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
|
||||
<p>The tutorial instructions, from now on, assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p></li>
|
||||
<li><p>You will also need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p></li>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
||||
don't have it already:</p>
|
||||
<pre>
|
||||
npm install
|
||||
</pre></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
|
|
@ -94,7 +96,7 @@ to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></l
|
|||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
|
|
@ -104,8 +106,8 @@ directory.</p>
|
|||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</p></li>
|
||||
already installed, you can use <code>node</code> to run a simple
|
||||
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -24,18 +24,15 @@ angular-seed, and run the application in the browser.
|
|||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>node ./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code>
|
||||
directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code> directory.</li>
|
||||
<li>Navigate in your browser to <code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -56,18 +53,14 @@ directory.</li>
|
|||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node
|
||||
scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code>
|
||||
directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code> directory.</li>
|
||||
<li>Navigate in your browser to <code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -80,7 +73,7 @@ directory.</li>
|
|||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
||||
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
|
||||
The code contains some key Angular elements that we will need going forward.
|
||||
The code contains some key Angular elements that we will need as we progress.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
|
|
@ -109,9 +102,11 @@ __`app/index.html`:__
|
|||
|
||||
<html ng-app>
|
||||
|
||||
The `ng-app` attribute represents an Angular directive (named `ngApp`; Angular uses
|
||||
`name-with-dashes` for attribute names and `camelCase` for the corresponding directive name)
|
||||
used to flag an element which Angular should consider to be the root element of our application.
|
||||
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
|
||||
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
|
||||
which implement them).
|
||||
This directive is used to flag the html element that Angular should consider to be the root element
|
||||
of our application.
|
||||
This gives application developers the freedom to tell Angular if the entire html page or only a
|
||||
portion of it should be treated as the Angular application.
|
||||
|
||||
|
|
@ -152,8 +147,7 @@ for most cases. In advanced cases, such as when using script loaders, you can us
|
|||
|
||||
There are 3 important things that happen during the app bootstrap:
|
||||
|
||||
1. The {@link api/AUTO.$injector injector} that will be used for dependency injection
|
||||
within this app is created.
|
||||
1. The {@link api/AUTO.$injector injector} that will be used for dependency injection is created.
|
||||
|
||||
2. The injector will then create the {@link api/ng.$rootScope root scope} that will
|
||||
become the context for the model of our application.
|
||||
|
|
@ -185,7 +179,7 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
|
|||
* Removed the example app
|
||||
* Added phone images to `app/img/phones/`
|
||||
* Added phone data files (JSON) to `app/phones/`
|
||||
* Added [Bootstrap](http://twitter.github.com/bootstrap/) files to `app/css/` and `app/img/`
|
||||
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ The view component is constructed by Angular from this template:
|
|||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<html ng-app>
|
||||
<html ng-app="phonecatApp">
|
||||
<head>
|
||||
...
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
|
|
@ -47,6 +47,7 @@ __`app/index.html`:__
|
|||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
|
@ -60,63 +61,73 @@ We replaced the hard-coded phone list with the
|
|||
repeater tells Angular to create a `<li>` element for each phone in the list using the first `<li>`
|
||||
tag as the template.
|
||||
|
||||
* As we've learned in step 0, the curly braces around `phone.name` and `phone.snippet` denote
|
||||
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
|
||||
__controller__ to the DOM at this point.
|
||||
|
||||
* As we've learned in {@link step_00 step 0}, the curly braces around `phone.name` and `phone.snippet` denote
|
||||
bindings. As opposed to evaluating constants, these expressions are referring to our application
|
||||
model, which was set up in our `PhoneListCtrl` controller.
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_02.png">
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_02.png">
|
||||
|
||||
## Model and Controller
|
||||
|
||||
The data __model__ (a simple array of phones in object literal notation) is instantiated within
|
||||
the `PhoneListCtrl` __controller__:
|
||||
The data __model__ (a simple array of phones in object literal notation) is now instantiated within
|
||||
the `PhoneListCtrl` __controller__. The __controller__ is simply a constructor function that takes a
|
||||
`$scope` parameter:
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
function PhoneListCtrl($scope) {
|
||||
|
||||
var phonecatApp = angular.module('phonecatApp', []);
|
||||
|
||||
phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||
$scope.phones = [
|
||||
{"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S."},
|
||||
{"name": "Motorola XOOM™ with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation tablet."},
|
||||
{"name": "MOTOROLA XOOM™",
|
||||
"snippet": "The Next, Next Generation tablet."}
|
||||
{'name': 'Nexus S',
|
||||
'snippet': 'Fast just got faster with Nexus S.'},
|
||||
{'name': 'Motorola XOOM™ with Wi-Fi',
|
||||
'snippet': 'The Next, Next Generation tablet.'},
|
||||
{'name': 'MOTOROLA XOOM™',
|
||||
'snippet': 'The Next, Next Generation tablet.'}
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
</pre>
|
||||
|
||||
Here we declared a controller called `PhoneListCtrl` and registered it in an AngularJS
|
||||
module, `phonecatApp`. Notice that our `ng-app` directive (on the `<html>` tag) now specifies the `phonecatApp`
|
||||
module name as the module to load when bootstrapping the Angular application.
|
||||
|
||||
|
||||
|
||||
Although the controller is not yet doing very much controlling, it is playing a crucial role. By
|
||||
providing context for our data model, the controller allows us to establish data-binding between
|
||||
Although the controller is not yet doing very much, it plays a crucial role. By providing context
|
||||
for our data model, the controller allows us to establish data-binding between
|
||||
the model and the view. We connected the dots between the presentation, data, and logic components
|
||||
as follows:
|
||||
|
||||
* `PhoneListCtrl` — the name of our controller function (located in the JavaScript file
|
||||
`controllers.js`), matches the value of the
|
||||
{@link api/ng.directive:ngController ngController} directive located
|
||||
on the `<body>` tag.
|
||||
* The {@link api/ng.directive:ngController ngController} directive, located on the `<body>` tag,
|
||||
references the name of our controller, `PhoneListCtrl` (located in the JavaScript file
|
||||
`controllers.js`).
|
||||
|
||||
* The phone data is then attached to the *scope* (`$scope`) that was injected into our controller
|
||||
function. The controller scope is a prototypical descendant of the root scope that was created
|
||||
when the application bootstrapped. This controller scope is available to all bindings located within
|
||||
* The `PhoneListCtrl` controller attaches the phone data to the `$scope` that was injected into our
|
||||
controller function. This *scope* is a prototypical descendant of the *root scope* that was created
|
||||
when the application was defined. This controller scope is available to all bindings located within
|
||||
the `<body ng-controller="PhoneListCtrl">` tag.
|
||||
|
||||
The concept of a scope in Angular is crucial; a scope can be seen as the glue which allows the
|
||||
### Scope
|
||||
|
||||
The concept of a scope in Angular is crucial. A scope can be seen as the glue which allows the
|
||||
template, model and controller to work together. Angular uses scopes, along with the information
|
||||
contained in the template, data model, and controller, to keep models and views separate, but in
|
||||
sync. Any changes made to the model are reflected in the view; any changes that occur in the view
|
||||
are reflected in the model.
|
||||
|
||||
To learn more about Angular scopes, see the {@link api/ng.$rootScope.Scope angular scope documentation}.
|
||||
To learn more about Angular scopes, see the {@link api/ng.$rootScope.Scope angular scope documentation}.
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
The "Angular way" makes it easy to test code as it is being developed. Take a look at the following
|
||||
unit test for your newly created controller:
|
||||
The "Angular way" of separating controller from the view, makes it easy to test code as it is being
|
||||
developed. If our controller is available on the global namespace then we can simply instantiate it
|
||||
with a mock `scope` object. Take a look at the following unit test for our controller:
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
|
|
@ -134,23 +145,54 @@ describe('PhoneCat controllers', function() {
|
|||
});
|
||||
</pre>
|
||||
|
||||
The test instantiates our PhoneListCtrl and verifies that its phones array property contains three
|
||||
records. This example demonstrates how easy it is to create a unit test for code in Angular. Since
|
||||
testing is such a critical part of software development, we make it easy to create tests in Angular
|
||||
so that developers are encouraged to write them.
|
||||
The test instantiates `PhoneListCtrl` and verifies that the phones array property on the scope
|
||||
contains three records. This example demonstrates how easy it is to create a unit test for code in
|
||||
Angular. Since testing is such a critical part of software development, we make it easy to create
|
||||
tests in Angular so that developers are encouraged to write them.
|
||||
|
||||
### Testing non-Global Controllers
|
||||
In practice, you will not want to have your controller functions in the global namespace. Instead,
|
||||
we have registered our controllers in the `phonecatApp` module. In this case Angular provides a
|
||||
service, `$controller`, which will retrieve your controller by name. Here is the same test using
|
||||
`$controller`:
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
|
||||
it('should create "phones" model with 3 phones', inject(function($controller) {
|
||||
var scope = {},
|
||||
ctrl = $controller('PhoneListCtrl', { $scope: scope });
|
||||
|
||||
expect(scope.phones.length).toBe(3);
|
||||
}));
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
Don't forget that we need to load up the `phonecatApp` module into the test so that the controller
|
||||
is available to be injected.
|
||||
|
||||
### Writing and Running Tests
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the {@link
|
||||
http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link
|
||||
https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
http://pivotal.github.com/jasmine/ Jasmine home page} and at the {@link
|
||||
http://pivotal.github.io/jasmine/ Jasmine docs}.
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||
http://karma-runner.github.io/ Karma}. To run the test, do the following:
|
||||
http://karma-runner.github.io/ Karma}. Ensure that the necessary karma plugins are installed.
|
||||
You can do this by issuing `npm install` into your terminal.
|
||||
|
||||
|
||||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
|
|
@ -164,7 +206,7 @@ is located at `./config/karma.conf.js`).
|
|||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
|
|
@ -175,9 +217,13 @@ is located at `./config/karma.conf.js`).
|
|||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
$scope.hello = "Hello, World!"
|
||||
$scope.name = "World";
|
||||
|
||||
Refresh your browser to make sure it says, "Hello, World!"
|
||||
Then add a new binding to `index.html`:
|
||||
|
||||
<p>Hello, {{name}}!</p>
|
||||
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue