Compare commits
2354 Commits
2.0.0-rc.0
...
4.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
1bdf7061b8 | |||
cc3afc888f | |||
5129e8e47c | |||
0cf753be30 | |||
c4a6263a01 | |||
45eac233eb | |||
ab26b6518d | |||
778ded9fcf | |||
b9f17a9cb2 | |||
74ce121dba | |||
96d06f7f09 | |||
8c20aaa328 | |||
b1a79fd2ec | |||
6c20e6ca2e | |||
88eb3b2ce8 | |||
0fa3895d5b | |||
ba17dcbf2b | |||
88e3d7af9f | |||
3dbd9a04d4 | |||
b36f60c74c | |||
57461e9ed7 | |||
612f120208 | |||
4a56b6e7f6 | |||
724ca373e7 | |||
3b896709a9 | |||
9a6f3d637f | |||
56f232cdd7 | |||
047cda5b3c | |||
9559d3e949 | |||
30380d010b | |||
17486fd696 | |||
0e2fd9d91a | |||
4e7752a12a | |||
e9ba7aa4f8 | |||
bb9c7ae6e7 | |||
b4d444a0a7 | |||
2f2b65bd38 | |||
269cf42b72 | |||
8b81bb1eb6 | |||
e4e9dbe33d | |||
1dc9be4b7d | |||
221b7a1176 | |||
d3f174a57f | |||
8dd16bbe67 | |||
5279d06e88 | |||
adc54302cb | |||
0f161ce27a | |||
563334e2c9 | |||
b565301186 | |||
1ece7366c8 | |||
7ac38aa357 | |||
53cf2ec573 | |||
1cfbefebe3 | |||
e5a144d902 | |||
2c6dab970b | |||
9e28568a8f | |||
db700dfc71 | |||
294c1cd7a7 | |||
fafee5a493 | |||
03e855ae8f | |||
96073e51c3 | |||
baa654a234 | |||
dfe29934b6 | |||
b988733553 | |||
44bb337acc | |||
56b3b3cbed | |||
0dcac966b4 | |||
b9d293af03 | |||
4da7925ad5 | |||
6b9aa2ca3d | |||
a696f4aade | |||
bb4db2d8f3 | |||
4676df5833 | |||
45cc444154 | |||
56e2f84fe8 | |||
600402d440 | |||
5e7a2fa854 | |||
881dce841f | |||
c4817988ca | |||
b64946b5f9 | |||
09b4bd0dfb | |||
c871af7b5a | |||
d1feb478a2 | |||
c211ef9b2d | |||
a7688d27f2 | |||
24af51a623 | |||
f6b5965a63 | |||
7a4c25535d | |||
ef32e6b0d0 | |||
5c431cee02 | |||
2ada3187a6 | |||
c33fda2607 | |||
3c2842be96 | |||
94312f0980 | |||
5bccff0d7a | |||
2e1413016e | |||
a378aab9aa | |||
1e3dd3dd9b | |||
701074cf89 | |||
e9a89c0693 | |||
d0366542fb | |||
e58d683931 | |||
7036e04ec6 | |||
d4ffa47ea6 | |||
80b66edfa7 | |||
12f03b90fd | |||
470997ebb9 | |||
bcba0332a6 | |||
41db177d0c | |||
e4b76a493f | |||
a2a290a83c | |||
14d7844b2b | |||
388afa414e | |||
4370049cea | |||
fb91b2fe78 | |||
0ba5bebf61 | |||
a9096437fd | |||
31d42d87c6 | |||
20a7e26d1e | |||
6e2c9cb586 | |||
559cf9d192 | |||
1961332f26 | |||
f9b929f28d | |||
69a4bb0bcd | |||
a7479f657a | |||
bc20e8ac9d | |||
a05e50fda3 | |||
ae7f5f37d2 | |||
45e1e36477 | |||
08ff67ea11 | |||
1bc5368ea0 | |||
093cc04748 | |||
9d2c71269b | |||
80d3e14ce4 | |||
ef48ee0a0a | |||
ec8e68ed56 | |||
0a29574d98 | |||
86b2b2504f | |||
69e14b500b | |||
1079b9381c | |||
7670cc1a86 | |||
ea63676970 | |||
1367cd9569 | |||
9aafdc7b02 | |||
1f90f29369 | |||
49fce37013 | |||
676081fe66 | |||
e0e5e78835 | |||
b4214d60a6 | |||
8270bec343 | |||
5921c872b6 | |||
52b21275f4 | |||
c48dd76f5c | |||
49fb8143e8 | |||
5f2b3173d7 | |||
c87c3bec93 | |||
2ffa1a71aa | |||
94f84c5d7e | |||
ff290af38c | |||
fe441186e7 | |||
f89d004c51 | |||
6c7300c7de | |||
22058298d3 | |||
104c157ef6 | |||
1df9319af1 | |||
d43c573ebc | |||
a699a448fb | |||
7b7ae5fe56 | |||
94b62c963d | |||
579567ca79 | |||
47d41d492b | |||
e075b1ba83 | |||
029f558d45 | |||
c5ea03a023 | |||
c7245189e2 | |||
cd3901f774 | |||
a64c9b5d5b | |||
863285a4b0 | |||
5f40e5ba21 | |||
d69717cf79 | |||
00979838ef | |||
a277e97dd7 | |||
9e5617e41e | |||
bc1320d926 | |||
77008e35ff | |||
01da4223d4 | |||
0854a5dea4 | |||
df7f5fc550 | |||
24ea3f022b | |||
3368f29a4d | |||
8960d4990c | |||
4165fddfc4 | |||
c37af2af5a | |||
da41a954b5 | |||
5a997ef4f0 | |||
d339d8b81d | |||
827c3fe199 | |||
8775ab9495 | |||
5885c52c1f | |||
80b9570dca | |||
f802194c18 | |||
7ad616a177 | |||
670b680b0a | |||
f7fba74c58 | |||
20b454cbc9 | |||
665dde2e5c | |||
4d5a4d89cd | |||
e130bc171f | |||
b141a227fb | |||
b7763559cd | |||
d1d0ce7613 | |||
2dd9654004 | |||
e35c25d2ce | |||
1e729d7ba2 | |||
fc8694ed11 | |||
05b2b49711 | |||
3ef73c2b19 | |||
4106d18172 | |||
1ce7fd7827 | |||
c4ecaeda64 | |||
b28f01bb7f | |||
c4e7c083e2 | |||
28bdc5af47 | |||
b88714bcdf | |||
d2859cdd71 | |||
4931a615bf | |||
a733444d0e | |||
6152eb24bc | |||
b2f9d56577 | |||
1c24271daf | |||
c3e5ddbe20 | |||
d02eab498f | |||
fc550185fc | |||
0c7726dd74 | |||
83361d811d | |||
1f54040ef4 | |||
65417374f1 | |||
0adb97bffb | |||
f20d1a8af5 | |||
e21e9c5fb7 | |||
d3a3a8e1fc | |||
dff6ee3272 | |||
ba52b2e08c | |||
0589f93e41 | |||
2f87eb52fe | |||
9d8c467cb0 | |||
67dc0912c5 | |||
b049217437 | |||
2191f44025 | |||
4b854be29e | |||
0a724208b9 | |||
1200cf25f4 | |||
635bf02b02 | |||
2d7b3a86cc | |||
523fd84d22 | |||
e8ea741039 | |||
1a92e3d406 | |||
be6c95ad03 | |||
f816319e41 | |||
5047d9780d | |||
b1e3dda5cb | |||
d169c2434e | |||
6d1f1a43bb | |||
e19bf70b47 | |||
a6f8e9fc90 | |||
d6382bfa0b | |||
4dea347101 | |||
5237b1c98c | |||
f364557629 | |||
c2aa981dd6 | |||
dc63cef10a | |||
2c294d5dff | |||
e1af25d93e | |||
123943a6e0 | |||
3a4b54daa4 | |||
95cbca20a5 | |||
aeed7373af | |||
2e3ac70e0a | |||
9aeb8c5357 | |||
424e6c4cb9 | |||
5cb2008e6c | |||
78f42c7aa1 | |||
d4d3782d45 | |||
46cb04d575 | |||
8c7e93bebe | |||
5d9cbd7d6f | |||
d061adc02d | |||
6d29faefea | |||
99aa49ab6c | |||
e5c6bb4286 | |||
d9a22dae4f | |||
fb6c4582a1 | |||
8578682dcf | |||
c0178de0e2 | |||
31322e73b7 | |||
9211a22039 | |||
3f67ab074a | |||
4bae4b3bb5 | |||
02dd90faed | |||
1c85e99588 | |||
ccb65893bf | |||
3e90ffd293 | |||
8063b0d9a2 | |||
21030e9a1c | |||
889b48d85f | |||
1bd04e95de | |||
f88cd2f22e | |||
f822f9599c | |||
9898d8f6d9 | |||
2dd6280ab8 | |||
35f9a1c2cb | |||
465516b905 | |||
db49d422f2 | |||
8ed92d75b0 | |||
50e5cb15dd | |||
c5c53f3666 | |||
bb0d23f82b | |||
1e6440e81b | |||
6b02b80a03 | |||
2c0c86e3ce | |||
5b4bea24de | |||
7690d02133 | |||
b2ae7b607e | |||
7c210645a3 | |||
07e0fce8fc | |||
0ac8e102de | |||
e74d8aaf92 | |||
881eb894bc | |||
0eca960494 | |||
eed83443b8 | |||
e5c4e5801f | |||
69fa3bbc03 | |||
445ed43b9a | |||
174334dec3 | |||
9c697030e6 | |||
697690349f | |||
0448e80704 | |||
e85232afd2 | |||
e7ece6c8ce | |||
67380d4b28 | |||
f114e40212 | |||
952471e25d | |||
c65e428778 | |||
842f52e841 | |||
eb2ceff4ba | |||
f49ab56160 | |||
c0f750af4e | |||
bcd37f52fb | |||
e69c1fb36c | |||
9da4c259a5 | |||
fcd116fdc0 | |||
383adc9ad9 | |||
9b8488f007 | |||
1817ddb57b | |||
1ee574c51e | |||
171a9bdc85 | |||
896916af29 | |||
e49c7fae22 | |||
6b65fc1286 | |||
0e3981afc1 | |||
e78508507d | |||
a23fa94ca8 | |||
4568d5ddac | |||
c6e893953f | |||
55dfa1b69d | |||
0fe3cd9a4c | |||
0c19898694 | |||
5b6e8ea3ec | |||
732f446ad2 | |||
f0e092515c | |||
14e785f5b7 | |||
01d1624884 | |||
33910ddfc9 | |||
01ca2db6ae | |||
6cefccb314 | |||
fa9e21e83c | |||
b6078f5887 | |||
c65b4fa9dc | |||
169ed82900 | |||
fd8e15b15d | |||
aa40366a92 | |||
40d8d9c3e3 | |||
ee2ac025ef | |||
aa3769ba69 | |||
d4ddb6004e | |||
84400bcc86 | |||
42d9998cbb | |||
c18d2fe5e3 | |||
d91a86aac6 | |||
d6e5e9283c | |||
eab7e490c9 | |||
3e90605db9 | |||
79671a6f12 | |||
a659259962 | |||
b56474d067 | |||
8395f0e138 | |||
dd0519abad | |||
f238c8ac7a | |||
8c27c62fab | |||
5031adc7a3 | |||
821b8f09d6 | |||
2bf1bbc071 | |||
7b0a86718c | |||
3edca4d37e | |||
a0a05041ac | |||
7256d0ede5 | |||
d62d89319e | |||
f5f1d5f65c | |||
a8d237581d | |||
d036165a19 | |||
d17e690eb4 | |||
714f2af0dd | |||
2b90cd532f | |||
3a64ad895a | |||
9ec0a4e105 | |||
4b3d135193 | |||
1d0ed6f75f | |||
6f330a5fc9 | |||
e23076f767 | |||
7295a5e7f2 | |||
20bed46737 | |||
2a5012d515 | |||
fb38fba8f9 | |||
4c35be3e07 | |||
e9f307f948 | |||
2e500cc85b | |||
56dce0e26d | |||
8a8c53250e | |||
08ff2e5249 | |||
a006c1418a | |||
90c223591f | |||
aaf6e05f56 | |||
3bee521aa4 | |||
95f48292b1 | |||
04cfa1ebdf | |||
4022173d1e | |||
c8baf51f4f | |||
b4db73d0bf | |||
e15a3f273f | |||
213c713409 | |||
9a8423da36 | |||
f0b0762f4a | |||
b5c4bf1c59 | |||
56c361ff6a | |||
562f7a2f8b | |||
6dd5201765 | |||
72361fb68f | |||
5c6ec20c7e | |||
440ef02f29 | |||
4e3d58a792 | |||
61d7c1e0b3 | |||
bf93389615 | |||
4398056146 | |||
1b547886d0 | |||
9591a08dfb | |||
65965c27a8 | |||
13b41bd631 | |||
f3524af68f | |||
0a56f4ea82 | |||
cf52284ac3 | |||
4a09c81724 | |||
16efb13dd1 | |||
986abbe0b2 | |||
25c2141991 | |||
2893c2c0a2 | |||
393c1007a8 | |||
66b6fc010d | |||
f31c9470fa | |||
4bd8f58552 | |||
93556a5720 | |||
5614c4ff0f | |||
c3065aac7a | |||
c767df0e4e | |||
25e5b2fdf0 | |||
307c4693dc | |||
349ad75de3 | |||
f562cbf86c | |||
804943c9b1 | |||
dea59165de | |||
a1322873c8 | |||
b8c839bd51 | |||
d2e5198b93 | |||
6cf7a1bf84 | |||
d46b8deeea | |||
bbb7a39414 | |||
d7d8fab211 | |||
51b06924bd | |||
aa4bd14b3f | |||
3ff6554cbc | |||
dfd8140084 | |||
6ea3ab7e14 | |||
9761db5ac2 | |||
75d1617b63 | |||
614a35d539 | |||
9ab401f4d3 | |||
82c81cd0d2 | |||
12959f444c | |||
25a6da244c | |||
5908b66ae9 | |||
480ef20eb1 | |||
c066281bad | |||
1b9493f725 | |||
ae26504e84 | |||
d420080b3b | |||
2975d8933c | |||
43c0e9a6bb | |||
f275f36081 | |||
e628b66cca | |||
3e73bea3e7 | |||
42cf06fa12 | |||
c4bbafc291 | |||
2d6a003dba | |||
e45b7ffcd9 | |||
627282d2c8 | |||
2f7492c986 | |||
2452cd14e0 | |||
bc69c74be0 | |||
897555ca78 | |||
966bcbad5a | |||
94b8612e4e | |||
b2b72190f8 | |||
f5c8e0989d | |||
4a09251921 | |||
36caaaa8e4 | |||
808275a9d5 | |||
be3784c957 | |||
555301ce3a | |||
7194fc2b9e | |||
2a3ca7bfcf | |||
4cbf8ccf05 | |||
a6c4490fce | |||
2c02d34c05 | |||
6c2d931744 | |||
86ffa884b7 | |||
3e548de99d | |||
909268036b | |||
519a324454 | |||
ef96763fa4 | |||
7dcca307d9 | |||
491d5a22a9 | |||
44572f114f | |||
1ef4696cb7 | |||
07a986d330 | |||
59d2b4c831 | |||
2a5bd2f345 | |||
3c06a5dc25 | |||
adeea5d86a | |||
dddbb1c1cb | |||
bccf0e69dc | |||
b15039d228 | |||
2235048432 | |||
484119e59f | |||
24099bdbd2 | |||
912ca44979 | |||
664a6273e1 | |||
c1a62e2154 | |||
aac37bedc0 | |||
a3884db87c | |||
fc5ac1ebc4 | |||
ad20d7d260 | |||
602522beb2 | |||
4e047302f2 | |||
419a812f04 | |||
f340e1a414 | |||
481c9b3258 | |||
8b2dfb2eca | |||
824ea8406c | |||
1f96a93f59 | |||
009d545787 | |||
53c25210a6 | |||
927aa69726 | |||
ce89039036 | |||
42198cd7d5 | |||
d6ba092a27 | |||
773b31de8f | |||
f79b320fc4 | |||
6a212fd561 | |||
be010a292a | |||
7c36e7f956 | |||
13ba2f90b9 | |||
75277cd94b | |||
46d150266b | |||
1b5384ee54 | |||
9f7d32a326 | |||
9de76ebfa5 | |||
46023e4792 | |||
b55aaf094f | |||
d90b622fa4 | |||
79e2bb9291 | |||
efbbefd353 | |||
c2fae72bc6 | |||
7908679c4b | |||
9ed9ff40b3 | |||
2f14415836 | |||
76e4911e8b | |||
ed5e98d0df | |||
146af1fed9 | |||
c60ba7a72f | |||
05beffe0d0 | |||
08c038ebd9 | |||
582550a90d | |||
1d53a870dd | |||
a0c58a6b5c | |||
d3eff6c483 | |||
2524d510bc | |||
8f5dd1f11e | |||
77ee27c59e | |||
73593d4bf3 | |||
a965d11cce | |||
52be848f94 | |||
69dfcf7385 | |||
785b7b640e | |||
e5a753e111 | |||
768cddbe62 | |||
92f244aa26 | |||
2a4bf9a0df | |||
45ddd6ba78 | |||
7886561997 | |||
752edca81b | |||
1bd858fb43 | |||
fcb4e66493 | |||
ef881475e9 | |||
458ca7112a | |||
2aba8b0ff2 | |||
77dc1ab675 | |||
3052fb234f | |||
79383ce150 | |||
c3c0e2e2a2 | |||
44a142fc02 | |||
3d9d839c6c | |||
69f87ca075 | |||
f224ca1461 | |||
19e869e7c9 | |||
7cab30f85d | |||
73407351e7 | |||
2c110931f8 | |||
2ced2a8a5a | |||
634b3bb88b | |||
4595a61aeb | |||
f80a157b65 | |||
6e35d13fbc | |||
fe35bc34f6 | |||
ad3bf6c54f | |||
a0e9fde653 | |||
3dc61779f0 | |||
09092ac3c2 | |||
778e6ad3b4 | |||
55dc0e4a5f | |||
4708b248d5 | |||
7694f974af | |||
acbf1d859c | |||
f3793b5953 | |||
22c021c57f | |||
d8f23f4b7f | |||
32fcec9fcb | |||
78039b41d6 | |||
89fd54e8e3 | |||
77cbf7f2bb | |||
383f23b578 | |||
2a3f4d7b17 | |||
ec92f4b198 | |||
121e5080aa | |||
fe1d0e29c5 | |||
469010ea8e | |||
f0cdb428f5 | |||
051d74802a | |||
f2bbef3e33 | |||
80d36b8db4 | |||
e3687706c7 | |||
648ce5981b | |||
9c23884da4 | |||
d708a8859c | |||
9ddf9b3d3d | |||
69f006cd89 | |||
4aaae3eada | |||
2e78b76fcf | |||
b2cf379d1c | |||
e25baa08b3 | |||
7103754178 | |||
1a069e8372 | |||
0fc11a43f1 | |||
0e3d655220 | |||
7c5cc9bc41 | |||
5f1dddc5d0 | |||
20a4f9923f | |||
e7c00be19d | |||
74ede9aa9b | |||
d1035da85c | |||
13533d2a30 | |||
953cb50fa5 | |||
3fffcf6645 | |||
d509ee078b | |||
8e221b826f | |||
830a780cb3 | |||
6fda97287e | |||
234c5599f1 | |||
f6710fefeb | |||
bda1909ede | |||
b3e3cd3add | |||
e5fdf4c70a | |||
97471d74b6 | |||
1de04b23b1 | |||
a178bc6c83 | |||
642c1db9ef | |||
579deeb9c5 | |||
bad58824a0 | |||
5494169fb4 | |||
5a3d7a62a2 | |||
a382d6dd20 | |||
52bf188b8f | |||
6f412bb449 | |||
e9fd8645ed | |||
a0aecac0e5 | |||
938ed1c76d | |||
eb8288f76c | |||
0936ceeab4 | |||
e0ad413a8e | |||
3045d02b9a | |||
e86573bac8 | |||
0a94845435 | |||
262bd23b84 | |||
7b8dae19af | |||
7c16ef942e | |||
a318b57257 | |||
fe47e6b783 | |||
091c390032 | |||
e391cacdf9 | |||
32feb8a532 | |||
a664aba2c9 | |||
d520fae70e | |||
fa93fd672e | |||
f4be2f907d | |||
2ea27a76d3 | |||
ec0acf9a1b | |||
a26dd28bdb | |||
7742ec00e7 | |||
2b5c983c13 | |||
ef153649b3 | |||
d321b0ebf5 | |||
b4265e0685 | |||
178fb79b5c | |||
5a7a58b1e0 | |||
f66ac821a2 | |||
fe299f4dfc | |||
4cac650675 | |||
cb7643ccea | |||
faa3478514 | |||
bc3f4bc816 | |||
c9f58cf78c | |||
6ccbfd41dd | |||
7d2554baa1 | |||
52a853e257 | |||
8f2fa0f766 | |||
fc60fa790c | |||
b74185369f | |||
7221632228 | |||
02f1222a8d | |||
c27ce7318f | |||
a838aba756 | |||
bfc97ff2cd | |||
57051f01ce | |||
e319cfefc3 | |||
444014ad96 | |||
867494a060 | |||
69ad99dca6 | |||
da5fc696bb | |||
b44b6ef8f5 | |||
0f21a5823b | |||
5ae6915600 | |||
8b9ab44eee | |||
b0a03fcab3 | |||
c951822c35 | |||
acda82c1ed | |||
a8815d6b08 | |||
d6791ff0e0 | |||
a2d35641e3 | |||
76dd026447 | |||
0ecd9b2df0 | |||
0e9503b500 | |||
f77ab6a2d2 | |||
97bc97153b | |||
445e5922ec | |||
b9fc090143 | |||
592f40aa9c | |||
24facdea2d | |||
aa2d3372a5 | |||
bf60418fdc | |||
cca4a5c519 | |||
6e5f8b59b3 | |||
8409b65153 | |||
38e2203b24 | |||
bd1dcb5f11 | |||
3993279527 | |||
bf1e2613b2 | |||
f7db0668d1 | |||
27d76776b8 | |||
8603d9c269 | |||
d55f747858 | |||
52de0fa558 | |||
d61ecf0663 | |||
5a9c5f28b8 | |||
15fc5dd7ee | |||
a5419608e0 | |||
5f95bf1dd2 | |||
33c8948fd3 | |||
606e51881a | |||
fdf4309b50 | |||
af996ef0c4 | |||
68d2dfdd2a | |||
07bd4b0630 | |||
df1718d624 | |||
17e3410d98 | |||
5effc330ed | |||
3df00828d7 | |||
8c477b2f45 | |||
7787771aba | |||
7275e1beb3 | |||
12ba62e5e2 | |||
e6e007e2f1 | |||
91dd138fa5 | |||
d972d82354 | |||
bdcf46f82e | |||
79e1c7b807 | |||
d22eeb70b8 | |||
aa92512ac6 | |||
f782b08f58 | |||
4202936bbf | |||
e1faca6386 | |||
f5b0e22d35 | |||
00693d70a2 | |||
bcef5efffe | |||
13ecc140e8 | |||
709a6dea06 | |||
16cfb88c00 | |||
efee6f5199 | |||
2aa8aae76d | |||
afb4bd9ef6 | |||
d641c36a45 | |||
f4566f8128 | |||
a67c06708d | |||
d9d57d71dd | |||
e06303a987 | |||
40b92ddf21 | |||
1681e4f57f | |||
71b7654660 | |||
eaaec6979c | |||
c587c63591 | |||
f50c1da4e2 | |||
0254ce1f6c | |||
c9b765f5c0 | |||
8c975ed156 | |||
bb35fcb562 | |||
57230b70a9 | |||
43dc60ce4f | |||
230b3b73d8 | |||
0b7dc2f9ff | |||
de1f44f51f | |||
f1cfddf6d6 | |||
ef621a2f00 | |||
df9761951b | |||
f786c560f1 | |||
c5557de3e7 | |||
ec3a5b54de | |||
cf269d9ff4 | |||
5fa5ffb82a | |||
4a57dcfd8d | |||
43923ffcf5 | |||
50c37d45dc | |||
a63359689f | |||
43d3a84df3 | |||
8310c91823 | |||
b64b5ece65 | |||
ed9c2b6281 | |||
1cf5f5fa38 | |||
a32078f85e | |||
decd129a4d | |||
c3c9ecb302 | |||
af520947aa | |||
040bf57966 | |||
65a60b7456 | |||
756ef09d12 | |||
9316f95467 | |||
83d94b7504 | |||
a121136fae | |||
a6bb84e02b | |||
3898dc488e | |||
ca3f9926f9 | |||
1c012a035f | |||
38c5304b7f | |||
9a049be67f | |||
2045c9e8ee | |||
6c4ec05a4a | |||
f7bfda31ff | |||
a92b573309 | |||
4fd13d71c8 | |||
bf7b82b658 | |||
c143fee849 | |||
0286956107 | |||
e884f4854d | |||
df1822fc2a | |||
42b4b6d21b | |||
36bc2ff269 | |||
1564042fe8 | |||
41c8c30973 | |||
61129fa12d | |||
3a5b4882bc | |||
425c1e6042 | |||
58605cf350 | |||
34b31dea7c | |||
a241ab7c07 | |||
745e10e6d2 | |||
33340dbbd1 | |||
52812c08e2 | |||
52f5ae1961 | |||
9be895b6da | |||
9f1c82537e | |||
5ab5cc77bb | |||
f1b6c6efa1 | |||
45ad13560b | |||
2045268cec | |||
fb1076b44a | |||
6fc46526ae | |||
3ef5ede6d6 | |||
136621ebc9 | |||
f23b22a0f4 | |||
0ca971c5bd | |||
3a6fcee0e6 | |||
8972137c29 | |||
cc6481077f | |||
c041b93418 | |||
bc33765913 | |||
31dce72b7b | |||
212f8dbde7 | |||
44da4984f9 | |||
d95344430c | |||
131626fc61 | |||
676bb0fa7d | |||
5a849829c4 | |||
671f73448c | |||
51d73d3e4e | |||
bf81b06a28 | |||
0621f07a2c | |||
1225ecfb14 | |||
5509453e72 | |||
70488ed382 | |||
03aedbe54b | |||
8395aab25d | |||
0dc15eb64a | |||
cba885a1fb | |||
fa4723a208 | |||
5bf08b886f | |||
89802316b9 | |||
2300c23332 | |||
fa39965a37 | |||
115f0fa842 | |||
734b8b8c13 | |||
54b41f57be | |||
df4254ae89 | |||
14ee75924b | |||
bd4045b6e7 | |||
255099aa61 | |||
1c24096650 | |||
32aeb1052d | |||
838d4bbf6c | |||
c4114c2f66 | |||
37b8691c8c | |||
93054d4e3d | |||
cfc12c6539 | |||
c0bdd89b5d | |||
d5515473bf | |||
ffe5c49c3e | |||
ae1dd5bfd0 | |||
cb657c4b55 | |||
42f60ca303 | |||
e33037a2f1 | |||
9cee8bcc83 | |||
003294d5df | |||
785292f44f | |||
15c2912527 | |||
096ae7c404 | |||
5972fdc817 | |||
2c42a50fc3 | |||
caa1cd2470 | |||
5fad37df69 | |||
727c2b38a4 | |||
b6287ccc51 | |||
69e8ace884 | |||
85d9db6bc4 | |||
0a2132ef10 | |||
d299ce4bcf | |||
0b9425bbb4 | |||
1a035a0dc7 | |||
84b4338ab5 | |||
b847257b16 | |||
c65d139081 | |||
57f0269491 | |||
4e6c41b3a1 | |||
7105021c41 | |||
f7313db0be | |||
1d2e70e3a4 | |||
21516c32e6 | |||
00a24b63da | |||
e71558ba89 | |||
7ac47acc1c | |||
60e49a7e4b | |||
c71e35cbf5 | |||
1348c65b0c | |||
ff03d87cdd | |||
a2bf334e6e | |||
f8690caa98 | |||
aa713d1dd9 | |||
a2519c6164 | |||
fa994810d5 | |||
c54580a4af | |||
730415e048 | |||
42a287fabf | |||
42d442dcd5 | |||
cc2873a94d | |||
63e15ffaec | |||
1b15170c89 | |||
26d1423ae9 | |||
61aad7925f | |||
79055f727b | |||
220d8377fe | |||
cc7780adf7 | |||
051a6ebe12 | |||
c9513b713a | |||
66e38b6754 | |||
7b82877ee5 | |||
c9ad5e46d6 | |||
2cdd051109 | |||
57cb82052b | |||
dd8204a655 | |||
cdda4082de | |||
0614c8c99d | |||
a343a8e1c2 | |||
a41c1bbdf4 | |||
f2c6157e74 | |||
32564ece27 | |||
3eee62fa71 | |||
617475005f | |||
0822066175 | |||
82f30e09f0 | |||
c649a5c5ab | |||
53f0c2206d | |||
0bce3907b8 | |||
2170379251 | |||
5a4e46db20 | |||
f5d44a42c9 | |||
673de004d2 | |||
f386cb4ba9 | |||
71e9cae1d0 | |||
df6762a170 | |||
d296298282 | |||
077e0be1e7 | |||
a52d076912 | |||
dae7cfc454 | |||
436af15d63 | |||
7b24028437 | |||
6a2bbffe10 | |||
f78e184822 | |||
78ad9adc1a | |||
9e2ec7a1aa | |||
643afa4b15 | |||
ed2ebeb52a | |||
567900e550 | |||
cc958c74ad | |||
62af613741 | |||
3ff816afa6 | |||
dd03bf12e1 | |||
645108f25b | |||
882efd125e | |||
d91e92c2f5 | |||
8858ebc4ab | |||
df4c0a3d1f | |||
b4363bc8af | |||
d26a827494 | |||
ea95c391c1 | |||
aa9b617c9d | |||
7192fec841 | |||
ee88c3c976 | |||
1ff0add29e | |||
5ee0f09b92 | |||
70b0ab457b | |||
c25d1f7ecc | |||
a45769a0a2 | |||
109dc99d32 | |||
2371d22d49 | |||
6f4b6edfea | |||
8c09933803 | |||
d309f7799c | |||
93deff6c33 | |||
c31535982c | |||
f5101782d9 | |||
5e5ae3cde6 | |||
53cf71430f | |||
04d02b55d1 | |||
043493cb62 | |||
2581c0851a | |||
27d72e87c3 | |||
eef4c22e87 | |||
4287f1716d | |||
ebc8e808a9 | |||
c9e5b599e4 | |||
e42a057048 | |||
0bb94df1da | |||
50e171c09b | |||
96697029c9 | |||
7c3b1367bc | |||
18be339ee9 | |||
ddda62b1f2 | |||
0ddae9b727 | |||
625b105d3a | |||
f9eb1f33f4 | |||
046c1a8a25 | |||
08e48c8f73 | |||
1b5e2b5129 | |||
562c8263dc | |||
99c0a7fae2 | |||
f4f6f4b4d8 | |||
cc89ef6c8c | |||
6ea5b05e7c | |||
f7b5478e9f | |||
873233e825 | |||
942104d9ac | |||
6dceaf209d | |||
2ab07d9418 | |||
1ef122988e | |||
db280fc67e | |||
ef0f29c372 | |||
1818056912 | |||
1df69cb4d2 | |||
2b20db6c5a | |||
174c016104 | |||
71ae2c4525 | |||
0f68351979 | |||
c74a438f0c | |||
c350ba29f6 | |||
6e40ef0f6d | |||
24e046fd6a | |||
979657989b | |||
8cb1046ce9 | |||
d53a898f46 | |||
f9f80003c8 | |||
d59ee3caaa | |||
b8ea71afb6 | |||
e2241a2f92 | |||
e8a1566065 | |||
d2ad871279 | |||
0b665c0ece | |||
875e66409c | |||
d7de5c4f8e | |||
51877ef4ed | |||
c377e80670 | |||
61002733bc | |||
5ff14de1f3 | |||
38069aba35 | |||
7dee1ee4cf | |||
af63378fa0 | |||
abad6673e6 | |||
75553200c0 | |||
6c77d7182a | |||
4a44832114 | |||
27539c8b80 | |||
e220a80093 | |||
3dd85cb7f1 | |||
511fe3d9f8 | |||
9ce8ef76bf | |||
7c07bfff97 | |||
86ba072758 | |||
fc1e45db92 | |||
a2deafc50f | |||
2fc5c57b31 | |||
93f323cfa2 | |||
bb9dfbc578 | |||
0bb516fae2 | |||
2ffecc0e14 | |||
b9647b7347 | |||
f25c97671a | |||
0a053a4cd5 | |||
4d7d2a2daa | |||
0cf5ece7f8 | |||
66df335998 | |||
fc2fe00d16 | |||
811962b2bb | |||
b867764b0d | |||
ce08982f78 | |||
cbe0976426 | |||
515ff61fcb | |||
d7c82f5c0f | |||
566d4361e2 | |||
ea2e5521e8 | |||
eb7d8c702c | |||
5d294624fa | |||
3aaf064d11 | |||
f38a700e35 | |||
501b83441d | |||
c03e25a7b7 | |||
1f5a5895e5 | |||
8a2324f86a | |||
6335b31702 | |||
6ef7a76e39 | |||
cc79dcac7f | |||
dc6f72e963 | |||
2b313e4979 | |||
4f8f8cfc66 | |||
8b782818f5 | |||
bd510ccdbb | |||
f1ce7607a6 | |||
7dfcaac730 | |||
c7a874dd2f | |||
aa5c8ca61f | |||
5c93a8800a | |||
05bbb8efcf | |||
14a30f3ca0 | |||
5ddecb18a7 | |||
c02325dd06 | |||
4a740f23a4 | |||
a782232ca3 | |||
a29f9f3ab8 | |||
3c2b2ff332 | |||
939d318242 | |||
39a2c39cef | |||
45e8e73670 | |||
ca41b4f5ff | |||
3c561475c8 | |||
23a27776e2 | |||
01111b04ff | |||
8560e1e4bf | |||
e0fbca9fb0 | |||
a7b76826a0 | |||
ece7985b8a | |||
9883e19e2e | |||
c631cfc2fd | |||
53c99cfc95 | |||
c56f3f2246 | |||
cc0e3d2296 | |||
917d43e108 | |||
bb7d55244d | |||
8a5eb08672 | |||
477e425f57 | |||
654ff6115a | |||
628d06c17c | |||
91980382e8 | |||
2f41b5c8a0 | |||
cd8cbd3762 | |||
292ccf882a | |||
a0e13b9797 | |||
a5c0349d88 | |||
c48021ab97 | |||
beb79e75bf | |||
0b62b6f783 | |||
895c542a20 | |||
c4fd862e15 | |||
6f18bd18bb | |||
00e157dc3b | |||
4be863c223 | |||
3009be8d6e | |||
4829fbb95c | |||
40e160c22c | |||
951ecb4d90 | |||
9318033294 | |||
4648b3e5de | |||
740e80492d | |||
cc22b051e3 | |||
75405ae0f5 | |||
f12d51992d | |||
6fd5bc075d | |||
675e582ffd | |||
0a22e8eefd | |||
44a4814766 | |||
3c23238129 | |||
916ed55d82 | |||
2451eb9ded | |||
ed639784d4 | |||
b77a3794b8 | |||
73a9ee4a05 | |||
9adf80385b | |||
a86c554a8e | |||
62078eee45 | |||
24e280a21a | |||
f7ff6c5a12 | |||
f6a7d6504c | |||
96bf42261b | |||
72bb38f83b | |||
4c9900dc3a | |||
6b26102931 | |||
bec5c5fdad | |||
04c11bb749 | |||
3f5331be9d | |||
48751cceae | |||
4a9745ef78 | |||
acc0fe6cf9 | |||
b238414984 | |||
231ed69507 | |||
60b10134df | |||
73c0a9daaf | |||
398bbb6aa9 | |||
05d1312306 | |||
bc6d1c87a6 | |||
712c7d5c3b | |||
e9479b30e8 | |||
7f6685e451 | |||
ce4eae65a7 | |||
4df48b202c | |||
33ced7088f | |||
3329977ec9 | |||
f84c3fdc5f | |||
44e1b23813 | |||
12b0a3d0e5 | |||
d2825077b1 | |||
156a52e390 | |||
bb7221f922 | |||
2eb4ee8393 | |||
87fe47737a | |||
9317056138 | |||
a235ae16ed | |||
79afcf0766 | |||
161a4dd15f | |||
6580d67875 | |||
04c6b2fe85 | |||
203b2ba637 | |||
5b99b8c18a | |||
6a011f4e8e | |||
f0c1f9ef39 | |||
97f35714f7 | |||
00e5b7d30a | |||
39c0f9ebb3 | |||
e60c765280 | |||
91dd672aa4 | |||
a5d2aadecb | |||
cc6749c158 | |||
f48142e679 | |||
947f9c3f56 | |||
7cd4741fcb | |||
2d520ae7e7 | |||
5d59c6e80f | |||
50345b8c36 | |||
7606c96c80 | |||
3466232f8b | |||
6e842fc5bf | |||
50c795280f | |||
51b22cf14f | |||
2291929a15 | |||
7fac4efede | |||
b96869afd2 | |||
6f4ee6101c | |||
6db27153ef | |||
9a11ec2624 | |||
aff1bc9f2d | |||
43512aa5eb | |||
c7f3aa71fb | |||
f9da3c98d6 | |||
a20a420be6 | |||
beadf6167a | |||
c8da7e995f | |||
8be6b12c2b | |||
4728f29f67 | |||
2a942e49ac | |||
e3aa19049f | |||
ef1eadadcd | |||
f444c11d21 | |||
d75502eeee | |||
ebcd14f8e9 | |||
b65f66feff | |||
dd68ae3ef1 | |||
1b04d70626 | |||
01bca41168 | |||
94e1ab33ce | |||
0b08dd8674 | |||
b2b47177cd | |||
f08257ff4a | |||
d1f4222c83 | |||
d21331e902 | |||
37f138e83d | |||
5dab0bad3c | |||
85e70a4cde | |||
6b564ecda5 | |||
d4cceff0ef | |||
a415613457 | |||
1a41bd1ca4 | |||
5a99393355 | |||
afcb3c0035 | |||
d2d36c61f3 | |||
0bd97ecda2 | |||
46bbcefb36 | |||
74b57dfa7d | |||
8c9c0986e9 | |||
4028fcaa51 | |||
7a8ef1eae5 | |||
e811a5d97f | |||
df44e3e425 | |||
cdb1a237e5 | |||
fcafdff10b | |||
c586656d43 | |||
3a307c2794 | |||
4f17dbc721 | |||
99989f5d3f | |||
6baf3baedd | |||
0d1f3c3b07 | |||
83e2d3d1cb | |||
b4613ab2d2 | |||
0ca05eee45 | |||
26c9e1dc70 | |||
797cb5ae7b | |||
63b82cd730 | |||
2b704f0586 | |||
ce5ba80792 | |||
8b18ef4ba2 | |||
cd18de7a21 | |||
fd19671c07 | |||
3fcd6fd93f | |||
c8d53d71a3 | |||
790362e243 | |||
2eda7a5293 | |||
9925aa89dc | |||
6195a45ae2 | |||
422d380b3e | |||
550ab31bd0 | |||
5fceb21549 | |||
29caa37943 | |||
8efbcc996a | |||
91c64d2b8d | |||
82e7ecd611 | |||
3d53b33391 | |||
34624b2db2 | |||
1ab5eb0844 | |||
630028350a | |||
7e4fd7d7da | |||
af2e80e068 | |||
8e6091de6c | |||
ecdaded25f | |||
c161ed415d | |||
ff3b71f7b3 | |||
16cc9b46aa | |||
3ce11ed58c | |||
7db75fa361 | |||
d6d4568830 | |||
a55d796c4b | |||
73f02c7861 | |||
8c8754e573 | |||
c977a906b3 | |||
e0eea6c2f4 | |||
3e377f520e | |||
8dc82a0080 | |||
4624a35845 | |||
8d4499959a | |||
2dfc9c653b | |||
106db0aba8 | |||
28c4852cd6 | |||
13c8211065 | |||
e73d0511cf | |||
e18626b7a2 | |||
4df7b1cfbc | |||
f9573ece41 | |||
93ade740e2 | |||
a46437c57d | |||
633c7d1ebe | |||
251953218c | |||
0d6cc17252 | |||
c8989c900f | |||
6134320f16 | |||
7f647822bd | |||
e34a04d2ad | |||
44093905e2 | |||
3e2900f74b | |||
11fd2eccec | |||
0eee1d5de3 | |||
1b77604ee2 | |||
cc5cfe87c3 | |||
48f230a951 | |||
2be50bdbb0 | |||
f7258ea52a | |||
28e8b2faab | |||
3c3e9ddb10 | |||
5162fb6d52 | |||
2fdb39e60a | |||
43c71ae103 | |||
f0bd528d77 | |||
50a024b42f | |||
b48f7bcb8d | |||
763ca60f5b | |||
0eca7abdd8 | |||
d0a95e35af | |||
acc6c8d0b7 | |||
3dbc66c1ac | |||
4ad6bcce54 | |||
0988cc82b0 | |||
20b03bad11 | |||
81d27daf0d | |||
bb8b82b3f5 | |||
915a6666f8 | |||
72da547d6a | |||
7c76a75452 | |||
a32c4ad2f0 | |||
fb3608aa5d | |||
0a46f37444 | |||
9b39e499ac | |||
a67cc8229d | |||
b58e9ea775 | |||
422effdd18 | |||
3b690b68a6 | |||
43349dd373 | |||
e44e8668ea | |||
69e72c0786 | |||
553344739c | |||
367f0fd142 | |||
58d9e7fc5a | |||
9d9e9c6ff1 | |||
ba88db5141 | |||
62e7c0f464 | |||
fc83bbbe98 | |||
482c019199 | |||
b449467940 | |||
0aba42ae5b | |||
0d1bf8148b | |||
b42411ba1f | |||
5a21f168d6 | |||
00b726f695 | |||
f02da4e91a | |||
d6b65db9a7 | |||
6f4e49ed53 | |||
46b212706b | |||
ca16fc29a6 | |||
9edea0b139 | |||
d15a1d64e1 | |||
c87847974a | |||
6f68330fa5 | |||
2b63330a36 | |||
06e4ca4bb3 | |||
43437c175a | |||
8d90a5a4cf | |||
93a4ca652a | |||
41178367d1 | |||
54f2edbb90 | |||
b652a7fc9f | |||
e34eb4520f | |||
190bcc89c1 | |||
64fc4648b7 | |||
bdb59129d0 | |||
d455942389 | |||
cdb3678fe3 | |||
e73ac1e992 | |||
51f3d22e4f | |||
00aa7a76b6 | |||
27b87ef535 | |||
44709e0dca | |||
31a7709ece | |||
a441b5b8fe | |||
76b8a49bfb | |||
db54a84d14 | |||
eb6ff65af7 | |||
23ee29b6a2 | |||
73a69895d8 | |||
2799e7a3ca | |||
b43f95435b | |||
450f61d384 | |||
f3dd91e1d7 | |||
979946c062 | |||
51e661eb74 | |||
921a17960c | |||
7a4f6621ed | |||
83bc5c97ef | |||
3f08efa35d | |||
0914dc35e8 | |||
b6746cce9c | |||
8cd97c2054 | |||
32d8cde9c6 | |||
1803ed2512 | |||
f08060b0b0 | |||
b77a4a40a4 | |||
e1109d52e1 | |||
0668ba50e8 | |||
44ff005ce3 | |||
aa88438b54 | |||
85be729c70 | |||
a5dc5705a3 | |||
9e3d13f61f | |||
961c9d48ae | |||
9229bbbc80 | |||
34feecf60e | |||
4c762a6be3 | |||
0426325ef7 | |||
0b54e3cf0a | |||
5cf58971f1 | |||
6518ff88b2 | |||
42b0c1d8a2 | |||
4a965052f9 | |||
5725c5925c | |||
a46291b67c | |||
4ac76ca281 | |||
e7a8e2757b | |||
1266460386 | |||
0ccb6e0dfc | |||
3050ae155c | |||
402fd934d0 | |||
6c86e8d80a | |||
60e6f91a53 | |||
e676fded21 | |||
25e070dd65 | |||
da8eb9f8b8 | |||
806a25413c | |||
5af1e891cd | |||
79eda30f0f | |||
6d02d2f107 | |||
ded518d47f | |||
27436270fd | |||
b4ea0b1601 | |||
7b31178546 | |||
2ff83324af | |||
4ef86891a3 | |||
eb5763c23f | |||
d1a3e3aff1 | |||
93d0a01d3d | |||
9af2d8b810 | |||
94dc632a6d | |||
29231877e6 | |||
e68252a79b | |||
4ec2a30942 | |||
e6b24437a9 | |||
a05f7b2d76 | |||
61e18434d3 | |||
57473e72ec | |||
c3bdd504d0 | |||
daa9da4047 | |||
245b0910ed | |||
a77db44129 | |||
34b3c534e7 | |||
fa47890032 | |||
d84a43c828 | |||
9a1babb30c | |||
30a332ee36 | |||
426b002897 | |||
2de8364de2 | |||
eacc9e6541 | |||
749dec7dfb | |||
96a9e66616 | |||
46e105f3ab | |||
f7a0e9ecb6 | |||
93025d1bc6 | |||
98d49d4ce3 | |||
1426f680f5 | |||
c7fc51a185 | |||
b7e69bc1a1 | |||
72544ba551 | |||
c43dd5a655 | |||
7f4954bed6 | |||
f1fc1dc669 | |||
cbe85a0893 | |||
7073cf74fe | |||
9d265b6f61 | |||
776a83f9da | |||
f29457f3f0 | |||
a005d1595e | |||
8d746e3f67 | |||
37e6da6dfb | |||
8aa2a0c1b2 | |||
6bfd514caf | |||
ad3f18c0dd | |||
39d04b4a15 | |||
6fbe56dbf2 | |||
8ebb8e44c8 | |||
6fcf962fb5 | |||
2708ce6a17 | |||
30bec78da3 | |||
9a04fcd061 | |||
ae62f082fd | |||
9cc3b2ca9e | |||
3fe1cb0253 | |||
0ed7773223 | |||
3f55aa609f | |||
74b45dfbf8 | |||
5c9f871b21 | |||
77dc6ef411 | |||
5eca6e4e40 | |||
0c65d5cf2b | |||
f65ebec3ed | |||
81bf3f66ca | |||
3cbded6694 | |||
137fff9632 | |||
695c08b9dd | |||
119794249b | |||
afb72164e4 | |||
9fee5630fd | |||
01de58d650 | |||
dabf214f17 | |||
fb2539e1d5 | |||
ad9f02a73e | |||
2d73583253 | |||
73f017bad9 | |||
055282f156 | |||
fe7de53b89 | |||
17e4cfc748 | |||
1608d91728 | |||
a3b90411aa | |||
5781b96490 | |||
f208ee0d57 | |||
8aa388de6c | |||
51d4c9dcbd | |||
e81dea695c | |||
3fec27961e | |||
3784696b9e | |||
8c45aebc18 | |||
810c722413 | |||
e2116c53f3 | |||
296a447e3c | |||
0961bd1eff | |||
9340e1b065 | |||
ae4fa56ee9 | |||
2d9d7f1310 | |||
5ee84fe0f6 | |||
1620426393 | |||
bf598d6b8b | |||
24eb8389d2 | |||
fcfddbf79c | |||
dc64e90ab9 | |||
e12b1277df | |||
797914e948 | |||
e0b0a594bb | |||
ed0ade6f34 | |||
5cc7b41f39 | |||
f2f1ec0117 | |||
e913d9954d | |||
d20488752b | |||
855f3afb28 | |||
3f44377f2f | |||
90295e3252 | |||
a620f95891 | |||
db66509e66 | |||
6605eb30e9 | |||
3644eef860 | |||
fb2509675d | |||
eef9512ce6 | |||
9f00a1b902 | |||
c369bc747d | |||
c03e1f2f59 | |||
17dcbf66b9 | |||
40b907a657 | |||
a33195dcf3 | |||
c693c03f1d | |||
de127109f9 | |||
83208983b3 | |||
327d04c9c6 | |||
54edce2bab | |||
1a145ac500 | |||
9f978cf49d | |||
41b781107b | |||
dcf75126bf | |||
1143b0389a | |||
97a2119596 | |||
fbd2dd9ca2 | |||
f463e09b9f | |||
42a5b6cbda | |||
0ad1215a92 | |||
7733c97df3 | |||
8a9e9c7bd3 | |||
3d8eb8cbca | |||
894747c34c | |||
8d5a312585 | |||
8eb81b3741 | |||
22d8f73bc9 | |||
249a6bdd98 | |||
3ad81b1beb | |||
5ab0534164 | |||
5150344213 | |||
98cef76931 | |||
6c5b653593 | |||
9ed8f2d26e | |||
33a2f86b28 | |||
fed1672a43 | |||
54dbed4f48 | |||
df759b8d4b | |||
6edf0474cc | |||
826f89f862 | |||
c43aec2182 | |||
ae75e3640a | |||
a5f2cc73f6 | |||
e1e5c40ef7 | |||
6420f75320 | |||
5face35ae5 | |||
398060d5ff | |||
638fd744aa | |||
098b461b69 | |||
9decc3d823 | |||
a5f2e205ef | |||
8899b83927 | |||
f6a410a4a8 | |||
3d5bb23184 | |||
ef37d2ae0b | |||
2eb234bc63 | |||
758ee95880 | |||
40e1112a8e | |||
397f5e2390 | |||
1a212259af | |||
f114dd300b | |||
5954a26bce | |||
bdbbe5aa20 | |||
15911367a2 | |||
8dd3f59c81 | |||
c9d28492b7 | |||
d1f93072a8 | |||
92d8bf9619 | |||
bd2281e32d | |||
0c50bc6449 | |||
f164715678 | |||
2aa615b4ae | |||
42c89b1b9b | |||
f6b75f56ad | |||
280540e4a2 | |||
fea216db12 | |||
b260eb06f6 | |||
1c937a10f9 | |||
ca23b4c55f | |||
7e12208ca6 | |||
2773281338 | |||
f8e8d22e4e | |||
cf4a9236b9 | |||
4450e7b246 | |||
cdbf67ee05 | |||
9a67f38728 | |||
25560ed048 | |||
2aa19fd078 | |||
cca9a58ded | |||
d6a25325c7 | |||
a717da2d3e | |||
b3e801ed9e | |||
3683fb6886 | |||
3bd0ce291e | |||
523fc5536c | |||
f5efccfb44 | |||
b6ec22de6b | |||
15f27b5455 | |||
127401598b | |||
503b07f698 | |||
f0a6329005 | |||
2982892acc | |||
9de56481f1 | |||
777eb2f159 | |||
05eebe0fed | |||
fdfbbd5bac | |||
1f3f8ef6c8 | |||
820eeb49d1 | |||
2d4be1c9eb | |||
2fef30f619 | |||
10113b63b5 | |||
545caab433 | |||
3f90659cc1 | |||
131914ac94 | |||
29a7c4538c | |||
f195bb608c | |||
66caabca0c | |||
25c6a3715d | |||
97cf0e40d5 | |||
6988a550ea | |||
8a1cdc2dd5 | |||
dadd5ddded | |||
56f8c95ee9 | |||
ed50e17e5b | |||
33b518ad21 | |||
b0e7c14545 | |||
5742d4720a | |||
9b356d9b86 | |||
793ac3f6b4 | |||
9b094e42a3 | |||
6ce7a5a1ea | |||
88920bfee1 | |||
2717bcc3af | |||
5d386dc426 | |||
f34af4f249 | |||
f04b6978fb | |||
ab958598d7 | |||
1914847e72 | |||
d95f0fd83d | |||
243612e36d | |||
c5cca8e098 | |||
99f7404d8b | |||
9ff6b0828f | |||
6f052d1daf | |||
63c194b71f | |||
46911117f1 | |||
2de1030413 | |||
1f6ade894e | |||
8407cfeac7 | |||
91d64a2855 | |||
40a06af79b | |||
8aef86f4a0 | |||
5bdc6ecec8 | |||
c179b5033b | |||
86f47273bc | |||
2e1bd46bb1 | |||
a9e773b47b | |||
10d38cbb72 | |||
a5371bfb8a | |||
4b2740f270 | |||
5b371736b2 | |||
c9b4bcf689 | |||
013f9a2bbc | |||
5bf1c93ead | |||
4f6ec01932 | |||
0f79e504c9 | |||
1a4e911b8b | |||
1f98519380 | |||
aad7010952 | |||
1be9ea681b | |||
f259a2204b | |||
4b1db0e61c | |||
aee764d14d | |||
47585498af | |||
37c5320e33 | |||
01111a1122 | |||
0b2bb1b6f5 | |||
f57df3cf8a | |||
c9c81e1fbc | |||
e38e04c1c2 | |||
99587ea4ed | |||
58b18d7fe7 | |||
04a50f5832 | |||
e157a065b0 | |||
41ef4b3d4a | |||
262650ab39 | |||
8c076d5a73 | |||
c5c456120c | |||
fdf6bc18dd | |||
297f0fd2c3 | |||
86405345b7 | |||
12c49042ab | |||
ba46ca683b | |||
ca42b49fa2 | |||
1b28cf71f5 | |||
6fd52dfb38 | |||
af2f5c3d7d | |||
65be81baf8 | |||
7a3689f175 | |||
8675b8dc48 | |||
279e816ea7 | |||
2d60ff14ae | |||
dee1b774f2 | |||
c0f2a22a08 | |||
5c8d3154d7 | |||
40f8a45b95 | |||
76a418760e | |||
49bf3f5b3a | |||
773c34900f | |||
791153c93c | |||
721f53f0d6 | |||
7498050421 | |||
5e3ccbcea9 | |||
8879aa1df4 | |||
44e0ad4987 | |||
c449f325ba | |||
5fe60759f9 | |||
6c389ed32f | |||
45549cda61 | |||
a13052fc73 | |||
4e7bb03e81 | |||
935c39a7e2 | |||
e0c1c13004 | |||
c08ca22dba | |||
a3f6e19881 | |||
37b617dccf | |||
c60ef45bc8 | |||
2b1ac63e3a | |||
6686bc62f6 | |||
1eaa193c51 | |||
b620f4f456 | |||
e484c62a8d | |||
9ba400d7d5 | |||
9cbd8f7afc | |||
8e6e90e703 | |||
26676c4833 | |||
39e0b4903c | |||
54c577cfe0 | |||
27024915e4 | |||
80f3e7591e | |||
933f45ef31 | |||
4fc37aeabd | |||
2fd1e88199 | |||
17f317d31e | |||
5941c92a31 | |||
d44d0852e5 | |||
80deac5cde | |||
8a54c1a115 | |||
0dbff55bc6 | |||
22916bb5d1 | |||
fe01e2efb7 | |||
7afee97d1b | |||
6fc267f22c | |||
ac84468f1c | |||
387a90e546 | |||
6eeb9495d8 | |||
566b4ef481 | |||
a191e9697c | |||
5504ca1e38 | |||
5c0cfdee48 | |||
bc888bf3a1 | |||
1745366530 | |||
1fb0db4aeb | |||
61960c51a3 | |||
14a3ade662 | |||
de97687422 | |||
a0251305ea | |||
2b8d12ddf0 | |||
1f6fd3c8fc | |||
55860e1621 | |||
4d51158b1a | |||
7d9c1e1225 | |||
d53edfec47 | |||
a6e5ddc5af | |||
b866f32832 | |||
97833d48c1 | |||
9c0031f7a5 | |||
164a091c71 | |||
4ed6cf7519 | |||
5267115481 | |||
43148d8233 | |||
537e99b4ea | |||
e1fcab777c | |||
f39c9c9e75 | |||
bbed364e7b | |||
3aca5ff9e2 | |||
9146bb0816 | |||
0c9e8dbf60 | |||
b34a04d53a | |||
0658eb4429 | |||
e178ee4ba0 | |||
9f506cd330 | |||
729dc3b764 | |||
7ce0fc7d47 | |||
b60eecfc47 | |||
346304762e | |||
e213939f28 | |||
4c39eace52 | |||
86fbd50c3d | |||
87d824e1b4 | |||
50acb96130 | |||
29c2dcff61 | |||
cea103a7ff | |||
d18694a1c3 | |||
b746c64229 | |||
d38aa5e25f | |||
efdc2d5118 | |||
515a8e0765 | |||
45de65bd45 | |||
9d6b98794e | |||
7aa1790874 | |||
5cd490eba2 | |||
ac1156739d | |||
7cefec77ef | |||
36d25f2a07 | |||
c3d2459a4e | |||
8847580fd7 | |||
cf3548a02f | |||
c197e2bb42 | |||
d1c989b8a5 | |||
57c9a07fff | |||
a19c4e8f9a | |||
53083c0b52 | |||
994d9212c1 | |||
52ddc96c9f | |||
057abefe50 | |||
f0e24b1a1e | |||
a1e3004e62 | |||
e504d4eb05 | |||
a6ad61d83e | |||
27a47e7841 | |||
b00b9fe564 | |||
fa0718ba9a | |||
155b88213c | |||
35ea02fb81 | |||
ddd2ac4f55 | |||
6f281ab3c4 | |||
fe8a7b0e82 | |||
76e6214b9b | |||
2d8f776e38 | |||
cf2d3cf920 | |||
b160ada5d1 | |||
1090601e8b | |||
01dd7dde24 | |||
13c39a52c6 | |||
3b80ab51ba | |||
1a386a58c8 | |||
04220be8fd | |||
48bf349c3c | |||
21fc1bb655 | |||
d38d375fa6 | |||
602836800b | |||
2953ea10a7 | |||
a738d0d54d | |||
d781e69948 | |||
c9b71fb5e2 | |||
dd6cb233b5 | |||
a3cf58b67a | |||
70d944a59c | |||
a5a422f8e7 | |||
e93b3d2360 | |||
7bc2d9a93a | |||
1c929031a2 | |||
f2809d1ed8 | |||
f4f6b8721a | |||
7e352a27f7 | |||
0c6b16c208 | |||
10475b859d | |||
4d793c4eb8 | |||
3ae29c08ac | |||
3331321f64 | |||
c6064a30a1 | |||
040b101842 | |||
a78a43c816 | |||
ec198b0dc6 | |||
b5d14c26d2 | |||
2019050db2 | |||
5f999225ba | |||
307d105d2c | |||
0b6967fd74 | |||
ef0c32512c | |||
9096481744 | |||
5936624d11 | |||
83723671af | |||
4d825dd9fd | |||
8e38291156 | |||
f93512bf27 | |||
c3fafa0651 | |||
9036f78b74 | |||
263122ea5f | |||
324f0147f6 | |||
be48ba1b06 | |||
b2a7fd05cb | |||
84f859d7b2 | |||
cbc8d0adf8 | |||
420e83a396 | |||
89f61087c7 | |||
1dcb663917 | |||
b7b56785d1 | |||
3a62023260 | |||
fa2ce8100b | |||
172a5663ef | |||
f4b972815b | |||
0cb93a436d | |||
b2e804c961 | |||
85ce184197 | |||
29c77df4be | |||
c39e0463a3 | |||
c27bc1956b | |||
72707d80ab | |||
f18356307b | |||
00475f25c8 | |||
bab6023eee | |||
5e12a95789 | |||
e5904f4089 | |||
cf1122cf9e | |||
6da79673c2 | |||
352ee53202 | |||
a20639558b | |||
b9347eb01c | |||
4dbd8ed6b8 | |||
cb980d3e43 | |||
f154e2c6cf | |||
d0a64f9c86 | |||
16ef21d086 | |||
39ecd01b86 | |||
5e0f8cf3f0 | |||
6c6b316bd9 | |||
b49dac7be5 | |||
4c26397937 | |||
e26e4f922e | |||
2ab1085dfb | |||
acc3a2de83 | |||
12abdd8782 | |||
2bcdec5aaf | |||
16dfe3c63f | |||
b4a467e387 | |||
53628e19ac | |||
62dd3ceb64 | |||
d5066a9a0f | |||
32b37432b0 | |||
5f3d02bc7c | |||
97e94dd6e0 | |||
846c031ec9 | |||
92340350d2 | |||
d4827caa08 | |||
6ce13b68fa | |||
e82b700ad0 | |||
60a2ba87d4 | |||
83c19a1fbc | |||
1513e201bb | |||
a38c9a1ef7 | |||
d5f5ce82ca | |||
6dc88f5b61 | |||
7a2ce7ff21 | |||
1ac38bd69a | |||
982fad0c45 | |||
390cefac72 | |||
798bface7f | |||
276fec6e50 | |||
95af14b97c | |||
0f0a8ade7c | |||
e0c83f669e | |||
016f0d8e9e | |||
3e5716ec16 | |||
75e6dfb9ab | |||
9634e8d14a | |||
f95a604b59 | |||
3ff20cd7e3 | |||
17f73cb7bc | |||
fba0e2ff12 | |||
b62415c962 | |||
adc135e6c8 | |||
ceac045a7f | |||
4d6da7b1a7 | |||
01b9de7a15 | |||
0a872ffd38 | |||
6f3a6a55a0 | |||
0795dd307b | |||
49fb7ef421 | |||
cbeeff2bd6 | |||
ce013a3dd9 | |||
1f7449ccf4 | |||
c43636f2bb | |||
9c2fe660a3 | |||
abc266fa35 | |||
7d853dd9ad | |||
3ac2821a1b | |||
666dc75c15 | |||
e9332c66d2 | |||
830aecd1a7 | |||
ac6959c819 | |||
2bf21e1747 | |||
9105ab9596 | |||
165357bfa3 | |||
33c7f74cb9 | |||
cb84cbf545 | |||
b2e0946696 | |||
67c80fbb5e | |||
c574e6808f | |||
9175a049d3 | |||
0035575c82 | |||
7bfe8aa553 | |||
1bff47f97d | |||
e8e61de28d | |||
54f8308999 | |||
6c99746f0b | |||
4086b49046 | |||
0fad9c2786 | |||
d75f928fca | |||
68f9aaf214 | |||
efe4633b15 | |||
19e65382f7 | |||
5a9eda73b4 | |||
c6f2b3e96b | |||
55921be1af | |||
9b1b5b393d | |||
3857c8226e | |||
595bcdd1ac | |||
27c25bd0e8 | |||
aec95015f8 | |||
b2db6401cc | |||
ebe14720eb | |||
7112d008d0 | |||
90af4763d8 | |||
390046d7b3 | |||
587c119c75 | |||
3019140e7e | |||
7a80f0d1e1 | |||
0894318f50 | |||
f9fc524a74 | |||
9019c6f937 | |||
166b73f4f3 | |||
cb94111f18 | |||
a88b887a05 | |||
6e62217b78 | |||
a01a54c180 | |||
db8290632f | |||
7bb5167239 | |||
6cdc53c497 | |||
15ae710d22 | |||
dd50124254 | |||
ff36b0384a | |||
50c9bed630 | |||
8b1b427195 | |||
9a05ca95f6 | |||
05266241af | |||
4ddf5536b4 | |||
f389b5a961 | |||
bac1a6eab3 | |||
ff400726ca | |||
267d864976 | |||
97a1084c99 | |||
61b339678d | |||
d537a26297 | |||
d414734aac | |||
817ddfa847 | |||
c1154b30c7 | |||
0d71345b93 | |||
f235454dd6 | |||
6a80578d05 | |||
d33cd43db1 | |||
9e3df8eefe | |||
cf73ad7c8f | |||
3e68b7eb1f | |||
9fbafba993 | |||
7a524e3deb | |||
7b6c4d5acc | |||
99c0d503d7 | |||
f86edae9f3 | |||
df1b1f6957 | |||
9099160038 | |||
119abe7bb9 | |||
67ed2e2c0a | |||
6d36a7a45f | |||
e2b1e1577d | |||
b30ddfbfc5 | |||
abfb522f83 | |||
b8136cc26e | |||
d00b26d941 | |||
12637a761c | |||
1a0aea67a0 | |||
0f1465b899 | |||
c0cfd3c6ed | |||
a81923b793 | |||
7150ace7c7 | |||
bdce154282 | |||
5a84048f72 | |||
188bda813e | |||
29700aa188 | |||
3229bf1665 | |||
52595f52f9 | |||
edec158dd8 | |||
0297398f5e | |||
9485f5a813 | |||
8f8c017882 | |||
eba6e7946d | |||
3cfe281790 | |||
6eac4b68bc | |||
fcbfacb6d5 | |||
b600915953 | |||
905f38acb8 | |||
38f4c5f155 | |||
509f4ec611 | |||
27a7b51d99 | |||
a033f8335b | |||
b98c9e74e1 | |||
9f784dcc5a | |||
b625f2471a | |||
3aa2606ff1 | |||
89704e0f93 | |||
908a102a87 | |||
dd6e0cf1b5 | |||
52a6ba7ed9 | |||
43e0fa513b | |||
de978229b2 | |||
ba62fe974b | |||
3a40cb1a85 | |||
883e0c48b1 | |||
18b6a55764 |
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -1,5 +1,9 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# JS files must always use LF for tools to work
|
# JS and TS files must always use LF for tools to work
|
||||||
*.js eol=lf
|
*.js eol=lf
|
||||||
|
*.ts eol=lf
|
||||||
|
|
||||||
|
# Must keep Windows line ending to be parsed correctly
|
||||||
|
scripts/windows/packages.txt eol=crlf
|
||||||
|
41
.github/ISSUE_TEMPLATE.md
vendored
41
.github/ISSUE_TEMPLATE.md
vendored
@ -1,16 +1,39 @@
|
|||||||
**IMPORTANT**: This repository's issues are reserved for feature requests and bug reports. Do not submit support requests here, see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question.
|
<!--
|
||||||
|
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
|
||||||
|
-->
|
||||||
|
|
||||||
|
**I'm submitting a ...** (check one with "x")
|
||||||
**Steps to reproduce and a minimal demo of the problem**
|
```
|
||||||
|
[ ] bug report => search github for a similar issue or PR before submitting
|
||||||
_Use https://plnkr.co or similar -- try this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5_
|
[ ] feature request
|
||||||
|
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||||
_What steps should we try in your demo to see the problem?_
|
```
|
||||||
|
|
||||||
**Current behavior**
|
**Current behavior**
|
||||||
|
<!-- Describe how the bug manifests. -->
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
<!-- Describe what the behavior would be without the bug. -->
|
||||||
|
|
||||||
**Expected/desired behavior**
|
**Minimal reproduction of the problem with instructions**
|
||||||
|
<!--
|
||||||
|
If the current behavior is a bug or you can illustrate your feature request better with an example,
|
||||||
|
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||||
|
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||||
|
-->
|
||||||
|
|
||||||
|
**What is the motivation / use case for changing the behavior?**
|
||||||
|
<!-- Describe the motivation or the concrete use case -->
|
||||||
|
|
||||||
**Other information**
|
**Please tell us about your environment:**
|
||||||
|
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
||||||
|
|
||||||
|
* **Angular version:** 2.0.X
|
||||||
|
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||||
|
|
||||||
|
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||||
|
<!-- All browsers where this could be reproduced -->
|
||||||
|
|
||||||
|
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
||||||
|
|
||||||
|
* **Node (for AoT issues):** `node --version` =
|
||||||
|
34
.github/PULL_REQUEST_TEMPLATE.md
vendored
34
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,24 +1,36 @@
|
|||||||
* **Please check if the PR fulfills these requirements**
|
**Please check if the PR fulfills these requirements**
|
||||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit-message-format
|
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
|
||||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||||
|
|
||||||
|
|
||||||
* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
**What kind of change does this PR introduce?** (check one with "x")
|
||||||
|
```
|
||||||
|
[ ] Bugfix
|
||||||
|
[ ] Feature
|
||||||
|
[ ] Code style update (formatting, local variables)
|
||||||
|
[ ] Refactoring (no functional changes, no api changes)
|
||||||
|
[ ] Build related changes
|
||||||
|
[ ] CI related changes
|
||||||
|
[ ] Other... Please describe:
|
||||||
|
```
|
||||||
|
|
||||||
|
**What is the current behavior?** (You can also link to an open issue here)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **What is the current behavior?** (You can also link to an open issue here)
|
**What is the new behavior?**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **What is the new behavior (if this is a feature change)?**
|
**Does this PR introduce a breaking change?** (check one with "x")
|
||||||
|
```
|
||||||
|
[ ] Yes
|
||||||
|
[ ] No
|
||||||
|
```
|
||||||
|
|
||||||
|
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
|
||||||
|
|
||||||
|
|
||||||
|
**Other information**:
|
||||||
* **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Other information**:
|
|
||||||
|
|
||||||
|
37
.gitignore
vendored
37
.gitignore
vendored
@ -1,36 +1,9 @@
|
|||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|
||||||
# Don’t commit the following directories created by pub.
|
|
||||||
packages
|
|
||||||
pubspec.lock
|
|
||||||
.pub
|
|
||||||
.packages
|
|
||||||
|
|
||||||
/dist/
|
/dist/
|
||||||
.buildlog
|
|
||||||
node_modules
|
node_modules
|
||||||
bower_components
|
bower_components
|
||||||
|
|
||||||
# Or broccoli working directory
|
|
||||||
tmp
|
|
||||||
|
|
||||||
# Or the files created by dart2js.
|
|
||||||
*.dart.js
|
|
||||||
*.dart.precompiled.js
|
|
||||||
*.js_
|
|
||||||
*.js.deps
|
|
||||||
*.js.map
|
|
||||||
|
|
||||||
# Files created by the template compiler
|
|
||||||
**/*.ngfactory.ts
|
|
||||||
**/*.css.ts
|
|
||||||
**/*.css.shim.ts
|
|
||||||
|
|
||||||
# Or type definitions we mirror from github
|
|
||||||
# (NB: these lines are removed in publish-build-artifacts.sh)
|
|
||||||
**/typings/**/*.d.ts
|
|
||||||
**/typings/tsd.cached.json
|
|
||||||
|
|
||||||
# Include when developing application packages.
|
# Include when developing application packages.
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
.c9
|
.c9
|
||||||
@ -47,13 +20,13 @@ modules/.vscode
|
|||||||
# Ignore npm debug log
|
# Ignore npm debug log
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
/docs/bower_components/
|
|
||||||
|
|
||||||
# build-analytics
|
# build-analytics
|
||||||
.build-analytics
|
.build-analytics
|
||||||
|
|
||||||
# built dart payload tests
|
|
||||||
/modules_dart/payload/**/build
|
|
||||||
|
|
||||||
# rollup-test output
|
# rollup-test output
|
||||||
/modules/rollup-test/dist/
|
/modules/rollup-test/dist/
|
||||||
|
|
||||||
|
# angular.io
|
||||||
|
/aio/node_modules
|
||||||
|
/aio/src/content/docs
|
||||||
|
/aio/dist
|
||||||
|
249
.pullapprove.yml
Normal file
249
.pullapprove.yml
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
# Configuration for pullapprove.com
|
||||||
|
#
|
||||||
|
# Approval access and primary role is determined by info in the project ownership spreadsheet:
|
||||||
|
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
|
||||||
|
#
|
||||||
|
# === GitHub username to Full name map ===
|
||||||
|
#
|
||||||
|
# alexeagle - Alex Eagle
|
||||||
|
# alxhub - Alex Rickabaugh
|
||||||
|
# chuckjaz - Chuck Jazdzewski
|
||||||
|
# gkalpak - George Kalpakas
|
||||||
|
# IgorMinar - Igor Minar
|
||||||
|
# kara - Kara Erickson
|
||||||
|
# matsko - Matias Niemelä
|
||||||
|
# mhevery - Misko Hevery
|
||||||
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
|
# pkozlowski-opensource - Pawel Kozlowski
|
||||||
|
# robwormald - Rob Wormald
|
||||||
|
# tbosch - Tobias Bosch
|
||||||
|
# vicb - Victor Berchet
|
||||||
|
# vikerman - Vikram Subramanian
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
group_defaults:
|
||||||
|
required: 1
|
||||||
|
reset_on_reopened:
|
||||||
|
enabled: true
|
||||||
|
approve_by_comment:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
groups:
|
||||||
|
root:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*"
|
||||||
|
exclude:
|
||||||
|
- "aio/*"
|
||||||
|
- "integration/*"
|
||||||
|
- "modules/*"
|
||||||
|
- "tools/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
public-api:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "tools/public_api_guard/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
build-and-ci:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*.yml"
|
||||||
|
- "*.json"
|
||||||
|
- "*.lock"
|
||||||
|
- "tools/*"
|
||||||
|
exclude:
|
||||||
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
|
- "tools/public_api_guard/*"
|
||||||
|
- "aio/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar #primary
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
integration:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "integration/*"
|
||||||
|
users:
|
||||||
|
- alexeagle
|
||||||
|
- mhevery
|
||||||
|
- tbosch
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
|
||||||
|
core:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/core/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
- mhevery
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler/animations:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/compiler/src/animation/*"
|
||||||
|
users:
|
||||||
|
- matsko #primary
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
compiler/i18n:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/compiler/src/i18n/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
compiler:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/compiler/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
- vicb
|
||||||
|
- chuckjaz
|
||||||
|
- mhevery
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler-cli:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
|
- "modules/@angular/compiler-cli/*"
|
||||||
|
users:
|
||||||
|
- alexeagle
|
||||||
|
- chuckjaz
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
common:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/common/*"
|
||||||
|
users:
|
||||||
|
- pkozlowski-opensource #primary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
forms:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/forms/*"
|
||||||
|
users:
|
||||||
|
- kara #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
http:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/http/*"
|
||||||
|
users:
|
||||||
|
- vikerman #primary
|
||||||
|
- alxhub
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
language-service:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/language-service/*"
|
||||||
|
users:
|
||||||
|
- chuckjaz #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
router:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/router/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
upgrade:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/upgrade/*"
|
||||||
|
users:
|
||||||
|
- petebacondarwin #primary
|
||||||
|
- gkalpak
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-browser:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/platform-browser/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
- vicb #secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-server:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/platform-server/*"
|
||||||
|
users:
|
||||||
|
- vikerman #primary
|
||||||
|
- alxhub
|
||||||
|
- vicb
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-webworker:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/platform-webworker/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
- tbosch #secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
benchpress:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/benchpress/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
angular.io:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "aio/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- robwormald
|
||||||
|
- petebacondarwin
|
||||||
|
- mhevery #fallback
|
182
.travis.yml
182
.travis.yml
@ -1,7 +1,7 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
node_js:
|
||||||
- '5.4.1'
|
- '6.9.5'
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
# firefox: "38.0"
|
# firefox: "38.0"
|
||||||
@ -17,176 +17,46 @@ branches:
|
|||||||
- g3_v2_0
|
- g3_v2_0
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
|
yarn: true
|
||||||
directories:
|
directories:
|
||||||
- ./node_modules
|
- ./node_modules
|
||||||
- ./.chrome/chromium
|
- ./.chrome/chromium
|
||||||
# - $HOME/.pub-cache
|
- ./aio/node_modules
|
||||||
|
|
||||||
|
|
||||||
#before_cache:
|
|
||||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
|
||||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
# GITHUB_TOKEN_ANGULAR
|
||||||
# - E2E_BROWSERS=ChromeOnTravis
|
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||||
# - LOGS_DIR=/tmp/angular-build/logs
|
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||||
# - ARCH=linux-x64
|
# FIREBASE_TOKEN
|
||||||
|
# This is needed for publishing builds to the "aio-staging" firebase site.
|
||||||
|
# TODO(i): the token was generated using the iminar@google account, we should switch to a shared/role-base account.
|
||||||
|
- secure: "MPx3UM77o5IlhT75PKHL0FXoB5tSXDc3vnCXCd1sRy4XUTZ9vjcV6nNuyqEf+SOw659bGbC1FI4mACGx1Q+z7MQDR85b1mcA9uSgHDkh+IR82CnCVdaX9d1RXafdJIArahxfmorbiiPPLyPIKggo7ituRm+2c+iraoCkE/pXxYg="
|
||||||
matrix:
|
matrix:
|
||||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||||
- CI_MODE=js
|
- CI_MODE=e2e EXPERIMENTAL_ES2015_DISTRO=1
|
||||||
- CI_MODE=lint
|
- CI_MODE=js
|
||||||
- CI_MODE=e2e
|
- CI_MODE=saucelabs_required
|
||||||
- CI_MODE=saucelabs_required
|
- CI_MODE=browserstack_required
|
||||||
- CI_MODE=browserstack_required
|
- CI_MODE=saucelabs_optional
|
||||||
|
- CI_MODE=browserstack_optional
|
||||||
#matrix:
|
- CI_MODE=docs_test
|
||||||
# allow_failures:
|
- CI_MODE=aio
|
||||||
# - env: "MODE=saucelabs_optional"
|
|
||||||
# - env: "MODE=browserstack_optional"
|
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
allow_failures:
|
||||||
|
- env: "CI_MODE=saucelabs_optional"
|
||||||
|
- env: "CI_MODE=browserstack_optional"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ./scripts/ci-lite/install.sh
|
- ./scripts/ci-lite/install.sh
|
||||||
|
|
||||||
before_script:
|
|
||||||
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- ./scripts/ci-lite/deploy_aio_staging.sh
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- ./scripts/ci-lite/cleanup.sh
|
- ./scripts/ci-lite/cleanup.sh
|
||||||
|
|
||||||
|
|
||||||
#branches:
|
|
||||||
# except:
|
|
||||||
# - g3_v2_0
|
|
||||||
#
|
|
||||||
#cache:
|
|
||||||
# directories:
|
|
||||||
# - $HOME/.pub-cache
|
|
||||||
# - $HOME/.chrome/chromium
|
|
||||||
#
|
|
||||||
#before_cache:
|
|
||||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
|
||||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
|
||||||
#
|
|
||||||
#env:
|
|
||||||
# global:
|
|
||||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
|
||||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
|
||||||
# - CXX=g++-4.8
|
|
||||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
|
||||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
|
||||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
|
||||||
# - E2E_BROWSERS=ChromeOnTravis
|
|
||||||
# - LOGS_DIR=/tmp/angular-build/logs
|
|
||||||
# - SAUCE_USERNAME=angular-ci
|
|
||||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
|
||||||
# - BROWSER_STACK_USERNAME=angularteam1
|
|
||||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
|
||||||
# - ARCH=linux-x64
|
|
||||||
# - DART_DEV_VERSION=latest
|
|
||||||
# - DART_STABLE_VERSION=latest
|
|
||||||
# - DART_CHANNEL=stable
|
|
||||||
# - DART_VERSION=$DART_STABLE_VERSION
|
|
||||||
# # Token for tsd to increase github rate limit
|
|
||||||
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
|
|
||||||
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
|
||||||
# # because those are not visible for pull requests, and those should also be reliable.
|
|
||||||
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
|
||||||
# # (password is in Valentine)
|
|
||||||
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
|
||||||
# # GITHUB_TOKEN_ANGULAR
|
|
||||||
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
|
||||||
# matrix:
|
|
||||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
|
||||||
# - MODE=dart
|
|
||||||
# - MODE=dart DART_CHANNEL=dev
|
|
||||||
# - MODE=saucelabs_required
|
|
||||||
# - MODE=browserstack_required
|
|
||||||
# - MODE=saucelabs_optional
|
|
||||||
# - MODE=browserstack_optional
|
|
||||||
# - MODE=dart_ddc
|
|
||||||
# - MODE=js
|
|
||||||
# - MODE=router
|
|
||||||
# - MODE=build_only
|
|
||||||
# - MODE=typescript_next
|
|
||||||
# - MODE=lint
|
|
||||||
#
|
|
||||||
#matrix:
|
|
||||||
# allow_failures:
|
|
||||||
# - env: "MODE=saucelabs_optional"
|
|
||||||
# - env: "MODE=browserstack_optional"
|
|
||||||
#
|
|
||||||
#addons:
|
|
||||||
# firefox: "38.0"
|
|
||||||
# apt:
|
|
||||||
# sources:
|
|
||||||
# - ubuntu-toolchain-r-test
|
|
||||||
# packages:
|
|
||||||
# - g++-4.8
|
|
||||||
#
|
|
||||||
#before_install:
|
|
||||||
# - node tools/analytics/build-analytics start ci job
|
|
||||||
# - node tools/analytics/build-analytics start ci before_install
|
|
||||||
# - echo ${TSDRC} > .tsdrc
|
|
||||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
|
||||||
# - export DISPLAY=:99.0
|
|
||||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
|
||||||
# - ./scripts/ci/init_android.sh
|
|
||||||
# - sh -e /etc/init.d/xvfb start
|
|
||||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
|
||||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
|
||||||
# - node tools/analytics/build-analytics success ci before_install
|
|
||||||
#
|
|
||||||
#install:
|
|
||||||
# - node tools/analytics/build-analytics start ci install
|
|
||||||
# # Install version of npm that we are locked against
|
|
||||||
# - npm install -g npm@3.5.3
|
|
||||||
# # Install version of Chromium that we are locked against
|
|
||||||
# - ./scripts/ci/install_chromium.sh
|
|
||||||
# # Install version of Dart based on the matrix build variables
|
|
||||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
|
||||||
# # Print the size of caches to ease debugging
|
|
||||||
# - du -sh ./node_modules || true
|
|
||||||
# # Install npm dependecies
|
|
||||||
# # check-node-modules will exit(1) if we don't need to install
|
|
||||||
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
|
||||||
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
|
||||||
# - node tools/analytics/build-analytics success ci install
|
|
||||||
#
|
|
||||||
#before_script:
|
|
||||||
# - node tools/analytics/build-analytics start ci before_script
|
|
||||||
# - mkdir -p $LOGS_DIR
|
|
||||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
|
||||||
# - node tools/analytics/build-analytics success ci before_script
|
|
||||||
#
|
|
||||||
#script:
|
|
||||||
# - node tools/analytics/build-analytics start ci script
|
|
||||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
|
||||||
# - node tools/analytics/build-analytics success ci script
|
|
||||||
#
|
|
||||||
#after_script:
|
|
||||||
# - node tools/analytics/build-analytics start ci after_script
|
|
||||||
# - ./scripts/ci/print-logs.sh
|
|
||||||
# - ./scripts/ci/after-script.sh
|
|
||||||
# - ./scripts/publish/publish-build-artifacts.sh
|
|
||||||
# - node tools/analytics/build-analytics success ci after_script
|
|
||||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
|
||||||
#
|
|
||||||
#notifications:
|
|
||||||
# webhooks:
|
|
||||||
# urls:
|
|
||||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
|
||||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
|
||||||
# - https://buildtimetrend.herokuapp.com/travis
|
|
||||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
|
||||||
# on_success: always # options: [always|never|change] default: always
|
|
||||||
# on_failure: always # options: [always|never|change] default: always
|
|
||||||
# on_start: never # default: never
|
|
||||||
# slack:
|
|
||||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
|
||||||
|
2465
CHANGELOG.md
2465
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
29
COMMITTER.md
29
COMMITTER.md
@ -1,4 +1,4 @@
|
|||||||
# Pushing changes into the Angular 2 tree
|
# Pushing changes into the Angular tree
|
||||||
|
|
||||||
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
|
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
|
||||||
for details about how we maintain a linear commit history, and the rules for committing.
|
for details about how we maintain a linear commit history, and the rules for committing.
|
||||||
@ -6,29 +6,16 @@ for details about how we maintain a linear commit history, and the rules for com
|
|||||||
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
||||||
Someone with committer access will do the rest.
|
Someone with committer access will do the rest.
|
||||||
|
|
||||||
## The `PR: merge` label and `presubmit-*` branches
|
# Change approvals
|
||||||
|
|
||||||
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
Change approvals in our monorepo are managed via [pullapprove.com](https://about.pullapprove.com/) and are configured via the `.pullapprove.yaml` file.
|
||||||
Angular committers and also prevent breakages on master.
|
|
||||||
|
|
||||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
|
||||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
|
||||||
so the caretaker reviews the changes manually.
|
|
||||||
|
|
||||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
# Merging
|
||||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
|
||||||
is notified that the label was added by an authorized person,
|
|
||||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
|
||||||
|
|
||||||
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
|
Once a change has all the approvals either the last approver or the PR author (if PR author has the project collaborator status) should mark the PR with "PR: merge" label.
|
||||||
|
This signals to the caretaker that the PR should be merged.
|
||||||
|
|
||||||
When a Travis build succeeds for a presubmit branch named following the convention,
|
# Who is the Caretaker?
|
||||||
Travis will re-base the commits, merge to master, and close the PR automatically.
|
|
||||||
|
|
||||||
Finally, after merge `mary-poppins` removes the presubmit branch.
|
See [this explanation](https://twitter.com/IgorMinar/status/799365744806854656).
|
||||||
|
|
||||||
## Administration
|
|
||||||
|
|
||||||
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
|
|
||||||
Edit the contents of the [CoreTeamMember Table](
|
|
||||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
|
||||||
|
124
CONTRIBUTING.md
124
CONTRIBUTING.md
@ -1,6 +1,6 @@
|
|||||||
# Contributing to Angular 2
|
# Contributing to Angular
|
||||||
|
|
||||||
We would love for you to contribute to Angular 2 and help make it even better than it is
|
We would love for you to contribute to Angular and help make it even better than it is
|
||||||
today! As a contributor, here are the guidelines we would like you to follow:
|
today! As a contributor, here are the guidelines we would like you to follow:
|
||||||
|
|
||||||
- [Code of Conduct](#coc)
|
- [Code of Conduct](#coc)
|
||||||
@ -17,19 +17,27 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
|||||||
|
|
||||||
## <a name="question"></a> Got a Question or Problem?
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](https://stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||||
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
|
||||||
|
|
||||||
## <a name="issue"></a> Found an Issue?
|
StackOverflow is a much better place to ask questions since:
|
||||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
|
||||||
|
- there are thousands of people willing to help on StackOverflow
|
||||||
|
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||||
|
- StackOverflow's voting system assures that the best answers are prominently visible.
|
||||||
|
|
||||||
|
To save your and our time we will be systematically closing all the issues that are requests for general support and redirecting people to StackOverflow.
|
||||||
|
|
||||||
|
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
||||||
|
|
||||||
|
## <a name="issue"></a> Found a Bug?
|
||||||
|
If you find a bug in the source code, you can help us by
|
||||||
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
||||||
[submit a Pull Request](#submit-pr) with a fix.
|
[submit a Pull Request](#submit-pr) with a fix.
|
||||||
|
|
||||||
## <a name="feature"></a> Want a Feature?
|
## <a name="feature"></a> Missing a Feature?
|
||||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
|
||||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
Repository. If you would like to *implement* a new feature, please submit an issue with
|
||||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
a proposal for your work first, to be sure that we can use it.
|
||||||
and we are not ready to accept major contributions ahead of the full release.
|
|
||||||
Please consider what kind of change it is:
|
Please consider what kind of change it is:
|
||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
@ -40,24 +48,22 @@ and help you to craft the change so that it is successfully accepted into the pr
|
|||||||
## <a name="submit"></a> Submission Guidelines
|
## <a name="submit"></a> Submission Guidelines
|
||||||
|
|
||||||
### <a name="submit-issue"></a> Submitting an Issue
|
### <a name="submit-issue"></a> Submitting an Issue
|
||||||
Before you submit an issue, search the archive, maybe your question was already answered.
|
|
||||||
|
|
||||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||||
Help us to maximize the effort we can spend fixing issues and adding new
|
|
||||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
|
||||||
chances of your issue being dealt with quickly:
|
|
||||||
|
|
||||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
|
||||||
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
|
|
||||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
|
||||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
|
||||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
|
||||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
|
|
||||||
* **Related Issues** - has a similar issue been reported before?
|
|
||||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
|
||||||
causing the problem (line of code or commit)
|
|
||||||
|
|
||||||
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
|
- version of Angular used
|
||||||
|
- 3rd-party libraries and their versions
|
||||||
|
- and most importantly - a use-case that fails
|
||||||
|
|
||||||
|
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.
|
||||||
|
|
||||||
|
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||||
|
|
||||||
|
Unfortunately we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.
|
||||||
|
|
||||||
|
You can file new issues by filling out our [new issue form](https://github.com/angular/angular/issues/new).
|
||||||
|
|
||||||
|
|
||||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||||
@ -95,7 +101,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
* In GitHub, send a pull request to `angular:master`.
|
* In GitHub, send a pull request to `angular:master`.
|
||||||
* If we suggest changes then:
|
* If we suggest changes then:
|
||||||
* Make the required updates.
|
* Make the required updates.
|
||||||
* Re-run the Angular 2 test suites for JS and Dart to ensure tests are still passing.
|
* Re-run the Angular test suites to ensure tests are still passing.
|
||||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -166,27 +172,63 @@ The **header** is mandatory and the **scope** of the header is optional.
|
|||||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
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.
|
to read on GitHub as well as in various git tools.
|
||||||
|
|
||||||
|
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||||
|
|
||||||
|
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||||||
|
|
||||||
|
```
|
||||||
|
docs(changelog): update change log to beta.5
|
||||||
|
```
|
||||||
|
```
|
||||||
|
fix(release): need to depend on latest rxjs and zone.js
|
||||||
|
|
||||||
|
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||||
|
```
|
||||||
|
|
||||||
### Revert
|
### Revert
|
||||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||||
|
|
||||||
### Type
|
### Type
|
||||||
Must be one of the following:
|
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 nor adds a feature
|
|
||||||
* **perf**: A code change that improves performance
|
|
||||||
* **test**: Adding missing tests or correcting existing tests
|
|
||||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||||
* **chore**: Other changes that don't modify `src` or `test` files
|
* **docs**: Documentation only changes
|
||||||
|
* **feat**: A new feature
|
||||||
|
* **fix**: A bug fix
|
||||||
|
* **perf**: A code change that improves performance
|
||||||
|
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||||
|
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||||
|
semi-colons, etc)
|
||||||
|
* **test**: Adding missing tests or correcting existing tests
|
||||||
|
|
||||||
### Scope
|
### Scope
|
||||||
The scope could be anything specifying place of the commit change. For example
|
The scope should be the name of the npm package affected (as perceived by person reading changelog generated from commit messages.
|
||||||
`Compiler`, `ElementInjector`, etc.
|
|
||||||
|
The following is the list of supported scopes:
|
||||||
|
|
||||||
|
* **common**
|
||||||
|
* **compiler**
|
||||||
|
* **compiler-cli**
|
||||||
|
* **core**
|
||||||
|
* **forms**
|
||||||
|
* **http**
|
||||||
|
* **language-service**
|
||||||
|
* **platform-browser**
|
||||||
|
* **platform-browser-dynamic**
|
||||||
|
* **platform-server**
|
||||||
|
* **platform-webworker**
|
||||||
|
* **platform-webworker-dynamic**
|
||||||
|
* **router**
|
||||||
|
* **upgrade**
|
||||||
|
* **tsc-wrapped**
|
||||||
|
|
||||||
|
There is currently few exception to the "use package name" rule:
|
||||||
|
|
||||||
|
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||||
|
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||||
|
* **aio**: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
||||||
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||||
|
|
||||||
### Subject
|
### Subject
|
||||||
The subject contains succinct description of the change:
|
The subject contains succinct description of the change:
|
||||||
@ -225,8 +267,8 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
|||||||
[github]: https://github.com/angular/angular
|
[github]: https://github.com/angular/angular
|
||||||
[gitter]: https://gitter.im/angular/angular
|
[gitter]: https://gitter.im/angular/angular
|
||||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
||||||
[jsfiddle]: http://jsfiddle.net/
|
[jsfiddle]: http://jsfiddle.net
|
||||||
[plunker]: http://plnkr.co/edit
|
[plunker]: http://plnkr.co/edit
|
||||||
[runnable]: http://runnable.com/
|
[runnable]: http://runnable.com
|
||||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
||||||
|
370
DEVELOPER.md
370
DEVELOPER.md
@ -1,19 +1,13 @@
|
|||||||
# Building and Testing Angular 2 for JS and Dart
|
# Building and Testing Angular
|
||||||
|
|
||||||
This document describes how to set up your development environment to build and test Angular, both
|
This document describes how to set up your development environment to build and test Angular.
|
||||||
JS and Dart versions. It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||||
|
|
||||||
* [Prerequisite Software](#prerequisite-software)
|
* [Prerequisite Software](#prerequisite-software)
|
||||||
* [Getting the Sources](#getting-the-sources)
|
* [Getting the Sources](#getting-the-sources)
|
||||||
* [Environment Variable Setup](#environment-variable-setup)
|
* [Installing NPM Modules](#installing-npm-modules)
|
||||||
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
* [Building](#building)
|
||||||
* [Build commands](#build-commands)
|
|
||||||
* [Running Tests Locally](#running-tests-locally)
|
* [Running Tests Locally](#running-tests-locally)
|
||||||
* [Code Style](#code-style)
|
|
||||||
* [Project Information](#project-information)
|
|
||||||
* [CI using Travis](#ci-using-travis)
|
|
||||||
* [Transforming Dart code](#transforming-dart-code)
|
|
||||||
* [Debugging](#debugging)
|
|
||||||
|
|
||||||
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||||
if you'd like to contribute to Angular.
|
if you'd like to contribute to Angular.
|
||||||
@ -32,17 +26,8 @@ following products on your development machine:
|
|||||||
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
|
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||||
source or as a pre-packaged bundle.
|
source or as a pre-packaged bundle.
|
||||||
|
|
||||||
* *Optional*: [Dart](https://www.dartlang.org) (version ` >=1.13.2 <2.0.0`), specifically the Dart-SDK and
|
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||||
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
to execute the selenium standalone server for e2e testing.
|
||||||
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
|
||||||
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
|
||||||
download [page for instructions](https://www.dartlang.org/tools/download.html).
|
|
||||||
You can also download both **stable** and **dev** channel versions from the [download
|
|
||||||
archive](https://www.dartlang.org/tools/download-archive). In that case, on Windows, Dart must be added
|
|
||||||
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
|
|
||||||
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Getting the Sources
|
## Getting the Sources
|
||||||
|
|
||||||
@ -65,44 +50,9 @@ cd angular
|
|||||||
# Add the main Angular repository as an upstream remote to your repository:
|
# Add the main Angular repository as an upstream remote to your repository:
|
||||||
git remote add upstream https://github.com/angular/angular.git
|
git remote add upstream https://github.com/angular/angular.git
|
||||||
```
|
```
|
||||||
|
## Installing NPM Modules
|
||||||
|
|
||||||
## Environment Variable Setup
|
Next, install the JavaScript modules needed to build and test Angular:
|
||||||
|
|
||||||
Define the environment variables listed below. These are mainly needed for the testing. The
|
|
||||||
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
|
|
||||||
your favorite shell.
|
|
||||||
|
|
||||||
Examples given below of possible values for initializing the environment variables assume **Mac OS
|
|
||||||
X** and that you have installed the Dart Editor in the directory named by
|
|
||||||
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
|
|
||||||
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
|
|
||||||
[here](https://www.dartlang.org/tools/pub/installing.html)):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# DART_SDK: path to a Dart SDK directory
|
|
||||||
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
|
|
||||||
|
|
||||||
# Update PATH to include the Dart SDK bin directory
|
|
||||||
PATH+=":$DART_SDK/bin"
|
|
||||||
```
|
|
||||||
|
|
||||||
And specify where the pub’s dependencies are downloaded. By default, this directory is located under .pub_cache
|
|
||||||
in your home directory (on Mac and Linux), or in AppData\Roaming\Pub\Cache (on Windows).
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# PUB_CACHE: location of pub dependencies
|
|
||||||
export PUB_CACHE="/Users/<user>/.pub-cache"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installing NPM Modules and Dart Packages
|
|
||||||
|
|
||||||
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Install Angular project dependencies (package.json)
|
# Install Angular project dependencies (package.json)
|
||||||
@ -121,242 +71,120 @@ particular `gulp` and `protractor` commands. If you prefer, you can drop this pa
|
|||||||
Since global installs can become stale, and required versions can vary by project, we avoid their
|
Since global installs can become stale, and required versions can vary by project, we avoid their
|
||||||
use in these instructions.
|
use in these instructions.
|
||||||
|
|
||||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
*Option 2*: globally installing the package `npm-run` by running `npm install -g npm-run`
|
||||||
|
(you might need to prefix this command with `sudo`). You will then be able to run locally installed
|
||||||
|
package scripts by invoking: e.g., `npm-run gulp build`
|
||||||
|
(see [npm-run project page](https://github.com/timoxley/npm-run) for more details).
|
||||||
|
|
||||||
|
|
||||||
|
*Option 3*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||||
|
|
||||||
## Build commands
|
## Installing Bower Modules
|
||||||
|
|
||||||
To build Angular and prepare tests, run:
|
Now run `bower` to install additional dependencies:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$(npm bin)/gulp build
|
# Install other Angular project dependencies (bower.json)
|
||||||
|
bower install
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
## Windows only
|
||||||
* Results are put in the `dist` folder.
|
|
||||||
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
|
|
||||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`.
|
|
||||||
|
|
||||||
You can selectively build either the JS or Dart versions as follows:
|
In order to create the right symlinks, run **as administrator**:
|
||||||
|
```shell
|
||||||
|
./scripts/windows/create-symlinks.sh
|
||||||
|
```
|
||||||
|
|
||||||
* `$(npm bin)/gulp build.js`
|
Before submitting a PR, do not forget to remove them:
|
||||||
* `$(npm bin)/gulp build.dart`
|
```shell
|
||||||
|
./scripts/windows/remove-symlinks.sh
|
||||||
|
```
|
||||||
|
|
||||||
To clean out the `dist` folder, run:
|
## Building
|
||||||
|
|
||||||
|
To build Angular run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$(npm bin)/gulp clean
|
./build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Results are put in the dist folder.
|
||||||
|
|
||||||
## Running Tests Locally
|
## Running Tests Locally
|
||||||
|
|
||||||
### Full test suite
|
To run tests:
|
||||||
|
|
||||||
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests
|
|
||||||
that run on Travis.
|
|
||||||
|
|
||||||
You can selectively run either the JS or Dart versions as follows:
|
|
||||||
|
|
||||||
* `$(npm bin)/gulp test.all.js`
|
|
||||||
* `$(npm bin)/gulp test.all.dart`
|
|
||||||
|
|
||||||
### Unit tests
|
|
||||||
|
|
||||||
You can run just the unit tests as follows:
|
|
||||||
|
|
||||||
* `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e.
|
|
||||||
watches the test files for changes and re-runs tests when files are updated).
|
|
||||||
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
|
|
||||||
* `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
|
||||||
|
|
||||||
If you prefer running tests in "single-run" mode rather than watch mode use:
|
|
||||||
|
|
||||||
* `$(npm bin)/gulp test.unit.js/ci`
|
|
||||||
* `$(npm bin)/gulp test.unit.cjs/ci`
|
|
||||||
* `$(npm bin)/gulp test.unit.dart/ci`
|
|
||||||
|
|
||||||
The task updates the dist folder with transpiled code whenever a source or test file changes, and
|
|
||||||
Karma is run against the new output.
|
|
||||||
|
|
||||||
**Note**: If you want to only run a single test you can alter the test you wish to run by changing
|
|
||||||
`it` to `iit` or `describe` to `ddescribe`. This will only run that individual test and make it
|
|
||||||
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
|
|
||||||
tests respectively.
|
|
||||||
|
|
||||||
**Note**: **watch mode** needs symlinks to work, so if you're using windows, ensure you have the
|
|
||||||
rights to built them in your operating system.
|
|
||||||
|
|
||||||
### Unit tests with Sauce Labs or Browser Stack
|
|
||||||
|
|
||||||
First, in a terminal, create a tunnel with [Sauce Connect](https://docs.saucelabs.com/reference/sauce-connect/) or [Browser Stack Local](https://www.browserstack.com/local-testing#command-line), and valid credentials.
|
|
||||||
|
|
||||||
Then, in another terminal:
|
|
||||||
- Define the credentials as environment variables, e.g.:
|
|
||||||
```
|
|
||||||
export SAUCE_USERNAME='my_user'; export SAUCE_ACCESS_KEY='my_key';
|
|
||||||
export BROWSER_STACK_USERNAME='my_user'; export BROWSER_STACK_ACCESS_KEY='my_key';
|
|
||||||
```
|
|
||||||
- Then run `gulp test.unit.js.(sauce|browserstack) --browsers=option1,option2,..,optionN`
|
|
||||||
The options are any mix of browsers and aliases which are defined in the [browser-providers.conf.js](https://github.com/angular/angular/blob/master/browser-providers.conf.js) file.
|
|
||||||
They are case insensitive, and the `SL_` or `BS_` prefix must not be added for browsers.
|
|
||||||
|
|
||||||
Some examples of commands:
|
|
||||||
```
|
|
||||||
gulp test.unit.js.sauce --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
|
||||||
gulp test.unit.js.browserstack --browsers=Safari,IE //run in Browser Stack with Safari 7, Safari 8, Safari 9, IE 9, IE 10 and IE 11
|
|
||||||
gulp test.unit.js.sauce --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
|
||||||
```
|
|
||||||
|
|
||||||
### E2E tests
|
|
||||||
|
|
||||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
|
||||||
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver).
|
|
||||||
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
|
|
||||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js e2e tests.
|
|
||||||
|
|
||||||
Angular specific command line options when running protractor:
|
|
||||||
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
|
||||||
|
|
||||||
### Performance tests
|
|
||||||
|
|
||||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
|
||||||
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver)
|
|
||||||
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
|
|
||||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: dart2js performance tests
|
|
||||||
|
|
||||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
|
||||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
|
||||||
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
### Formatting with <a name="clang-format">clang-format</a>
|
|
||||||
|
|
||||||
We use [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to automatically enforce code
|
|
||||||
style for our TypeScript code. This allows us to focus our code reviews more on the content, and
|
|
||||||
less on style nit-picking. It also lets us encode our style guide in the `.clang-format` file in the
|
|
||||||
repository, allowing many tools and editors to share our settings.
|
|
||||||
|
|
||||||
To check the formatting of your code, run
|
|
||||||
|
|
||||||
gulp check-format
|
|
||||||
|
|
||||||
Note that the continuous build on Travis runs `gulp enforce-format`. Unlike the `check-format` task,
|
|
||||||
this will actually fail the build if files aren't formatted according to the style guide.
|
|
||||||
|
|
||||||
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
|
|
||||||
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
|
|
||||||
to some whitespace difference.
|
|
||||||
|
|
||||||
* Use `$(npm bin)/clang-format -i [file name]` to format a file (or multiple).
|
|
||||||
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives
|
|
||||||
you a command line to format your code.
|
|
||||||
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
|
|
||||||
touched.
|
|
||||||
* You can run this as a **git pre-commit hook** to automatically format your delta regions when you
|
|
||||||
commit a change. In the angular repo, run
|
|
||||||
|
|
||||||
```
|
|
||||||
$ echo -e '#!/bin/sh\nexec git clang-format' > .git/hooks/pre-commit
|
|
||||||
$ chmod u+x !$
|
|
||||||
```
|
|
||||||
|
|
||||||
* **WebStorm** can run clang-format on the current file.
|
|
||||||
1. Under Preferences, open Tools > External Tools.
|
|
||||||
1. Plus icon to Create Tool
|
|
||||||
1. Fill in the form:
|
|
||||||
- Name: clang-format
|
|
||||||
- Description: Format
|
|
||||||
- Synchronize files after execution: checked
|
|
||||||
- Open console: not checked
|
|
||||||
- Show in: Editor menu
|
|
||||||
- Program: `$ProjectFileDir$/node_modules/.bin/clang-format`
|
|
||||||
- Parameters: `-i -style=file $FilePath$`
|
|
||||||
- Working directory: `$ProjectFileDir$`
|
|
||||||
* `clang-format` integrations are also available for many popular editors (`vim`, `emacs`,
|
|
||||||
`Sublime Text`, etc.).
|
|
||||||
|
|
||||||
### Linting
|
|
||||||
|
|
||||||
We use [tslint](https://github.com/palantir/tslint) for linting. See linting rules in [gulpfile](gulpfile.js). To lint, run
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
$ ./test.sh node # Run all angular tests on node
|
||||||
|
|
||||||
|
$ ./test.sh browser # Run all angular tests in browser
|
||||||
|
$ ./test.sh browserNoRouter # Optionally run all angular tests without router in browser
|
||||||
|
|
||||||
|
$ ./test.sh tools # Run angular tooling (not framework) tests
|
||||||
|
```
|
||||||
|
|
||||||
|
You should execute the 3 test suites before submitting a PR to github.
|
||||||
|
|
||||||
|
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||||
|
|
||||||
|
- CircleCI fails if your code is not formatted properly,
|
||||||
|
- Travis CI fails if any of the test suites described above fails.
|
||||||
|
|
||||||
|
## Update the public API tests
|
||||||
|
|
||||||
|
If you happen to modify the public API of Angular, API golden files must be updated using:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ gulp public-api:update
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: The command `gulp public-api:enforce` fails when the API doesn't match the golden files. Make sure to rebuild
|
||||||
|
the project before trying to verify after an API change.
|
||||||
|
|
||||||
|
## <a name="clang-format"></a> Formatting your source code
|
||||||
|
|
||||||
|
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||||
|
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||||
|
|
||||||
|
You can automatically format your code by running:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ gulp format
|
||||||
|
```
|
||||||
|
|
||||||
|
## Linting/verifying your source code
|
||||||
|
|
||||||
|
You can check that your code is properly formatted and adheres to coding style by running:
|
||||||
|
|
||||||
|
``` shell
|
||||||
$ gulp lint
|
$ gulp lint
|
||||||
```
|
```
|
||||||
|
|
||||||
## Generating the API documentation
|
## Publishing snapshot builds
|
||||||
|
|
||||||
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
|
When the `master` branch successfully builds on Travis, it automatically publishes build artifacts
|
||||||
|
to repositories in the Angular org, eg. the `@angular/core` package is published to
|
||||||
|
http://github.com/angular/core-builds.
|
||||||
|
The ES2015 version of Angular is published to a different branch in these repos, for example
|
||||||
|
http://github.com/angular/core-builds#master-es2015
|
||||||
|
|
||||||
```shell
|
You may find that your un-merged change needs some validation from external participants.
|
||||||
$(npm bin)/gulp docs/angular.io
|
Rather than requiring them to pull your Pull Request and build Angular locally, you can
|
||||||
|
publish the `*-builds` snapshots just like our Travis build does.
|
||||||
|
|
||||||
|
First time, you need to create the github repositories:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ export TOKEN=[get one from https://github.com/settings/tokens]
|
||||||
|
$ CREATE_REPOS=1 ./scripts/publish/publish-build-artifacts.sh [github username]
|
||||||
```
|
```
|
||||||
|
|
||||||
You can serve the generated documentation to check how it would render on [angular.io](https://angular.io/):
|
For subsequent snapshots, just run
|
||||||
- check out the [angular.io repo](https://github.com/angular/angular.io) locally,
|
|
||||||
- install dependencies as described in the [angular.io README](https://github.com/angular/angular.io/blob/master/README.md),
|
|
||||||
- copy the generated documentation from your local angular repo at `angular/dist/angular.io/partials/api/angular2` to your local angular.io repo at `angular.io/public/docs/js/latest/api`,
|
|
||||||
- run `harp compile` at the root of the angular.io repo to check the generated documentation for errors,
|
|
||||||
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
|
|
||||||
|
|
||||||
## Project Information
|
``` shell
|
||||||
|
$ ./scripts/publish/publish-build-artifacts.sh [github username]
|
||||||
|
```
|
||||||
|
|
||||||
### Folder structure
|
The script will publish the build snapshot to a branch with the same name as your current branch,
|
||||||
|
and create it if it doesn't exist.
|
||||||
* `modules/*`: modules that will be loaded in the browser
|
|
||||||
* `tools/*`: tools that are needed to build Angular
|
|
||||||
* `dist/*`: build files are placed here.
|
|
||||||
|
|
||||||
### File suffixes
|
|
||||||
|
|
||||||
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
|
|
||||||
* `*.dart`: Dart files that don't get transpiled
|
|
||||||
|
|
||||||
## CI using Travis
|
|
||||||
|
|
||||||
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
|
||||||
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
|
||||||
|
|
||||||
## Transforming Dart code
|
|
||||||
|
|
||||||
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
|
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
### Debug the transpiler
|
|
||||||
|
|
||||||
If you need to debug the transpiler:
|
|
||||||
|
|
||||||
- add a `debugger;` statement in the transpiler code,
|
|
||||||
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
|
|
||||||
debugger
|
|
||||||
- press "c" to execute the program until you reach the `debugger;` statement,
|
|
||||||
- you can then type "repl" to enter the REPL and inspect variables in the context.
|
|
||||||
|
|
||||||
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
|
|
||||||
code you want to debug (the former will process the "modules" folder while the later processes
|
|
||||||
the transpiler specs).
|
|
||||||
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
|
|
||||||
the developer tools are opened in the browser running Karma.
|
|
||||||
|
|
||||||
### Debug the tests
|
|
||||||
|
|
||||||
If you need to debug the tests:
|
|
||||||
|
|
||||||
- add a `debugger;` statement to the test you want to debug (or the source code),
|
|
||||||
- execute karma `$(npm bin)/gulp test.js`,
|
|
||||||
- press the top right "DEBUG" button,
|
|
||||||
- open the DevTools and press F5,
|
|
||||||
- the execution halts at the `debugger;` statement
|
|
||||||
|
|
||||||
**Note (WebStorm users)**:
|
|
||||||
|
|
||||||
1. Create a Karma run config from WebStorm.
|
|
||||||
2. Then in the "Run" menu, press "Debug 'karma-js.conf.js'", and WebStorm will stop in the generated
|
|
||||||
code on the `debugger;` statement.
|
|
||||||
3. You can then step into the code and add watches.
|
|
||||||
|
|
||||||
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
|
|
||||||
the original source files are not supported at the moment.
|
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2014-2016 Google, Inc. http://angular.io
|
Copyright (c) 2014-2017 Google, Inc. http://angular.io
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Naming Conventions in Angular2
|
Naming Conventions in Angular
|
||||||
---
|
---
|
||||||
|
|
||||||
In general Angular2 should follow TypeScript naming conventions.
|
In general Angular should follow TypeScript naming conventions.
|
||||||
See: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
|
See: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
|
||||||
|
|
||||||
|
|
||||||
|
21
README.md
21
README.md
@ -1,19 +1,18 @@
|
|||||||
[](https://travis-ci.org/angular/angular)
|
[](https://travis-ci.org/angular/angular)
|
||||||
|
[](https://circleci.com/gh/angular/angular/tree/master)
|
||||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[](http://issuestats.com/github/angular/angular)
|
[](http://issuestats.com/github/angular/angular)
|
||||||
[](http://issuestats.com/github/angular/angular)
|
[](http://issuestats.com/github/angular/angular)
|
||||||
[](http://badge.fury.io/js/angular2)
|
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||||
[](https://npmjs.org/package/angular2)
|
|
||||||
|
|
||||||
[](https://saucelabs.com/u/angular2-ci)
|
[](https://saucelabs.com/u/angular2-ci)
|
||||||
|
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||||
|
|
||||||
Angular
|
Angular
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript (JS) and other languages.
|
||||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
|
||||||
|
|
||||||
Angular 2 is currently in **Beta**.
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
@ -25,11 +24,7 @@ Angular 2 is currently in **Beta**.
|
|||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
|
[browserstack]: https://www.browserstack.com/
|
||||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
[dart]: http://www.dartlang.org
|
|
||||||
[dartium]: http://www.dartlang.org/tools/dartium
|
|
||||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||||
[ng2]: http://angular.io
|
[ng]: http://angular.io
|
||||||
[ngDart]: http://angulardart.org
|
|
||||||
[ngJS]: http://angularjs.org
|
|
||||||
|
62
SAVED_REPLIES.md
Normal file
62
SAVED_REPLIES.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Saved Responses for Angular's Issue Tracker
|
||||||
|
|
||||||
|
The following are canned responses that the Angular team should use to close issues on our issue tracker that fall into the listed resolution categories.
|
||||||
|
|
||||||
|
Since GitHub currently doesn't allow us to have a repository-wide or organization-wide list of [saved replies](https://help.github.com/articles/working-with-saved-replies/), these replies need to be maintained by individual team members. Since the responses can be modified in the future, all responses are versioned to simplify the process of keeping the responses up to date.
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Already Fixed (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. Luckily it has already been fixed in one of the recent releases. Please update to the most recent version to resolve the problem.
|
||||||
|
|
||||||
|
If after upgrade the problem still exists in your application please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular: Don't Understand (v1)
|
||||||
|
```
|
||||||
|
I'm sorry but we don't understand the problem you are reporting.
|
||||||
|
|
||||||
|
If the problem still exists please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Duplicate (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. However this issue is a duplicate of an existing issue #<ISSUE_NUMBER>. Please subscribe to that issue for future updates.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Insufficient Information Provided (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information.
|
||||||
|
|
||||||
|
If the problem still persists, please file a new issue and ensure you provide all of the required information when filling out the issue template.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular: Issue Outside of Angular (v1)
|
||||||
|
```
|
||||||
|
I'm sorry but this issue is not caused by Angular. Please contact the author(s) of project <PROJECT NAME> or file issue on their issue tracker.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Non-reproducible (v1)
|
||||||
|
```
|
||||||
|
I'm sorry but we can't reproduce the problem following the instructions you provided.
|
||||||
|
|
||||||
|
If the problem still exists please open a new issue following [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue).
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular: Obsolete (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. This issue is now obsolete due to changes in the recent releases. Please update to the most recent Angular version.
|
||||||
|
|
||||||
|
If the problem still persists, please file a new issue and ensure you provide the version of Angular affected and include the steps to reproduce the problem when filling out the issue template.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Support Request (v1)
|
||||||
|
```
|
||||||
|
Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](http://stackoverflow.com/) using tag `angular`.
|
||||||
|
|
||||||
|
If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-got-a-question-or-problem).
|
||||||
|
```
|
142
TOOLS.md
142
TOOLS.md
@ -1,4 +1,140 @@
|
|||||||
# Developer Tools for Angular 2
|
# Developer Tools for Angular
|
||||||
|
|
||||||
- [JavaScript](TOOLS_JS.md)
|
Here you will find a collection of tools and tips for keeping your application
|
||||||
- [Dart](TOOLS_DART.md)
|
perform well and contain fewer bugs.
|
||||||
|
|
||||||
|
## Angular debug tools in the dev console
|
||||||
|
|
||||||
|
Angular provides a set of debug tools that are accessible from any browser's
|
||||||
|
developer console. In Chrome the dev console can be accessed by pressing
|
||||||
|
Ctrl + Shift + j.
|
||||||
|
|
||||||
|
### Enabling debug tools
|
||||||
|
|
||||||
|
By default the debug tools are disabled. You can enable debug tools as follows:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {enableDebugTools} from '@angular/platform-browser';
|
||||||
|
|
||||||
|
bootstrap(Application).then((appRef) => {
|
||||||
|
enableDebugTools(appRef);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using debug tools
|
||||||
|
|
||||||
|
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
|
||||||
|
top level object is called `ng` and contains more specific tools inside it.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
ng.profiler.timeChangeDetection();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
### Change detection profiler
|
||||||
|
|
||||||
|
If your application is janky (it misses frames) or is slow according to other
|
||||||
|
metrics it is important to find the root cause of the issue. Change detection
|
||||||
|
is a phase in Angular's lifecycle that detects changes in values that are
|
||||||
|
bound to UI, and if it finds a change it performs the corresponding UI update.
|
||||||
|
However, sometimes it is hard to tell if the slowness is due to the act of
|
||||||
|
computing the changes being slow, or due to the act of applying those changes
|
||||||
|
to the UI. For your application to be performant it is important that the
|
||||||
|
process of computing changes is very fast. For best results it should be under
|
||||||
|
3 milliseconds in order to leave room for the application logic, the UI updates
|
||||||
|
and browser's rendering pipeline to fit withing the 16 millisecond frame
|
||||||
|
(assuming the 60 FPS target frame rate).
|
||||||
|
|
||||||
|
Change detection profiler repeatedly performs change detection without invoking
|
||||||
|
any user actions, such as clicking buttons or entering text in input fields. It
|
||||||
|
then computes the average amount of time it took to perform a single cycle of
|
||||||
|
change detection in milliseconds and prints it to the console. This number
|
||||||
|
depends on the current state of the UI. You will likely see different numbers
|
||||||
|
as you go from one screen in your application to another.
|
||||||
|
|
||||||
|
#### Running the profiler
|
||||||
|
|
||||||
|
Enable debug tools (see above), then in the dev console enter the following:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
ng.profiler.timeChangeDetection();
|
||||||
|
```
|
||||||
|
|
||||||
|
The results will be printed to the console.
|
||||||
|
|
||||||
|
#### Recording CPU profile
|
||||||
|
|
||||||
|
Pass `{record: true}` an argument:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
ng.profiler.timeChangeDetection({record: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open the "Profiles" tab. You will see the recorded profile titled
|
||||||
|
"Change Detection". In Chrome, if you record the profile repeatedly, all the
|
||||||
|
profiles will be nested under "Change Detection".
|
||||||
|
|
||||||
|
#### Interpreting the numbers
|
||||||
|
|
||||||
|
In a properly-designed application repeated attempts to detect changes without
|
||||||
|
any user actions should result in no changes to be applied on the UI. It is
|
||||||
|
also desirable to have the cost of a user action be proportional to the amount
|
||||||
|
of UI changes required. For example, popping up a menu with 5 items should be
|
||||||
|
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
||||||
|
change detection with no UI updates should be as fast as possible. Ideally the
|
||||||
|
number printed by the profiler should be well below the length of a single
|
||||||
|
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
|
||||||
|
|
||||||
|
#### Investigating slow change detection
|
||||||
|
|
||||||
|
So you found a screen in your application on which the profiler reports a very
|
||||||
|
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
||||||
|
recording while profiling:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
ng.profiler.timeChangeDetection({record: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
Then look for hot spots using
|
||||||
|
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
||||||
|
|
||||||
|
#### Reducing change detection cost
|
||||||
|
|
||||||
|
There are many reasons for slow change detection. To gain intuition about
|
||||||
|
possible causes it would help to understand how change detection works. Such a
|
||||||
|
discussion is outside the scope of this document (TODO link to docs), but here
|
||||||
|
are some key concepts in brief.
|
||||||
|
|
||||||
|
By default Angular uses "dirty checking" mechanism for finding model changes.
|
||||||
|
This mechanism involves evaluating every bound expression that's active on the
|
||||||
|
UI. These usually include text interpolation via `{{expression}}` and property
|
||||||
|
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
||||||
|
costly to compute they could contribute to slow change detection. A good way to
|
||||||
|
speed things up is to use plain class fields in your expressions and avoid any
|
||||||
|
kinds of computation. Example:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@Component({
|
||||||
|
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||||
|
})
|
||||||
|
class FancyButton {
|
||||||
|
// GOOD: no computation, just return the value
|
||||||
|
isEnabled: boolean;
|
||||||
|
|
||||||
|
// BAD: computes the final value upon request
|
||||||
|
_title: String;
|
||||||
|
get title(): String { return this._title.trim().toUpperCase(); }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Most cases like these could be solved by precomputing the value and storing the
|
||||||
|
final value in a field.
|
||||||
|
|
||||||
|
Angular also supports a second type of change detection - the "push" model. In
|
||||||
|
this model Angular does not poll your component for changes. Instead, the
|
||||||
|
component "tells" Angular when it changes and only then does Angular perform
|
||||||
|
the update. This model is suitable in situations when your data model uses
|
||||||
|
observable or immutable objects (also a discussion for another time).
|
||||||
|
376
TOOLS_DART.md
376
TOOLS_DART.md
@ -1,376 +0,0 @@
|
|||||||
# Developer Tools for Dart
|
|
||||||
|
|
||||||
Use these tools and techniques to increase your app's performance
|
|
||||||
and reliability.
|
|
||||||
|
|
||||||
* [Angular debugging tools](#angular-debugging-tools)
|
|
||||||
* [Code size](#code-size)
|
|
||||||
* [Performance](#performance)
|
|
||||||
|
|
||||||
|
|
||||||
## Angular debugging tools
|
|
||||||
|
|
||||||
Starting with alpha.38, Angular provides a set of debugging tools
|
|
||||||
that are accessible from any browser's developer console.
|
|
||||||
In Chrome, you can get to the dev console by pressing
|
|
||||||
Ctrl + Shift + J (on Mac: Cmd + Opt + J).
|
|
||||||
|
|
||||||
### Enabling the debugging tools
|
|
||||||
|
|
||||||
By default the debugging tools are disabled.
|
|
||||||
Enable the debugging tools as follows:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:angular2/platform/browser.dart';
|
|
||||||
|
|
||||||
main() async {
|
|
||||||
var appRef = await bootstrap(Application);
|
|
||||||
enableDebugTools(appRef);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- Change function name to enableDebuggingTools? -->
|
|
||||||
|
|
||||||
|
|
||||||
### Using the debugging tools
|
|
||||||
|
|
||||||
In the browser, open the dev console. The top-level object is called `ng` and
|
|
||||||
contains more specific tools inside it.
|
|
||||||
|
|
||||||
For example, to run the change detection profiler on your app:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// In the dev console:
|
|
||||||
ng.profiler.timeChangeDetection();
|
|
||||||
```
|
|
||||||
|
|
||||||
The [Change detection profiler](#change-detection-profiler) section
|
|
||||||
has more details.
|
|
||||||
<!-- Point to API docs when they're published, if they're useful.
|
|
||||||
They should be under
|
|
||||||
http://www.dartdocs.org/documentation/angular2/latest
|
|
||||||
and/or
|
|
||||||
https://angular.io/docs/js/latest/api/. -->
|
|
||||||
|
|
||||||
|
|
||||||
## Code size
|
|
||||||
|
|
||||||
Code must be downloaded, parsed, and executed. Too much code can lead to
|
|
||||||
slow application start-up time, especially on slow networks and low-end devices.
|
|
||||||
The tools and techniques in this section can help you to identify
|
|
||||||
unnecessarily large code and to reduce code size.
|
|
||||||
|
|
||||||
### Finding contributors to code size
|
|
||||||
|
|
||||||
Options for investigating code size include the `--dump-info` dart2js option,
|
|
||||||
ng2soyc, `reflector.trackUsage()`, and code coverage information
|
|
||||||
from the Dart VM.
|
|
||||||
|
|
||||||
#### Use --dump-info
|
|
||||||
|
|
||||||
The `--dump-info` option of `dart2js` outputs information about what happened
|
|
||||||
during compilation. You can specify `--dump-info` in `pubspec.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
transformers:
|
|
||||||
...
|
|
||||||
- $dart2js:
|
|
||||||
commandLineOptions:
|
|
||||||
- --dump-info
|
|
||||||
```
|
|
||||||
|
|
||||||
The [Dump Info Visualizer](https://github.com/dart-lang/dump-info-visualizer)
|
|
||||||
can help you analyze the output.
|
|
||||||
For more information, see the
|
|
||||||
[dart2js_info API reference](http://dart-lang.github.io/dart2js_info/doc/api/).
|
|
||||||
|
|
||||||
#### Use ng2soyc.dart
|
|
||||||
|
|
||||||
[ng2soyc](https://github.com/angular/ng2soyc.dart) is a utility for analyzing
|
|
||||||
code size contributors in Angular 2 applications. It groups code size by
|
|
||||||
library and, assuming your library names follow
|
|
||||||
[standard naming conventions](https://www.dartlang.org/articles/style-guide/#do-prefix-library-names-with-the-package-name-and-a-dot-separated-path)
|
|
||||||
(package.library.sublibrary...), gives the code size breakdown at
|
|
||||||
each level. To reduce noise in the output of very large apps, ng2soyc provides
|
|
||||||
an option to hide libraries that are too small, so you can focus on the biggest
|
|
||||||
contributors.
|
|
||||||
|
|
||||||
#### Find unused reflection data
|
|
||||||
|
|
||||||
Your app might have types that are annotated with `@Component` or `@Injectable`
|
|
||||||
but never used.
|
|
||||||
To find these unused types, use `reflector.trackUsage()` and then,
|
|
||||||
after exercising your app, `reflector.listUnusedKeys()`.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
|
||||||
...
|
|
||||||
main() async {
|
|
||||||
reflector.trackUsage();
|
|
||||||
await bootstrap(AppComponent);
|
|
||||||
print('Unused keys: ${reflector.listUnusedKeys()}');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When you run that code (in Dartium or another browser),
|
|
||||||
you'll see a list of types that Angular _can_ inject but hasn't needed to.
|
|
||||||
Consider removing those types or their `@Component`/`@Injectable` annotation
|
|
||||||
to decrease your app's code size.
|
|
||||||
|
|
||||||
Three conditions must be true for `listUnusedKeys()` to return helpful data:
|
|
||||||
|
|
||||||
1. The angular2 transformer must run on the app.
|
|
||||||
2. If you're running a JavaScript version of the app,
|
|
||||||
the app must not be minified, so that the names are readable.
|
|
||||||
3. You must exercise your app in as many ways as possible
|
|
||||||
before calling `listUnusedKeys()`.
|
|
||||||
Otherwise, you might get false positives:
|
|
||||||
keys that haven't been used only because you didn't exercise
|
|
||||||
the relevant feature of the app.
|
|
||||||
|
|
||||||
To run the angular2 transformer, first specify it in `pubspec.yaml`:
|
|
||||||
|
|
||||||
```
|
|
||||||
name: hello_world
|
|
||||||
...
|
|
||||||
transformers:
|
|
||||||
- angular2:
|
|
||||||
entry_points: web/main.dart
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use pub to run the transformer. If you use `pub serve`,
|
|
||||||
it provides both Dart and unminified (by default) JavaScript versions.
|
|
||||||
If you want to serve actual files, then use `pub build` in debug mode
|
|
||||||
to generate Dart and unminified JavaScript files:
|
|
||||||
`pub build --mode=debug`.
|
|
||||||
|
|
||||||
The `reflector.trackUsage()` method makes Angular track the reflection
|
|
||||||
information used by the app. Reflection information (`ReflectionInfo`) is a data
|
|
||||||
structure that stores information that Angular uses for locating DI factories
|
|
||||||
and for generating change detectors and other code related to a
|
|
||||||
given type.
|
|
||||||
|
|
||||||
#### Use code coverage to find dead code
|
|
||||||
|
|
||||||
When running in Dartium (or in the Dart VM, in general) you can request code
|
|
||||||
coverage information from the VM. You can either use
|
|
||||||
[observatory](https://www.dartlang.org/tools/observatory/) or download
|
|
||||||
the coverage file and use your own tools to inspect it. Lines of code that are
|
|
||||||
not covered are top candidates for dead code.
|
|
||||||
|
|
||||||
Keep in mind, however, that uncovered code is not sufficient evidence of dead
|
|
||||||
code, only necessary evidence. It is perfectly possible that you simply didn't
|
|
||||||
exercise your application in a way that triggers the execution of uncovered
|
|
||||||
code. A common example is error handling code. Just because your testing never
|
|
||||||
encountered an error does not mean the error won't happen in production. You
|
|
||||||
therefore don't have to rush and remove all the `catch` blocks.
|
|
||||||
|
|
||||||
### Reducing code size
|
|
||||||
|
|
||||||
To reduce code size, you can disable reflection,
|
|
||||||
enable minification, and manually remove dead code.
|
|
||||||
You can also try less safe options such as
|
|
||||||
telling dart2js to trust type annotations.
|
|
||||||
|
|
||||||
|
|
||||||
#### Disable reflection
|
|
||||||
|
|
||||||
`dart:mirrors` allows discovering program metadata at runtime. However, this
|
|
||||||
means that `dart2js` needs to retain that metadata and thus increase the size
|
|
||||||
of resulting JS output. In practice, however, it is possible to extract most
|
|
||||||
metadata necessary for your metaprogramming tasks statically using a
|
|
||||||
transformer and `package:analyzer`, and act on it before compiling to JS.
|
|
||||||
|
|
||||||
#### Enable minification
|
|
||||||
|
|
||||||
Minification shortens all your `longMethodNames` into 2- or 3-letter long
|
|
||||||
symbols. `dart2js` ensures that this kind of renaming is done safely, without
|
|
||||||
breaking the functionality of your programs. You can enable it in `pubspec.yaml`
|
|
||||||
under `$dart2js` transformer:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
transformers:
|
|
||||||
...
|
|
||||||
- $dart2js:
|
|
||||||
minify: true
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Manually remove dead code
|
|
||||||
|
|
||||||
`dart2js` comes with dead code elimination out-of-the-box. However, it may not
|
|
||||||
always be able to tell if a piece of code could be used. Consider the following
|
|
||||||
example:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
/// This function decides which serialization format to use
|
|
||||||
void setupSerializers() {
|
|
||||||
if (server.doYouSupportProtocolBuffers()) {
|
|
||||||
useProtobufSerializers();
|
|
||||||
} else {
|
|
||||||
useJsonSerializers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example the application asks the server what kind of serialization
|
|
||||||
format it uses and dynamically chooses one or the other. `dart2js` can't
|
|
||||||
tell whether the server responds with yes or no, so it must retain both
|
|
||||||
kinds of serializers. However, if you know that your server supports
|
|
||||||
protocol buffers, you can remove that `if` block entirely and default to
|
|
||||||
protocol buffers.
|
|
||||||
|
|
||||||
Code coverage (see above) is a good way to find dead code in your app.
|
|
||||||
|
|
||||||
#### Unsafe options
|
|
||||||
|
|
||||||
Dart also provides more aggressive optimization options. However, you have to
|
|
||||||
be careful when using them and as of today the benefits aren't that clear. If
|
|
||||||
your type annotations are inaccurate you may end up with non-Darty runtime
|
|
||||||
behavior, including the classic "undefined is not a function" tautology, as
|
|
||||||
well as the "keep on truckin'" behavior, e.g. `null + 1 == 1` and
|
|
||||||
`{} + [] == 0`.
|
|
||||||
|
|
||||||
`--trust-type-annotations` tells `dart2js` to trust that your type annotations
|
|
||||||
are correct. So if you have a function `foo(Bar bar)` the compiler can omit the
|
|
||||||
check that `bar` is truly `Bar` when calling methods on it.
|
|
||||||
|
|
||||||
`--trust-primitives` tells `dart2js` that primitive types, such as numbers and
|
|
||||||
booleans are never `null` when performing arithmetic, and that your program
|
|
||||||
does not run into range error when operating on lists, letting the compiler
|
|
||||||
remove some of the error checking code.
|
|
||||||
|
|
||||||
Specify these options in `pubspec.yaml`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
transformers:
|
|
||||||
...
|
|
||||||
- $dart2js:
|
|
||||||
commandLineOptions:
|
|
||||||
- --trust-type-annotations
|
|
||||||
- --trust-primitives
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
### Change detection profiler
|
|
||||||
|
|
||||||
If your application is janky (it misses frames) or is slow according to other
|
|
||||||
metrics, you need to find out why. This tool helps by measuring the average
|
|
||||||
speed of _change detection_, a phase in Angular's
|
|
||||||
lifecycle that detects changes in values that are bound to the UI.
|
|
||||||
Janky UI updates can result from slowness either in _computing_ the changes or
|
|
||||||
in _applying_ those changes to the UI.
|
|
||||||
|
|
||||||
For your app to be performant, the process of _computing_ changes must be very
|
|
||||||
fast—preferably **under 3 milliseconds**.
|
|
||||||
Fast change computation leaves room for
|
|
||||||
the application logic, UI updates, and browser rendering pipeline
|
|
||||||
to fit within a 16 ms frame (assuming a target frame rate of 60 FPS).
|
|
||||||
|
|
||||||
The change detection profiler repeatedly performs change detection
|
|
||||||
without invoking any user actions, such as clicking buttons or entering
|
|
||||||
text in input fields. It then computes the average amount of time
|
|
||||||
(in milliseconds) to perform a single cycle of change detection and
|
|
||||||
prints that to the console. This number depends on the current state of the UI. You are likely to see different numbers
|
|
||||||
as you go from one screen in your application to another.
|
|
||||||
|
|
||||||
#### Running the profiler
|
|
||||||
|
|
||||||
Before running the profiler, enable the debugging tools
|
|
||||||
and put the app into the state you want to measure:
|
|
||||||
|
|
||||||
1. If you haven't already done so,
|
|
||||||
[enable the debugging tools](#enabling-the-debugging-tools).
|
|
||||||
2. Navigate the app to a screen whose performance you want to profile.
|
|
||||||
3. Make sure the screen is in a state that you want to measure.
|
|
||||||
For example, you might want to profile the screen several times,
|
|
||||||
with different amounts and kinds of data.
|
|
||||||
|
|
||||||
To run the profiler, enter the following in the dev console:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection();
|
|
||||||
```
|
|
||||||
|
|
||||||
The results are visible in the console.
|
|
||||||
|
|
||||||
|
|
||||||
#### Recording CPU profiles
|
|
||||||
|
|
||||||
To record a profile, pass `{record: true}` to `timeChangeDetection()`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection({record: true});
|
|
||||||
```
|
|
||||||
|
|
||||||
Then open the **Profiles** tab. The recorded profile has the title
|
|
||||||
**Change Detection**. In Chrome, if you record the profile repeatedly, all the
|
|
||||||
profiles are nested under Change Detection.
|
|
||||||
|
|
||||||
|
|
||||||
#### Interpreting the numbers
|
|
||||||
|
|
||||||
In a properly designed application, repeated attempts to detect changes without
|
|
||||||
any user actions result in no changes to the UI. It is
|
|
||||||
also desirable to have the cost of a user action be proportional to the amount
|
|
||||||
of UI changes required. For example, popping up a menu with 5 items should be
|
|
||||||
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
|
||||||
change detection with no UI updates should be as fast as possible.
|
|
||||||
|
|
||||||
#### Investigating slow change detection
|
|
||||||
|
|
||||||
So you found a screen in your application on which the profiler reports a very
|
|
||||||
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
|
||||||
recording while profiling:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection({record: true});
|
|
||||||
```
|
|
||||||
|
|
||||||
Then look for hot spots using
|
|
||||||
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
|
||||||
|
|
||||||
#### Reducing change detection cost
|
|
||||||
|
|
||||||
There are many reasons for slow change detection. To gain intuition about
|
|
||||||
possible causes it helps to understand how change detection works. Such a
|
|
||||||
discussion is outside the scope of this document,
|
|
||||||
but here are some key concepts.
|
|
||||||
|
|
||||||
<!-- TODO: link to change detection docs -->
|
|
||||||
|
|
||||||
By default, Angular uses a _dirty checking_ mechanism to find model changes.
|
|
||||||
This mechanism involves evaluating every bound expression that's active on the
|
|
||||||
UI. These usually include text interpolation via `{{expression}}` and property
|
|
||||||
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
|
||||||
costly to compute, they might contribute to slow change detection. A good way to
|
|
||||||
speed things up is to use plain class fields in your expressions and avoid any
|
|
||||||
kind of computation. For example:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
@View(
|
|
||||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
|
||||||
)
|
|
||||||
class FancyButton {
|
|
||||||
// GOOD: no computation, just returns the value
|
|
||||||
bool isEnabled;
|
|
||||||
|
|
||||||
// BAD: computes the final value upon request
|
|
||||||
String _title;
|
|
||||||
String get title => _title.trim().toUpperCase();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Most cases like these can be solved by precomputing the value and storing the
|
|
||||||
final value in a field.
|
|
||||||
|
|
||||||
Angular also supports a second type of change detection: the _push_ model. In
|
|
||||||
this model, Angular does not poll your component for changes. Instead, the
|
|
||||||
component tells Angular when it changes, and only then does Angular perform
|
|
||||||
the update. This model is suitable in situations when your data model uses
|
|
||||||
observable or immutable objects.
|
|
||||||
|
|
||||||
<!-- TODO: link to discussion of push model -->
|
|
140
TOOLS_JS.md
140
TOOLS_JS.md
@ -1,140 +0,0 @@
|
|||||||
# Developer Tools for JavaScript
|
|
||||||
|
|
||||||
Here you will find a collection of tools and tips for keeping your application
|
|
||||||
perform well and contain fewer bugs.
|
|
||||||
|
|
||||||
## Angular debug tools in the dev console
|
|
||||||
|
|
||||||
Angular provides a set of debug tools that are accessible from any browser's
|
|
||||||
developer console. In Chrome the dev console can be accessed by pressing
|
|
||||||
Ctrl + Shift + j.
|
|
||||||
|
|
||||||
### Enabling debug tools
|
|
||||||
|
|
||||||
By default the debug tools are disabled. You can enable debug tools as follows:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {enableDebugTools} from 'angular2/platform/browser';
|
|
||||||
|
|
||||||
bootstrap(Application).then((appRef) => {
|
|
||||||
enableDebugTools(appRef);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using debug tools
|
|
||||||
|
|
||||||
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
|
|
||||||
top level object is called `ng` and contains more specific tools inside it.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
### Change detection profiler
|
|
||||||
|
|
||||||
If your application is janky (it misses frames) or is slow according to other
|
|
||||||
metrics it is important to find the root cause of the issue. Change detection
|
|
||||||
is a phase in Angular's lifecycle that detects changes in values that are
|
|
||||||
bound to UI, and if it finds a change it performs the corresponding UI update.
|
|
||||||
However, sometimes it is hard to tell if the slowness is due to the act of
|
|
||||||
computing the changes being slow, or due to the act of applying those changes
|
|
||||||
to the UI. For your application to be performant it is important that the
|
|
||||||
process of computing changes is very fast. For best results it should be under
|
|
||||||
3 milliseconds in order to leave room for the application logic, the UI updates
|
|
||||||
and browser's rendering pipeline to fit withing the 16 millisecond frame
|
|
||||||
(assuming the 60 FPS target frame rate).
|
|
||||||
|
|
||||||
Change detection profiler repeatedly performs change detection without invoking
|
|
||||||
any user actions, such as clicking buttons or entering text in input fields. It
|
|
||||||
then computes the average amount of time it took to perform a single cycle of
|
|
||||||
change detection in milliseconds and prints it to the console. This number
|
|
||||||
depends on the current state of the UI. You will likely see different numbers
|
|
||||||
as you go from one screen in your application to another.
|
|
||||||
|
|
||||||
#### Running the profiler
|
|
||||||
|
|
||||||
Enable debug tools (see above), then in the dev console enter the following:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection();
|
|
||||||
```
|
|
||||||
|
|
||||||
The results will be printed to the console.
|
|
||||||
|
|
||||||
#### Recording CPU profile
|
|
||||||
|
|
||||||
Pass `{record: true}` an argument:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection({record: true});
|
|
||||||
```
|
|
||||||
|
|
||||||
Then open the "Profiles" tab. You will see the recorded profile titled
|
|
||||||
"Change Detection". In Chrome, if you record the profile repeatedly, all the
|
|
||||||
profiles will be nested under "Change Detection".
|
|
||||||
|
|
||||||
#### Interpreting the numbers
|
|
||||||
|
|
||||||
In a properly-designed application repeated attempts to detect changes without
|
|
||||||
any user actions should result in no changes to be applied on the UI. It is
|
|
||||||
also desirable to have the cost of a user action be proportional to the amount
|
|
||||||
of UI changes required. For example, popping up a menu with 5 items should be
|
|
||||||
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
|
||||||
change detection with no UI updates should be as fast as possible. Ideally the
|
|
||||||
number printed by the profiler should be well below the length of a single
|
|
||||||
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
|
|
||||||
|
|
||||||
#### Investigating slow change detection
|
|
||||||
|
|
||||||
So you found a screen in your application on which the profiler reports a very
|
|
||||||
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
|
||||||
recording while profiling:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
ng.profiler.timeChangeDetection({record: true});
|
|
||||||
```
|
|
||||||
|
|
||||||
Then look for hot spots using
|
|
||||||
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
|
||||||
|
|
||||||
#### Reducing change detection cost
|
|
||||||
|
|
||||||
There are many reasons for slow change detection. To gain intuition about
|
|
||||||
possible causes it would help to understand how change detection works. Such a
|
|
||||||
discussion is outside the scope of this document (TODO link to docs), but here
|
|
||||||
are some key concepts in brief.
|
|
||||||
|
|
||||||
By default Angular uses "dirty checking" mechanism for finding model changes.
|
|
||||||
This mechanism involves evaluating every bound expression that's active on the
|
|
||||||
UI. These usually include text interpolation via `{{expression}}` and property
|
|
||||||
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
|
||||||
costly to compute they could contribute to slow change detection. A good way to
|
|
||||||
speed things up is to use plain class fields in your expressions and avoid any
|
|
||||||
kinds of computation. Example:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
@Component({
|
|
||||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
|
||||||
})
|
|
||||||
class FancyButton {
|
|
||||||
// GOOD: no computation, just return the value
|
|
||||||
isEnabled: boolean;
|
|
||||||
|
|
||||||
// BAD: computes the final value upon request
|
|
||||||
_title: String;
|
|
||||||
get title(): String { return this._title.trim().toUpperCase(); }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Most cases like these could be solved by precomputing the value and storing the
|
|
||||||
final value in a field.
|
|
||||||
|
|
||||||
Angular also supports a second type of change detection - the "push" model. In
|
|
||||||
this model Angular does not poll your component for changes. Instead, the
|
|
||||||
component "tells" Angular when it changes and only then does Angular perform
|
|
||||||
the update. This model is suitable in situations when your data model uses
|
|
||||||
observable or immutable objects (also a discussion for another time).
|
|
@ -1,31 +1,105 @@
|
|||||||
# Triage Process and Github Labels for Angular 2
|
# Triage Process and Github Labels for Angular
|
||||||
|
|
||||||
This document describes how the Angular team uses labels and milestones to triage issues on github.
|
This document describes how the Angular team uses labels and milestones
|
||||||
|
to triage issues on github. The basic idea of the process is that
|
||||||
|
caretaker only assigns a component and type (bug, feature) label. The
|
||||||
|
owner of the component than is in full control of how the issues should
|
||||||
|
be triaged further.
|
||||||
|
|
||||||
# Issues and PRs
|
Once this process is implemented and in use, we will revisit it to see
|
||||||
## Triaged vs Untriaged Issues
|
if further labeling is needed.
|
||||||
|
|
||||||
Every triaged issue must have four attributes assigned to it:
|
## Components
|
||||||
|
|
||||||
* `priority` -- P0 through P4. P0 issues are "drop everything and do this now". P4 are nice to have.
|
A caretaker should be able to determine which component the issue
|
||||||
* `component` -- Which area of Angular knowledge this relates to.
|
belongs to. The components have a clear piece of source code associated
|
||||||
* `effort` -- Rough assessment of how much work this issue is. E.g. `effort: easy` means
|
with it.
|
||||||
"probably a few hours of work".
|
|
||||||
* `type` -- Whether this issue is a bug, feature, or other kind of task.
|
|
||||||
|
|
||||||
Untriaged issues are any issues in the queue that don't yet have these four attributes.
|
* `comp: animations`: `@matsko`
|
||||||
|
* `comp: benchpress`: `@tbosch`
|
||||||
|
* `comp: build & ci`: `@IgorMinar` -- All build and CI scripts
|
||||||
|
* `comp: common`: `@mhevery` -- This includes core components / pipes.
|
||||||
|
* `comp: core & compiler`: `@tbosch` -- Because core and compiler are very
|
||||||
|
intertwined, we will be treating them as one.
|
||||||
|
* `comp: forms`: `@kara`
|
||||||
|
* `comp: http`: `@jeffbcross`
|
||||||
|
* `comp: i18n`: `@vicb`
|
||||||
|
* `comp: language service`: `@chuckjaz`
|
||||||
|
* `comp: metadata-extractor`: `@chuckjaz`
|
||||||
|
* `comp: router`: `@vicb`
|
||||||
|
* `comp: testing`: `@juliemr`
|
||||||
|
* `comp: upgrade`: `@mhevery`
|
||||||
|
* `comp: web-worker`: `@vicb`
|
||||||
|
* `comp: zones`: `@mhevery`
|
||||||
|
|
||||||
You can view a report of untriaged issues here, in our
|
There are few components which are cross-cutting. They don't have
|
||||||
[Angular Triage Dashboard](http://mhevery.github.io/github_issues/).
|
a clear location in the source tree. We will treat them as a component
|
||||||
|
even thought no specific source tree is associated with them.
|
||||||
|
|
||||||
|
* `comp: docs`: `@naomiblack`
|
||||||
|
* `comp: packaging`: `@IgorMinar`
|
||||||
|
* `comp: performance`: `@tbosch`
|
||||||
|
* `comp: security`: `@IgorMinar`
|
||||||
|
|
||||||
|
|
||||||
|
## Type
|
||||||
|
What kind of problem is this?
|
||||||
|
|
||||||
|
* `type: RFC / discussion / question`
|
||||||
|
* `type: bug`
|
||||||
|
* `type: chore`
|
||||||
|
* `type: feature`
|
||||||
|
* `type: performance`
|
||||||
|
* `type: refactor`
|
||||||
|
|
||||||
|
## Caretaker Triage Process
|
||||||
|
|
||||||
|
It is the caretaker's responsibility to assign `comp: *` to each new
|
||||||
|
issue as they come in. The reason why we limit the responsibility of the
|
||||||
|
caretaker to this one label is that it is likely that without domain
|
||||||
|
knowledge the caretaker could mislabel issues or lack knowledge of
|
||||||
|
duplicate issues.
|
||||||
|
|
||||||
|
|
||||||
|
## Component's owner Triage Process
|
||||||
|
|
||||||
|
At this point we are leaving each component owner to determine their own
|
||||||
|
process for their component.
|
||||||
|
|
||||||
|
It will be up to the component owner to determine the order in which the
|
||||||
|
issues within the component will be resolved.
|
||||||
|
|
||||||
|
Several owners have adopted the issue categorization based on
|
||||||
|
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
|
||||||
|
used by AngularJS. In this system every issue is assigned frequency and
|
||||||
|
severity based on which the total user pain score is calculated.
|
||||||
|
|
||||||
|
Following is the definition of various frequency and severity levels:
|
||||||
|
|
||||||
|
1. `freq(score): *` – How often does this issue come up? How many developers does this affect?
|
||||||
|
* low (1) - obscure issue affecting a handful of developers
|
||||||
|
* moderate (2) - impacts auxiliary usage patterns, only small number of applications are affected
|
||||||
|
* high (3) - impacts primary usage patterns, affecting most Angular apps
|
||||||
|
* critical (4) - impacts all Angular apps
|
||||||
|
1. `severity(score): *` - How bad is the issue?
|
||||||
|
* inconvenience (1) - causes ugly/boilerplate code in apps
|
||||||
|
* confusing (2) - unexpected or inconsistent behavior; hard-to-debug
|
||||||
|
* broken expected use (3) - it's hard or impossible for a developer using Angular to accomplish something that Angular should be able to do
|
||||||
|
* memory leak (4)
|
||||||
|
* regression (5) - functionality that used to work no longer works in a new release due to an unintentional change
|
||||||
|
* security issue (6)
|
||||||
|
|
||||||
|
|
||||||
|
These criteria are then used to calculate a "user pain" score as follows:
|
||||||
|
|
||||||
|
`pain = severity × frequency`
|
||||||
|
|
||||||
Issues should also have a clear action to complete that can be addressed or resolved within the
|
|
||||||
scope of Angular 2. We'll close issues that don't meet these criteria.
|
|
||||||
|
|
||||||
### Assigning Issues to Milestones
|
### Assigning Issues to Milestones
|
||||||
|
|
||||||
Any issue that is being worked on must have:
|
Any issue that is being worked on must have:
|
||||||
|
|
||||||
* An `assignee`: The person doing the work.
|
* An `Assignee`: The person doing the work.
|
||||||
* A `Milestone`: When we expect to complete this work.
|
* A `Milestone`: When we expect to complete this work.
|
||||||
|
|
||||||
We aim to only have at most three milestones open at a time:
|
We aim to only have at most three milestones open at a time:
|
||||||
@ -37,7 +111,10 @@ We aim to only have at most three milestones open at a time:
|
|||||||
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
|
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
|
||||||
consists of all issues that have been triaged but do not have an assignee or milestone.
|
consists of all issues that have been triaged but do not have an assignee or milestone.
|
||||||
|
|
||||||
## Triaged vs Untriaged PRs
|
## Triaged vs Untrained PRs
|
||||||
|
|
||||||
|
PRs should also be label with a `comp: *` so that it is clear which
|
||||||
|
primary area the PR effects.
|
||||||
|
|
||||||
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
||||||
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
||||||
@ -63,90 +140,6 @@ uncontroversial change.
|
|||||||
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
|
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
|
||||||
PR to land.
|
PR to land.
|
||||||
|
|
||||||
Victor (`vsavkin`) and Tobias (`tbosch`) are owners of the PR queue. Here is a list of [current
|
|
||||||
untriaged PRs](https://github.com/angular/angular/pulls?utf8=%E2%9C%93&q=is%3Aopen+no%3Amilestone+is%3Apr+-label%3A%22pr_action%3A+cleanup%22+-label%3A%22pr_action%3A+merge%22+-label%3A%22pr_action%3A+review%22+-label%3A%22pr_action%3A+discuss%22+-label%3A%22pr_state%3A+blocked%22+-label%3A%22pr_state%3A+WIP%22+).
|
|
||||||
|
|
||||||
# Prioritization of Work
|
|
||||||
|
|
||||||
What should you be working on?
|
|
||||||
|
|
||||||
1. Any PRs that are assigned to you that don't have `pr_state: WIP` or `pr_state: blocked`
|
|
||||||
1. Any issues that are assigned to you in the lowest-numbered Milestone
|
|
||||||
1. Any issues that are assigned to you in any Milestone
|
|
||||||
|
|
||||||
If there are no issues assigned to you in any Milestone, pick an issue, self-assign it, and add
|
|
||||||
it to the most appropriate Milestone based on effort.
|
|
||||||
|
|
||||||
Here are some suggestions for what to work on next:
|
|
||||||
|
|
||||||
* Filter for issues in a component that you are knowledgeable about, and pick something that has a
|
|
||||||
high priority.
|
|
||||||
* Filter for any small effort task that has the special `cust: GT` or `cust:Ionic` tags,
|
|
||||||
and priority > P3.
|
|
||||||
* Add a new task that's really important, add `component`, `priority`, `effort`, `type` and
|
|
||||||
assign it to yourself and the most appropriate milestone.
|
|
||||||
|
|
||||||
# Labels Used in Triage
|
|
||||||
|
|
||||||
## Priority
|
|
||||||
How urgent is this issue? We use priority to determine what should be worked on in each new
|
|
||||||
milestone.
|
|
||||||
|
|
||||||
* `P0: critical` -- drop everything to work on this
|
|
||||||
* `P1: urgent` -- resolve quickly in the current milestone. people are blocked
|
|
||||||
* `P2: required` -- needed for development but not urgent yet. workaround exists, or e.g. new API
|
|
||||||
* `P3: important` -- must complete before Angular 2 is ready for release
|
|
||||||
* `P4: nice to have` -- a good idea, but maybe not until after release
|
|
||||||
|
|
||||||
|
|
||||||
## Effort
|
|
||||||
Rough, non-binding estimate of how much work this issue represents. Please change this assessment
|
|
||||||
for anything you're working on to better reflect reality.
|
|
||||||
|
|
||||||
* `effort: easy` -- straightforward issue that can be resolved in a few hours, e.g. < 1 day of work.
|
|
||||||
* `effort: medium` -- issue that will be a few days of work. Can be completed within a single
|
|
||||||
milestone.
|
|
||||||
* `effort: tough` -- issue that will likely take more than 1 milestone to complete.
|
|
||||||
|
|
||||||
<!-- We don't like these label names as
|
|
||||||
they're not absolute (what is one developer-hour, really?) but decided it wasn't worth arguing
|
|
||||||
over terms. -->
|
|
||||||
|
|
||||||
## Component
|
|
||||||
Which area of Angular knowledge is this issue most closely related to? Helpful when deciding what
|
|
||||||
to work on next.
|
|
||||||
|
|
||||||
* `comp: benchpress` -- benchmarks and performance testing → *tbosch*, *crossj*
|
|
||||||
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks → *iminar*, *caitp*
|
|
||||||
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart → *mprobst*, *alexeagle*
|
|
||||||
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) →
|
|
||||||
*mhevery*
|
|
||||||
* `comp: core/animations` -- animations framework → *matsko*
|
|
||||||
* `comp: core/change_detection` -- change detection → *vsavkin*
|
|
||||||
* `comp: core/di` -- dependency injection → *vicb*, *rkirov*
|
|
||||||
* `comp: core/directives` -- directives
|
|
||||||
* `comp: core/forms` -- forms → *vsavkin*
|
|
||||||
* `comp: core/pipes` -- pipes
|
|
||||||
* `comp: core/view` -- runtime processing of the `View`s
|
|
||||||
* `comp: core/view/compiler` -- static analysis of the templates which generate `ProtoView`s.
|
|
||||||
* `comp: core/testbed` -- e2e tests and support for them
|
|
||||||
* `comp: core/webworker` -- core web worker infrastructure
|
|
||||||
* `comp: dart-transformer` -- Dart transforms → *kegluneq*, *jakemac*
|
|
||||||
* `comp: data-access` -- → *jeffbcross*
|
|
||||||
* `comp: docs` -- API docs and doc generation → *naomiblack*, *petebacondarwin*
|
|
||||||
* `comp: material-components` -- Angular Material components built in Angular 2 → *jelbourn*
|
|
||||||
* `comp: router` -- Component Router → *btford*, *igorminar*, *matsko*
|
|
||||||
* `comp: wrenchjs`
|
|
||||||
|
|
||||||
## Type
|
|
||||||
What kind of problem is this?
|
|
||||||
|
|
||||||
* `type RFC / discussion / question`
|
|
||||||
* `type bug`
|
|
||||||
* `type chore`
|
|
||||||
* `type feature`
|
|
||||||
* `type performance`
|
|
||||||
* `type refactor`
|
|
||||||
|
|
||||||
## Special Labels
|
## Special Labels
|
||||||
|
|
||||||
@ -160,9 +153,6 @@ More active discussion is needed before the issue can be worked on further. Typi
|
|||||||
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
||||||
`cla:yes` should be merged into master.
|
`cla:yes` should be merged into master.
|
||||||
|
|
||||||
### cust
|
|
||||||
This is an issue causing user pain for early adopter customers `cust: GT` or `cust: Ionic`.
|
|
||||||
|
|
||||||
### WORKS_AS_INTENDED
|
### WORKS_AS_INTENDED
|
||||||
|
|
||||||
Only used on closed issues, to indicate to the reporter why we closed it.
|
Only used on closed issues, to indicate to the reporter why we closed it.
|
||||||
|
5
aio/.firebaserc
Normal file
5
aio/.firebaserc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"staging": "aio-staging"
|
||||||
|
}
|
||||||
|
}
|
31
aio/README.md
Normal file
31
aio/README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Site
|
||||||
|
|
||||||
|
This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.26.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||||
|
Before running the tests make sure you are serving the app via `ng serve`.
|
||||||
|
|
||||||
|
## Deploying to GitHub Pages
|
||||||
|
|
||||||
|
Run `ng github-pages:deploy` to deploy to GitHub Pages.
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
60
aio/angular-cli.json
Normal file
60
aio/angular-cli.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0-beta.26",
|
||||||
|
"name": "site"
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"root": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"assets": [
|
||||||
|
"assets",
|
||||||
|
"content",
|
||||||
|
"favicon.ico"
|
||||||
|
],
|
||||||
|
"index": "index.html",
|
||||||
|
"main": "main.ts",
|
||||||
|
"polyfills": "polyfills.ts",
|
||||||
|
"test": "test.ts",
|
||||||
|
"tsconfig": "tsconfig.json",
|
||||||
|
"prefix": "aio",
|
||||||
|
"styles": [
|
||||||
|
"styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"source": "environments/environment.ts",
|
||||||
|
"dev": "environments/environment.ts",
|
||||||
|
"prod": "environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"e2e": {
|
||||||
|
"protractor": {
|
||||||
|
"config": "./protractor.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"karma": {
|
||||||
|
"config": "./karma.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"styleExt": "css",
|
||||||
|
"prefixInterfaces": false,
|
||||||
|
"inline": {
|
||||||
|
"style": false,
|
||||||
|
"template": false
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"class": false,
|
||||||
|
"component": true,
|
||||||
|
"directive": true,
|
||||||
|
"module": false,
|
||||||
|
"pipe": true,
|
||||||
|
"service": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
aio/build/docs-app.js
Normal file
3
aio/build/docs-app.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (gulp) => () => {
|
||||||
|
// TODO:(petebd): hook up with whatever builds need doing for the webapp
|
||||||
|
};
|
24
aio/build/docs.js
Normal file
24
aio/build/docs.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generate: (gulp) => () => {
|
||||||
|
const path = require('path');
|
||||||
|
const Dgeni = require('dgeni');
|
||||||
|
const angularDocsPackage = require(path.resolve(__dirname, '../transforms/angular.io-package'));
|
||||||
|
const dgeni = new Dgeni([angularDocsPackage]);
|
||||||
|
return dgeni.generate();
|
||||||
|
},
|
||||||
|
|
||||||
|
test: (gulp) => () => {
|
||||||
|
const execSync = require('child_process').execSync;
|
||||||
|
execSync(
|
||||||
|
'node ../dist/tools/cjs-jasmine/index-tools ../../transforms/**/*.spec.js',
|
||||||
|
{stdio: ['inherit', 'inherit', 'inherit']});
|
||||||
|
}
|
||||||
|
};
|
18
aio/content/cheatsheet/bootstrapping.md
Normal file
18
aio/content/cheatsheet/bootstrapping.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Bootstrapping
|
||||||
|
@cheatsheetIndex 0
|
||||||
|
@description
|
||||||
|
{@target ts}`import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';`{@endtarget}
|
||||||
|
{@target js}Available from the `ng.platformBrowserDynamic` namespace{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`platformBrowserDynamic().bootstrapModule(AppModule);`|`platformBrowserDynamic().bootstrapModule`
|
||||||
|
syntax(js):
|
||||||
|
`document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
ng.platformBrowserDynamic
|
||||||
|
.platformBrowserDynamic()
|
||||||
|
.bootstrapModule(app.AppModule);
|
||||||
|
});`|`platformBrowserDynamic().bootstrapModule`
|
||||||
|
description:
|
||||||
|
Bootstraps the app, using the root component from the specified `NgModule`. {@target js}Must be wrapped in the event listener to fire when the page loads.{@endtarget}
|
34
aio/content/cheatsheet/built-in-directives.md
Normal file
34
aio/content/cheatsheet/built-in-directives.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Built-in directives
|
||||||
|
@cheatsheetIndex 3
|
||||||
|
@description
|
||||||
|
{@target ts}`import { CommonModule } from '@angular/common';`{@endtarget}
|
||||||
|
{@target js}Available using the `ng.common.CommonModule` module{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<section *ngIf="showSection">`|`*ngIf`
|
||||||
|
description:
|
||||||
|
Removes or recreates a portion of the DOM tree based on the `showSection` expression.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<li *ngFor="let item of list">`|`*ngFor`
|
||||||
|
description:
|
||||||
|
Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<div [ngSwitch]="conditionExpression">
|
||||||
|
<template [ngSwitchCase]="case1Exp">...</template>
|
||||||
|
<template ngSwitchCase="case2LiteralString">...</template>
|
||||||
|
<template ngSwitchDefault>...</template>
|
||||||
|
</div>`|`[ngSwitch]`|`[ngSwitchCase]`|`ngSwitchCase`|`ngSwitchDefault`
|
||||||
|
description:
|
||||||
|
Conditionally swaps the contents of the div by selecting one of the embedded templates based on the current value of `conditionExpression`.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<div [ngClass]="{active: isActive, disabled: isDisabled}">`|`[ngClass]`
|
||||||
|
description:
|
||||||
|
Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand expression should return {class-name: true/false} map.
|
49
aio/content/cheatsheet/class-decorators.md
Normal file
49
aio/content/cheatsheet/class-decorators.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Class decorators
|
||||||
|
@cheatsheetIndex 5
|
||||||
|
@description
|
||||||
|
{@target ts}`import { Directive, ... } from '@angular/core';`{@endtarget}
|
||||||
|
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@Component({...})
|
||||||
|
class MyComponent() {}`|`@Component({...})`
|
||||||
|
syntax(js):
|
||||||
|
`var MyComponent = ng.core.Component({...}).Class({...})`|`ng.core.Component({...})`
|
||||||
|
description:
|
||||||
|
Declares that a class is a component and provides metadata about the component.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@Directive({...})
|
||||||
|
class MyDirective() {}`|`@Directive({...})`
|
||||||
|
syntax(js):
|
||||||
|
`var MyDirective = ng.core.Directive({...}).Class({...})`|`ng.core.Directive({...})`
|
||||||
|
description:
|
||||||
|
Declares that a class is a directive and provides metadata about the directive.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@Pipe({...})
|
||||||
|
class MyPipe() {}`|`@Pipe({...})`
|
||||||
|
syntax(js):
|
||||||
|
`var MyPipe = ng.core.Pipe({...}).Class({...})`|`ng.core.Pipe({...})`
|
||||||
|
description:
|
||||||
|
Declares that a class is a pipe and provides metadata about the pipe.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@Injectable()
|
||||||
|
class MyService() {}`|`@Injectable()`
|
||||||
|
syntax(js):
|
||||||
|
`var OtherService = ng.core.Class(
|
||||||
|
{constructor: function() { }});
|
||||||
|
var MyService = ng.core.Class(
|
||||||
|
{constructor: [OtherService, function(otherService) { }]});`|`var MyService = ng.core.Class({constructor: [OtherService, function(otherService) { }]});`
|
||||||
|
description:
|
||||||
|
{@target ts}Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
|
||||||
|
{@endtarget}
|
||||||
|
{@target js}
|
||||||
|
Declares a service to inject into a class by providing an array with the services, with the final item being the function to receive the injected services.
|
||||||
|
{@endtarget}
|
38
aio/content/cheatsheet/component-configuration.md
Normal file
38
aio/content/cheatsheet/component-configuration.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Component configuration
|
||||||
|
@cheatsheetIndex 7
|
||||||
|
@description
|
||||||
|
{@target js}`ng.core.Component` extends `ng.core.Directive`,
|
||||||
|
so the `ng.core.Directive` configuration applies to components as well{@endtarget}
|
||||||
|
{@target ts}`@Component` extends `@Directive`,
|
||||||
|
so the `@Directive` configuration applies to components as well{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`moduleId: module.id`|`moduleId:`
|
||||||
|
description:
|
||||||
|
If set, the `templateUrl` and `styleUrl` are resolved relative to the component.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`viewProviders: [MyService, { provide: ... }]`|`viewProviders:`
|
||||||
|
syntax(js):
|
||||||
|
`viewProviders: [MyService, { provide: ... }]`|`viewProviders:`
|
||||||
|
description:
|
||||||
|
List of dependency injection providers scoped to this component's view.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`template: 'Hello {{name}}'
|
||||||
|
templateUrl: 'my-component.html'`|`template:`|`templateUrl:`
|
||||||
|
description:
|
||||||
|
Inline template or external template URL of the component's view.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`styles: ['.primary {color: red}']
|
||||||
|
styleUrls: ['my-component.css']`|`styles:`|`styleUrls:`
|
||||||
|
description:
|
||||||
|
List of inline CSS styles or external stylesheet URLs for styling the component’s view.
|
30
aio/content/cheatsheet/dependency-injection.md
Normal file
30
aio/content/cheatsheet/dependency-injection.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Dependency injection configuration
|
||||||
|
@cheatsheetIndex 10
|
||||||
|
@description
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`{ provide: MyService, useClass: MyMockService }`|`provide`|`useClass`
|
||||||
|
syntax(js):
|
||||||
|
`{ provide: MyService, useClass: MyMockService }`|`provide`|`useClass`
|
||||||
|
description:
|
||||||
|
Sets or overrides the provider for `MyService` to the `MyMockService` class.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`{ provide: MyService, useFactory: myFactory }`|`provide`|`useFactory`
|
||||||
|
syntax(js):
|
||||||
|
`{ provide: MyService, useFactory: myFactory }`|`provide`|`useFactory`
|
||||||
|
description:
|
||||||
|
Sets or overrides the provider for `MyService` to the `myFactory` factory function.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`{ provide: MyValue, useValue: 41 }`|`provide`|`useValue`
|
||||||
|
syntax(js):
|
||||||
|
`{ provide: MyValue, useValue: 41 }`|`provide`|`useValue`
|
||||||
|
description:
|
||||||
|
Sets or overrides the provider for `MyValue` to the value `41`.
|
86
aio/content/cheatsheet/directive-and-component-decorators.md
Normal file
86
aio/content/cheatsheet/directive-and-component-decorators.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Class field decorators for directives and components
|
||||||
|
@cheatsheetIndex 8
|
||||||
|
@description
|
||||||
|
{@target ts}`import { Input, ... } from '@angular/core';`{@endtarget}
|
||||||
|
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@Input() myProperty;`|`@Input()`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.Input(myProperty, myComponent);`|`ng.core.Input(`|`);`
|
||||||
|
description:
|
||||||
|
Declares an input property that you can update via property binding (example:
|
||||||
|
`<my-cmp [myProperty]="someExpression">`).
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@Output() myEvent = new EventEmitter();`|`@Output()`
|
||||||
|
syntax(js):
|
||||||
|
`myEvent = new ng.core.EventEmitter();
|
||||||
|
ng.core.Output(myEvent, myComponent);`|`ng.core.Output(`|`);`
|
||||||
|
description:
|
||||||
|
Declares an output property that fires events that you can subscribe to with an event binding (example: `<my-cmp (myEvent)="doSomething()">`).
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@HostBinding('class.valid') isValid;`|`@HostBinding('class.valid')`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.HostBinding('class.valid',
|
||||||
|
'isValid', myComponent);`|`ng.core.HostBinding('class.valid', 'isValid'`|`);`
|
||||||
|
description:
|
||||||
|
Binds a host element property (here, the CSS class `valid`) to a directive/component property (`isValid`).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@HostListener('click', ['$event']) onClick(e) {...}`|`@HostListener('click', ['$event'])`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.HostListener('click',
|
||||||
|
['$event'], onClick(e) {...}, myComponent);`|`ng.core.HostListener('click', ['$event'], onClick(e)`|`);`
|
||||||
|
description:
|
||||||
|
Subscribes to a host element event (`click`) with a directive/component method (`onClick`), optionally passing an argument (`$event`).
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@ContentChild(myPredicate) myChildComponent;`|`@ContentChild(myPredicate)`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.ContentChild(myPredicate,
|
||||||
|
'myChildComponent', myComponent);`|`ng.core.ContentChild(myPredicate,`|`);`
|
||||||
|
description:
|
||||||
|
Binds the first result of the component content query (`myPredicate`) to a property (`myChildComponent`) of the class.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@ContentChildren(myPredicate) myChildComponents;`|`@ContentChildren(myPredicate)`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.ContentChildren(myPredicate,
|
||||||
|
'myChildComponents', myComponent);`|`ng.core.ContentChildren(myPredicate,`|`);`
|
||||||
|
description:
|
||||||
|
Binds the results of the component content query (`myPredicate`) to a property (`myChildComponents`) of the class.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@ViewChild(myPredicate) myChildComponent;`|`@ViewChild(myPredicate)`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.ViewChild(myPredicate,
|
||||||
|
'myChildComponent', myComponent);`|`ng.core.ViewChild(myPredicate,`|`);`
|
||||||
|
description:
|
||||||
|
Binds the first result of the component view query (`myPredicate`) to a property (`myChildComponent`) of the class. Not available for directives.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@ViewChildren(myPredicate) myChildComponents;`|`@ViewChildren(myPredicate)`
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.ViewChildren(myPredicate,
|
||||||
|
'myChildComponents', myComponent);`|`ng.core.ViewChildren(myPredicate,`|`);`
|
||||||
|
description:
|
||||||
|
Binds the results of the component view query (`myPredicate`) to a property (`myChildComponents`) of the class. Not available for directives.
|
23
aio/content/cheatsheet/directive-configuration.md
Normal file
23
aio/content/cheatsheet/directive-configuration.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Directive configuration
|
||||||
|
@cheatsheetIndex 6
|
||||||
|
@description
|
||||||
|
{@target ts}`@Directive({ property1: value1, ... })`{@endtarget}
|
||||||
|
{@target js}`ng.core.Directive({ property1: value1, ... }).Class({...})`{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`selector: '.cool-button:not(a)'`|`selector:`
|
||||||
|
description:
|
||||||
|
Specifies a CSS selector that identifies this directive within a template. Supported selectors include `element`,
|
||||||
|
`[attribute]`, `.class`, and `:not()`.
|
||||||
|
|
||||||
|
Does not support parent-child relationship selectors.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`providers: [MyService, { provide: ... }]`|`providers:`
|
||||||
|
syntax(js):
|
||||||
|
`providers: [MyService, { provide: ... }]`|`providers:`
|
||||||
|
description:
|
||||||
|
List of dependency injection providers for this directive and its children.
|
12
aio/content/cheatsheet/forms.md
Normal file
12
aio/content/cheatsheet/forms.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Forms
|
||||||
|
@cheatsheetIndex 4
|
||||||
|
@description
|
||||||
|
{@target ts}`import { FormsModule } from '@angular/forms';`{@endtarget}
|
||||||
|
{@target js}Available using the `ng.forms.FormsModule` module{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<input [(ngModel)]="userName">`|`[(ngModel)]`
|
||||||
|
description:
|
||||||
|
Provides two-way data-binding, parsing, and validation for form controls.
|
@ -1,8 +1,8 @@
|
|||||||
@cheatsheetSection
|
@cheatsheetSection
|
||||||
Directive and component change detection and lifecycle hooks
|
Directive and component change detection and lifecycle hooks
|
||||||
@cheatsheetIndex 8
|
@cheatsheetIndex 9
|
||||||
@description
|
@description
|
||||||
{@target ts dart}(implemented as class methods){@endtarget}
|
{@target ts}(implemented as class methods){@endtarget}
|
||||||
{@target js}(implemented as component properties){@endtarget}
|
{@target js}(implemented as component properties){@endtarget}
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
@ -10,14 +10,12 @@ syntax(ts):
|
|||||||
`constructor(myService: MyService, ...) { ... }`|`constructor(myService: MyService, ...)`
|
`constructor(myService: MyService, ...) { ... }`|`constructor(myService: MyService, ...)`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`constructor: function(MyService, ...) { ... }`|`constructor: function(MyService, ...)`
|
`constructor: function(MyService, ...) { ... }`|`constructor: function(MyService, ...)`
|
||||||
syntax(dart):
|
|
||||||
`MyAppComponent(MyService myService, ...) { ... }`|`MyAppComponent(MyService myService, ...)`
|
|
||||||
description:
|
description:
|
||||||
The class constructor is called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.
|
Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.
|
||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngOnChanges(changeRecord) { ... }`|`ngOnChanges(changeRecord)`
|
`ngOnChanges(changeRecord) { ... }`|`ngOnChanges(changeRecord)`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngOnChanges: function(changeRecord) { ... }`|`ngOnChanges: function(changeRecord)`
|
`ngOnChanges: function(changeRecord) { ... }`|`ngOnChanges: function(changeRecord)`
|
||||||
@ -26,16 +24,16 @@ Called after every change to input properties and before processing content or c
|
|||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngOnInit() { ... }`|`ngOnInit()`
|
`ngOnInit() { ... }`|`ngOnInit()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngOnInit: function() { ... }`|`ngOnInit: function()`
|
`ngOnInit: function() { ... }`|`ngOnInit: function()`
|
||||||
description:
|
description:
|
||||||
Called after the constructor, initializing input properties, and the first call to ngOnChanges.
|
Called after the constructor, initializing input properties, and the first call to `ngOnChanges`.
|
||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngDoCheck() { ... }`|`ngDoCheck()`
|
`ngDoCheck() { ... }`|`ngDoCheck()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngDoCheck: function() { ... }`|`ngDoCheck: function()`
|
`ngDoCheck: function() { ... }`|`ngDoCheck: function()`
|
||||||
@ -44,16 +42,16 @@ Called every time that the input properties of a component or a directive are ch
|
|||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngAfterContentInit() { ... }`|`ngAfterContentInit()`
|
`ngAfterContentInit() { ... }`|`ngAfterContentInit()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngAfterContentInit: function() { ... }`|`ngAfterContentInit: function()`
|
`ngAfterContentInit: function() { ... }`|`ngAfterContentInit: function()`
|
||||||
description:
|
description:
|
||||||
Called after ngOnInit when the component's or directive's content has been initialized.
|
Called after `ngOnInit` when the component's or directive's content has been initialized.
|
||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngAfterContentChecked() { ... }`|`ngAfterContentChecked()`
|
`ngAfterContentChecked() { ... }`|`ngAfterContentChecked()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngAfterContentChecked: function() { ... }`|`ngAfterContentChecked: function()`
|
`ngAfterContentChecked: function() { ... }`|`ngAfterContentChecked: function()`
|
||||||
@ -62,16 +60,16 @@ Called after every check of the component's or directive's content.
|
|||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngAfterViewInit() { ... }`|`ngAfterViewInit()`
|
`ngAfterViewInit() { ... }`|`ngAfterViewInit()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngAfterViewInit: function() { ... }`|`ngAfterViewInit: function()`
|
`ngAfterViewInit: function() { ... }`|`ngAfterViewInit: function()`
|
||||||
description:
|
description:
|
||||||
Called after ngAfterContentInit when the component's view has been initialized. Applies to components only.
|
Called after `ngAfterContentInit` when the component's view has been initialized. Applies to components only.
|
||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngAfterViewChecked() { ... }`|`ngAfterViewChecked()`
|
`ngAfterViewChecked() { ... }`|`ngAfterViewChecked()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngAfterViewChecked: function() { ... }`|`ngAfterViewChecked: function()`
|
`ngAfterViewChecked: function() { ... }`|`ngAfterViewChecked: function()`
|
||||||
@ -80,7 +78,7 @@ Called after every check of the component's view. Applies to components only.
|
|||||||
|
|
||||||
|
|
||||||
@cheatsheetItem
|
@cheatsheetItem
|
||||||
syntax(ts dart):
|
syntax(ts):
|
||||||
`ngOnDestroy() { ... }`|`ngOnDestroy()`
|
`ngOnDestroy() { ... }`|`ngOnDestroy()`
|
||||||
syntax(js):
|
syntax(js):
|
||||||
`ngOnDestroy: function() { ... }`|`ngOnDestroy: function()`
|
`ngOnDestroy: function() { ... }`|`ngOnDestroy: function()`
|
58
aio/content/cheatsheet/ngmodules.md
Normal file
58
aio/content/cheatsheet/ngmodules.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
NgModules
|
||||||
|
@cheatsheetIndex 1
|
||||||
|
@description
|
||||||
|
{@target ts}`import { NgModule } from '@angular/core';`{@endtarget}
|
||||||
|
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`@NgModule({ declarations: ..., imports: ...,
|
||||||
|
exports: ..., providers: ..., bootstrap: ...})
|
||||||
|
class MyModule {}`|`NgModule`
|
||||||
|
description:
|
||||||
|
Defines a module that contains components, directives, pipes, and providers.
|
||||||
|
|
||||||
|
syntax(js):
|
||||||
|
`ng.core.NgModule({declarations: ..., imports: ...,
|
||||||
|
exports: ..., providers: ..., bootstrap: ...}).
|
||||||
|
Class({ constructor: function() {}})`
|
||||||
|
description:
|
||||||
|
Defines a module that contains components, directives, pipes, and providers.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`declarations: [MyRedComponent, MyBlueComponent, MyDatePipe]`|`declarations:`
|
||||||
|
description:
|
||||||
|
List of components, directives, and pipes that belong to this module.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`imports: [BrowserModule, SomeOtherModule]`|`imports:`
|
||||||
|
description:
|
||||||
|
List of modules to import into this module. Everything from the imported modules
|
||||||
|
is available to `declarations` of this module.
|
||||||
|
|
||||||
|
syntax(js):
|
||||||
|
`imports: [ng.platformBrowser.BrowserModule, SomeOtherModule]`|`imports:`
|
||||||
|
description:
|
||||||
|
List of modules to import into this module. Everything from the imported modules
|
||||||
|
is available to `declarations` of this module.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`exports: [MyRedComponent, MyDatePipe]`|`exports:`
|
||||||
|
description:
|
||||||
|
List of components, directives, and pipes visible to modules that import this module.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`providers: [MyService, { provide: ... }]`|`providers:`
|
||||||
|
description:
|
||||||
|
List of dependency injection providers visible both to the contents of this module and to importers of this module.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`bootstrap: [MyAppComponent]`|`bootstrap:`
|
||||||
|
description:
|
||||||
|
List of components to bootstrap when this module is bootstrapped.
|
170
aio/content/cheatsheet/routing.md
Normal file
170
aio/content/cheatsheet/routing.md
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Routing and navigation
|
||||||
|
@cheatsheetIndex 11
|
||||||
|
@description
|
||||||
|
{@target ts}`import { Routes, RouterModule, ... } from '@angular/router';`{@endtarget}
|
||||||
|
{@target js}Available from the `ng.router` namespace{@endtarget}
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`const routes: Routes = [
|
||||||
|
{ path: '', component: HomeComponent },
|
||||||
|
{ path: 'path/:routeParam', component: MyComponent },
|
||||||
|
{ path: 'staticPath', component: ... },
|
||||||
|
{ path: '**', component: ... },
|
||||||
|
{ path: 'oldPath', redirectTo: '/staticPath' },
|
||||||
|
{ path: ..., component: ..., data: { message: 'Custom' } }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const routing = RouterModule.forRoot(routes);`|`Routes`
|
||||||
|
syntax(js):
|
||||||
|
`var routes = [
|
||||||
|
{ path: '', component: HomeComponent },
|
||||||
|
{ path: ':routeParam', component: MyComponent },
|
||||||
|
{ path: 'staticPath', component: ... },
|
||||||
|
{ path: '**', component: ... },
|
||||||
|
{ path: 'oldPath', redirectTo: '/staticPath' },
|
||||||
|
{ path: ..., component: ..., data: { message: 'Custom' } }
|
||||||
|
]);
|
||||||
|
|
||||||
|
var routing = ng.router.RouterModule.forRoot(routes);`|`ng.router.Routes`
|
||||||
|
description:
|
||||||
|
Configures routes for the application. Supports static, parameterized, redirect, and wildcard routes. Also supports custom route data and resolve.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
<router-outlet name="aux"></router-outlet>
|
||||||
|
`|`router-outlet`
|
||||||
|
description:
|
||||||
|
Marks the location to load the component of the active route.
|
||||||
|
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`
|
||||||
|
<a routerLink="/path">
|
||||||
|
<a [routerLink]="[ '/path', routeParam ]">
|
||||||
|
<a [routerLink]="[ '/path', { matrixParam: 'value' } ]">
|
||||||
|
<a [routerLink]="[ '/path' ]" [queryParams]="{ page: 1 }">
|
||||||
|
<a [routerLink]="[ '/path' ]" fragment="anchor">
|
||||||
|
`|`[routerLink]`
|
||||||
|
description:
|
||||||
|
Creates a link to a different view based on a route instruction consisting of a route path, required and optional parameters, query parameters, and a fragment. To navigate to a root route, use the `/` prefix; for a child route, use the `./`prefix; for a sibling or parent, use the `../` prefix.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<a [routerLink]="[ '/path' ]" routerLinkActive="active">`
|
||||||
|
description:
|
||||||
|
The provided classes are added to the element when the `routerLink` becomes the current active route.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`class CanActivateGuard implements CanActivate {
|
||||||
|
canActivate(
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
{ path: ..., canActivate: [CanActivateGuard] }`|`CanActivate`
|
||||||
|
syntax(js):
|
||||||
|
`var CanActivateGuard = ng.core.Class({
|
||||||
|
canActivate: function(route, state) {
|
||||||
|
// return Observable/Promise boolean or boolean
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{ path: ..., canActivate: [CanActivateGuard] }`|`CanActivate`
|
||||||
|
description:
|
||||||
|
An interface for defining a class that the router should call first to determine if it should activate this component. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`class CanDeactivateGuard implements CanDeactivate<T> {
|
||||||
|
canDeactivate(
|
||||||
|
component: T,
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
{ path: ..., canDeactivate: [CanDeactivateGuard] }`|`CanDeactivate`
|
||||||
|
syntax(js):
|
||||||
|
`var CanDeactivateGuard = ng.core.Class({
|
||||||
|
canDeactivate: function(component, route, state) {
|
||||||
|
// return Observable/Promise boolean or boolean
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{ path: ..., canDeactivate: [CanDeactivateGuard] }`|`CanDeactivate`
|
||||||
|
description:
|
||||||
|
An interface for defining a class that the router should call first to determine if it should deactivate this component after a navigation. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`class CanActivateChildGuard implements CanActivateChild {
|
||||||
|
canActivateChild(
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
{ path: ..., canActivateChild: [CanActivateGuard],
|
||||||
|
children: ... }`|`CanActivateChild`
|
||||||
|
syntax(js):
|
||||||
|
`var CanActivateChildGuard = ng.core.Class({
|
||||||
|
canActivateChild: function(route, state) {
|
||||||
|
// return Observable/Promise boolean or boolean
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{ path: ..., canActivateChild: [CanActivateChildGuard],
|
||||||
|
children: ... }`|`CanActivateChild`
|
||||||
|
description:
|
||||||
|
An interface for defining a class that the router should call first to determine if it should activate the child route. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`class ResolveGuard implements Resolve<T> {
|
||||||
|
resolve(
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<any>|Promise<any>|any { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
{ path: ..., resolve: [ResolveGuard] }`|`Resolve`
|
||||||
|
syntax(js):
|
||||||
|
`var ResolveGuard = ng.core.Class({
|
||||||
|
resolve: function(route, state) {
|
||||||
|
// return Observable/Promise value or value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{ path: ..., resolve: [ResolveGuard] }`|`Resolve`
|
||||||
|
description:
|
||||||
|
An interface for defining a class that the router should call first to resolve route data before rendering the route. Should return a value or an Observable/Promise that resolves to a value.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax(ts):
|
||||||
|
`class CanLoadGuard implements CanLoad {
|
||||||
|
canLoad(
|
||||||
|
route: Route
|
||||||
|
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
{ path: ..., canLoad: [CanLoadGuard], loadChildren: ... }`|`CanLoad`
|
||||||
|
syntax(js):
|
||||||
|
`var CanLoadGuard = ng.core.Class({
|
||||||
|
canLoad: function(route) {
|
||||||
|
// return Observable/Promise boolean or boolean
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{ path: ..., canLoad: [CanLoadGuard], loadChildren: ... }`|`CanLoad`
|
||||||
|
description:
|
||||||
|
An interface for defining a class that the router should call first to check if the lazy loaded module should be loaded. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||||
|
|
94
aio/content/cheatsheet/template-syntax.md
Normal file
94
aio/content/cheatsheet/template-syntax.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
@cheatsheetSection
|
||||||
|
Template syntax
|
||||||
|
@cheatsheetIndex 2
|
||||||
|
@description
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<input [value]="firstName">`|`[value]`
|
||||||
|
description:
|
||||||
|
Binds property `value` to the result of expression `firstName`.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<div [attr.role]="myAriaRole">`|`[attr.role]`
|
||||||
|
description:
|
||||||
|
Binds attribute `role` to the result of expression `myAriaRole`.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<div [class.extra-sparkle]="isDelightful">`|`[class.extra-sparkle]`
|
||||||
|
description:
|
||||||
|
Binds the presence of the CSS class `extra-sparkle` on the element to the truthiness of the expression `isDelightful`.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<div [style.width.px]="mySize">`|`[style.width.px]`
|
||||||
|
description:
|
||||||
|
Binds style property `width` to the result of expression `mySize` in pixels. Units are optional.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<button (click)="readRainbow($event)">`|`(click)`
|
||||||
|
description:
|
||||||
|
Calls method `readRainbow` when a click event is triggered on this button element (or its children) and passes in the event object.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<div title="Hello {{ponyName}}">`|`{{ponyName}}`
|
||||||
|
description:
|
||||||
|
Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
|
||||||
|
`<div [title]="'Hello ' + ponyName">`
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<p>Hello {{ponyName}}</p>`|`{{ponyName}}`
|
||||||
|
description:
|
||||||
|
Binds text content to an interpolated string, for example, "Hello Seabiscuit".
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<my-cmp [(title)]="name">`|`[(title)]`
|
||||||
|
description:
|
||||||
|
Sets up two-way data binding. Equivalent to: `<my-cmp [title]="name" (titleChange)="name=$event">`
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<video #movieplayer ...>
|
||||||
|
<button (click)="movieplayer.play()">
|
||||||
|
</video>`|`#movieplayer`|`(click)`
|
||||||
|
description:
|
||||||
|
Creates a local variable `movieplayer` that provides access to the `video` element instance in data-binding and event-binding expressions in the current template.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<p *myUnless="myExpression">...</p>`|`*myUnless`
|
||||||
|
description:
|
||||||
|
The `*` symbol turns the current element into an embedded template. Equivalent to:
|
||||||
|
`<template [myUnless]="myExpression"><p>...</p></template>`
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<p>Card No.: {{cardNumber | myCardNumberFormatter}}</p>`|`{{cardNumber | myCardNumberFormatter}}`
|
||||||
|
description:
|
||||||
|
Transforms the current value of expression `cardNumber` via the pipe called `myCardNumberFormatter`.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<p>Employer: {{employer?.companyName}}</p>`|`{{employer?.companyName}}`
|
||||||
|
description:
|
||||||
|
The safe navigation operator (`?`) means that the `employer` field is optional and if `undefined`, the rest of the expression should be ignored.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<svg:rect x="0" y="0" width="100" height="100"/>`|`svg:`
|
||||||
|
description:
|
||||||
|
An SVG snippet template needs an `svg:` prefix on its root element to disambiguate the SVG element from an HTML component.
|
||||||
|
|
||||||
|
@cheatsheetItem
|
||||||
|
syntax:
|
||||||
|
`<svg>
|
||||||
|
<rect x="0" y="0" width="100" height="100"/>
|
||||||
|
</svg>`|`svg`
|
||||||
|
description:
|
||||||
|
An `<svg>` root element is detected as an SVG element automatically, without the prefix.
|
160
aio/content/cookbook/component-relative-paths.md
Normal file
160
aio/content/cookbook/component-relative-paths.md
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
@title
|
||||||
|
Component-relative Paths
|
||||||
|
|
||||||
|
@intro
|
||||||
|
Use relative URLs for component templates and styles.
|
||||||
|
|
||||||
|
@description
|
||||||
|
## Write *Component-Relative* URLs to component templates and style files
|
||||||
|
|
||||||
|
Our components often refer to external template and style files.
|
||||||
|
We identify those files with a URL in the `templateUrl` and `styleUrls` properties of the `@Component` metadata
|
||||||
|
as seen here:
|
||||||
|
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.ts' -region='absolute-config' -linenums='false' }
|
||||||
|
|
||||||
|
By default, we *must* specify the full path back to the application root.
|
||||||
|
We call this an ***absolute path*** because it is *absolute* with respect to the application root.
|
||||||
|
|
||||||
|
There are two problems with an *absolute path*:
|
||||||
|
|
||||||
|
1. We have to remember the full path back to the application root.
|
||||||
|
|
||||||
|
2. We have to update the URL when we move the component around in the application files structure.
|
||||||
|
|
||||||
|
It would be much easier to write and maintain our application components if we could specify template and style locations
|
||||||
|
*relative* to their component class file.
|
||||||
|
|
||||||
|
*We can!*
|
||||||
|
|
||||||
|
~~~ {.alert.is-important}
|
||||||
|
|
||||||
|
We can if we build our application as `commonjs` modules and load those modules
|
||||||
|
with a suitable package loader such as `systemjs` or `webpack`.
|
||||||
|
Learn why [below](#why-default).
|
||||||
|
|
||||||
|
The Angular CLI uses these technologies and defaults to the
|
||||||
|
*component-relative path* approach described here.
|
||||||
|
CLI users can skip this chapter or read on to understand
|
||||||
|
how it works.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## _Component-Relative_ Paths
|
||||||
|
|
||||||
|
Our goal is to specify template and style URLs *relative* to their component class files,
|
||||||
|
hence the term ***component-relative path***.
|
||||||
|
|
||||||
|
The key to success is following a convention that puts related component files in well-known locations.
|
||||||
|
|
||||||
|
We recommend keeping component template and component-specific style files as *siblings* of their
|
||||||
|
companion component class files.
|
||||||
|
Here we see the three files for `SomeComponent` sitting next to each other in the `app` folder.
|
||||||
|
|
||||||
|
<aio-file-tree>
|
||||||
|
<aio-folder>app
|
||||||
|
<aio-file>some.component.css</aio-file>
|
||||||
|
<aio-file>some.component.html</aio-file>
|
||||||
|
<aio-file>some.component.ts</aio-file>
|
||||||
|
<aio-file>...</aio-file>
|
||||||
|
</aio-folder>
|
||||||
|
</aio-file-tree>
|
||||||
|
|
||||||
|
We'll have more files and folders — and greater folder depth — as our application grows.
|
||||||
|
We'll be fine as long as the component files travel together as the inseparable siblings they are.
|
||||||
|
|
||||||
|
### Set the *moduleId*
|
||||||
|
|
||||||
|
Having adopted this file structure convention, we can specify locations of the template and style files
|
||||||
|
relative to the component class file simply by setting the `moduleId` property of the `@Component` metadata like this
|
||||||
|
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.ts' -region='module-id' -linenums='false'}
|
||||||
|
|
||||||
|
We strip the `app/` base path from the `templateUrl` and `styleUrls` and replace it with `./`.
|
||||||
|
The result looks like this:
|
||||||
|
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.ts' -region='relative-config' -linenums='false'}
|
||||||
|
|
||||||
|
~~~ {.alert.is-helpful}
|
||||||
|
|
||||||
|
Webpack users may prefer [an alternative approach](#webpack).
|
||||||
|
|
||||||
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
**We can see the <live-example name="cb-component-relative-paths"></live-example>**
|
||||||
|
and download the source code from there
|
||||||
|
or simply read the pertinent source here.
|
||||||
|
|
||||||
|
<md-tab-group>
|
||||||
|
<md-tab label="app/some.component.ts">
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.ts'}
|
||||||
|
</md-tab>
|
||||||
|
<md-tab label="app/some.component.html">
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.html'}
|
||||||
|
</md-tab>
|
||||||
|
<md-tab label="app/some.component.css">
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.css'}
|
||||||
|
</md-tab>
|
||||||
|
<md-tab label="app/app.component.ts">
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/app.component.ts'}
|
||||||
|
</md-tab>
|
||||||
|
<md-tab-group>
|
||||||
|
|
||||||
|
|
||||||
|
{@a why-default}
|
||||||
|
|
||||||
|
## Appendix: why *component-relative* is not the default
|
||||||
|
|
||||||
|
A *component-relative* path is obviously superior to an *absolute* path.
|
||||||
|
Why did Angular default to the *absolute* path?
|
||||||
|
Why do *we* have to set the `moduleId`? Why can't Angular set it?
|
||||||
|
|
||||||
|
First, let's look at what happens if we use a relative path and omit the `moduleId`.
|
||||||
|
|
||||||
|
`EXCEPTION: Failed to load some.component.html`
|
||||||
|
|
||||||
|
Angular can't find the file so it throws an error.
|
||||||
|
|
||||||
|
Why can't Angular calculate the template and style URLs from the component file's location?
|
||||||
|
|
||||||
|
Because the location of the component can't be determined without the developer's help.
|
||||||
|
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
|
||||||
|
from CommonJS packages, to name a few.
|
||||||
|
We might generate modules in any of several formats.
|
||||||
|
We might not be writing modular code at all!
|
||||||
|
|
||||||
|
With this diversity of packaging and module load strategies,
|
||||||
|
it's not possible for Angular to know with certainty where these files reside at runtime.
|
||||||
|
|
||||||
|
The only location Angular can be sure of is the URL of the `index.html` home page, the application root.
|
||||||
|
So by default it resolves template and style paths relative to the URL of `index.html`.
|
||||||
|
That's why we previously wrote our file URLs with an `app/` base path prefix.
|
||||||
|
|
||||||
|
But *if* we follow the recommended guidelines and we write modules in `commonjs` format
|
||||||
|
and we use a module loader that *plays nice*,
|
||||||
|
*then* we — the developers of the application —
|
||||||
|
know that the semi-global `module.id` variable is available and contains
|
||||||
|
the absolute URL of the component class module file.
|
||||||
|
|
||||||
|
That knowledge enables us to tell Angular where the *component* file is
|
||||||
|
by setting the `moduleId`:
|
||||||
|
|
||||||
|
{@example 'cb-component-relative-paths/ts/app/some.component.ts' -region='module-id' -linenums='false'}
|
||||||
|
|
||||||
|
|
||||||
|
{@a webpack}
|
||||||
|
|
||||||
|
## Webpack: load templates and styles
|
||||||
|
Webpack developers have an alternative to `moduleId`.
|
||||||
|
|
||||||
|
They can load templates and styles at runtime by adding `./` at the beginning of the `template` and `styles` / `styleUrls`
|
||||||
|
properties that reference *component-relative URLS.
|
||||||
|
|
||||||
|
{@example 'webpack/ts/src/app/app.component.ts' --linenums='false'}
|
||||||
|
|
||||||
|
Webpack will do a `require` behind the scenes to load the templates and styles. Read more [here](../guide/webpack.html#highlights).
|
||||||
|
|
||||||
|
See the [Introduction to Webpack](../guide/webpack.html).
|
42
aio/content/examples/cb-component-relative-paths/e2e-spec.ts
Normal file
42
aio/content/examples/cb-component-relative-paths/e2e-spec.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
'use strict'; // necessary for es6 output in node
|
||||||
|
|
||||||
|
import { browser, element, by, ElementFinder } from 'protractor';
|
||||||
|
|
||||||
|
describe('Cookbook: component-relative paths', function () {
|
||||||
|
|
||||||
|
interface Page {
|
||||||
|
title: ElementFinder;
|
||||||
|
absComp: ElementFinder;
|
||||||
|
relComp: ElementFinder;
|
||||||
|
|
||||||
|
}
|
||||||
|
function getPageStruct() {
|
||||||
|
return {
|
||||||
|
title: element( by.tagName( 'h1' )),
|
||||||
|
absComp: element( by.css( 'absolute-path div' ) ),
|
||||||
|
relComp: element( by.css( 'relative-path div' ) )
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let page: Page;
|
||||||
|
beforeAll(function () {
|
||||||
|
browser.get('');
|
||||||
|
page = getPageStruct();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display title of the sample', function () {
|
||||||
|
expect(element(by.tagName('h1')).getText()).toContain('Paths');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have absolute-path element', function () {
|
||||||
|
expect(page.absComp.isPresent()).toBe(true, 'no <absolute-path> element');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the absolute path text', function () {
|
||||||
|
expect(page.absComp.getText()).toContain('Absolute');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the component-relative path text', function () {
|
||||||
|
expect(page.relComp.getText()).toContain('Component-relative');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,12 @@
|
|||||||
|
// #docregion
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template:
|
||||||
|
`<h1>Absolute & <i>Component-Relative</i> Paths</h1>
|
||||||
|
<absolute-path></absolute-path>
|
||||||
|
<relative-path></relative-path>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class AppComponent {}
|
@ -0,0 +1,18 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { SomeAbsoluteComponent, SomeRelativeComponent } from './some.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
SomeAbsoluteComponent,
|
||||||
|
SomeRelativeComponent
|
||||||
|
],
|
||||||
|
bootstrap: [ AppComponent ]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
@ -0,0 +1,5 @@
|
|||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
@ -0,0 +1,22 @@
|
|||||||
|
/* #docregion */
|
||||||
|
div.absolute {
|
||||||
|
background: beige;
|
||||||
|
border: 1px solid darkred;
|
||||||
|
color: red;
|
||||||
|
margin: 8px;
|
||||||
|
max-width: 20em;
|
||||||
|
padding: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.relative {
|
||||||
|
background: powderblue;
|
||||||
|
border: 1px solid darkblue;
|
||||||
|
color: Blue;
|
||||||
|
font-style: italic;
|
||||||
|
margin: 8px;
|
||||||
|
max-width: 20em;
|
||||||
|
padding: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
<!-- #docregion -->
|
||||||
|
<div class={{class}}>
|
||||||
|
{{type}}<br>{{path}}
|
||||||
|
</div>
|
@ -0,0 +1,37 @@
|
|||||||
|
// #docregion
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
///////// Using Absolute Paths ///////
|
||||||
|
|
||||||
|
// #docregion absolute-config
|
||||||
|
@Component({
|
||||||
|
selector: 'absolute-path',
|
||||||
|
templateUrl: 'app/some.component.html',
|
||||||
|
styleUrls: ['app/some.component.css']
|
||||||
|
})
|
||||||
|
// #enddocregion absolute-config
|
||||||
|
export class SomeAbsoluteComponent {
|
||||||
|
class = 'absolute';
|
||||||
|
type = 'Absolute template & style URLs';
|
||||||
|
path = 'app/path.component.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// Using Relative Paths ///////
|
||||||
|
|
||||||
|
// #docregion relative-config
|
||||||
|
@Component({
|
||||||
|
// #docregion module-id
|
||||||
|
moduleId: module.id,
|
||||||
|
// #enddocregion module-id
|
||||||
|
selector: 'relative-path',
|
||||||
|
templateUrl: './some.component.html',
|
||||||
|
styleUrls: ['./some.component.css']
|
||||||
|
})
|
||||||
|
// #enddocregion relative-config
|
||||||
|
|
||||||
|
export class SomeRelativeComponent {
|
||||||
|
class = 'relative';
|
||||||
|
type = 'Component-relative template & style URLs';
|
||||||
|
path = 'path.component.html';
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
Component-Relative Paths
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<!-- #docregion style -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||||
|
<!-- #enddocregion style -->
|
||||||
|
|
||||||
|
<!-- Polyfills for older browsers -->
|
||||||
|
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||||
|
|
||||||
|
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||||
|
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||||
|
|
||||||
|
<script src="systemjs.config.js"></script>
|
||||||
|
<script>
|
||||||
|
System.import('app').catch(function(err){ console.error(err); });
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<my-app>Loading app...</my-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"description": "Module-relative Paths",
|
||||||
|
"files": [
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js"
|
||||||
|
],
|
||||||
|
"tags": [ "cookbook" ]
|
||||||
|
}
|
21
aio/content/examples/webpack/e2e-spec.ts
Normal file
21
aio/content/examples/webpack/e2e-spec.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use strict'; // necessary for es6 output in node
|
||||||
|
|
||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
describe('QuickStart E2E Tests', function () {
|
||||||
|
|
||||||
|
let expectedMsg = 'Hello from Angular App with Webpack';
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
browser.get('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should display: ${expectedMsg}`, function () {
|
||||||
|
expect(element(by.css('h1')).getText()).toEqual(expectedMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an image', function () {
|
||||||
|
expect(element(by.css('img')).isPresent()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,58 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
// #docregion one-entry
|
||||||
|
entry: {
|
||||||
|
app: 'src/app.ts'
|
||||||
|
}
|
||||||
|
// #enddocregion one-entry
|
||||||
|
|
||||||
|
// #docregion app-example
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
...
|
||||||
|
})
|
||||||
|
export class AppComponent {}
|
||||||
|
// #enddocregion app-example
|
||||||
|
|
||||||
|
// #docregion one-output
|
||||||
|
output: {
|
||||||
|
filename: 'app.js'
|
||||||
|
}
|
||||||
|
// #enddocregion one-output
|
||||||
|
|
||||||
|
// #docregion two-entries
|
||||||
|
entry: {
|
||||||
|
app: 'src/app.ts',
|
||||||
|
vendor: 'src/vendor.ts'
|
||||||
|
},
|
||||||
|
|
||||||
|
output: {
|
||||||
|
filename: '[name].js'
|
||||||
|
}
|
||||||
|
// #enddocregion two-entries
|
||||||
|
|
||||||
|
// #docregion loaders
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/
|
||||||
|
loader: 'awesome-typescript-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/
|
||||||
|
loaders: 'style-loader!css-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// #enddocregion loaders
|
||||||
|
|
||||||
|
// #docregion imports
|
||||||
|
// #docregion single-import
|
||||||
|
import { AppComponent } from './app.component.ts';
|
||||||
|
// #enddocregion single-import
|
||||||
|
import 'uiframework/dist/uiframework.css';
|
||||||
|
// #enddocregion imports
|
||||||
|
|
||||||
|
// #docregion plugins
|
||||||
|
plugins: [
|
||||||
|
new webpack.optimize.UglifyJsPlugin()
|
||||||
|
]
|
||||||
|
// #enddocregion plugins
|
5
aio/content/examples/webpack/ts/.gitignore
vendored
Normal file
5
aio/content/examples/webpack/ts/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
dist
|
||||||
|
!karma.webpack.conf.js
|
||||||
|
!webpack.config.js
|
||||||
|
!config/*
|
||||||
|
!public/css/styles.css
|
12
aio/content/examples/webpack/ts/config/helpers.js
Normal file
12
aio/content/examples/webpack/ts/config/helpers.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// #docregion
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var _root = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
function root(args) {
|
||||||
|
args = Array.prototype.slice.call(arguments, 0);
|
||||||
|
return path.join.apply(path, [_root].concat(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.root = root;
|
||||||
|
// #enddocregion
|
22
aio/content/examples/webpack/ts/config/karma-test-shim.js
Normal file
22
aio/content/examples/webpack/ts/config/karma-test-shim.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// #docregion
|
||||||
|
Error.stackTraceLimit = Infinity;
|
||||||
|
|
||||||
|
require('core-js/es6');
|
||||||
|
require('core-js/es7/reflect');
|
||||||
|
|
||||||
|
require('zone.js/dist/zone');
|
||||||
|
require('zone.js/dist/long-stack-trace-zone');
|
||||||
|
require('zone.js/dist/proxy');
|
||||||
|
require('zone.js/dist/sync-test');
|
||||||
|
require('zone.js/dist/jasmine-patch');
|
||||||
|
require('zone.js/dist/async-test');
|
||||||
|
require('zone.js/dist/fake-async-test');
|
||||||
|
|
||||||
|
var appContext = require.context('../src', true, /\.spec\.ts/);
|
||||||
|
|
||||||
|
appContext.keys().forEach(appContext);
|
||||||
|
|
||||||
|
var testing = require('@angular/core/testing');
|
||||||
|
var browser = require('@angular/platform-browser-dynamic/testing');
|
||||||
|
|
||||||
|
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
|
39
aio/content/examples/webpack/ts/config/karma.conf.js
Normal file
39
aio/content/examples/webpack/ts/config/karma.conf.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpackConfig = require('./webpack.test');
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
var _config = {
|
||||||
|
basePath: '',
|
||||||
|
|
||||||
|
frameworks: ['jasmine'],
|
||||||
|
|
||||||
|
files: [
|
||||||
|
{pattern: './config/karma-test-shim.js', watched: false}
|
||||||
|
],
|
||||||
|
|
||||||
|
preprocessors: {
|
||||||
|
'./config/karma-test-shim.js': ['webpack', 'sourcemap']
|
||||||
|
},
|
||||||
|
|
||||||
|
webpack: webpackConfig,
|
||||||
|
|
||||||
|
webpackMiddleware: {
|
||||||
|
stats: 'errors-only'
|
||||||
|
},
|
||||||
|
|
||||||
|
webpackServer: {
|
||||||
|
noInfo: true
|
||||||
|
},
|
||||||
|
|
||||||
|
reporters: ['progress'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: false,
|
||||||
|
browsers: ['PhantomJS'],
|
||||||
|
singleRun: true
|
||||||
|
};
|
||||||
|
|
||||||
|
config.set(_config);
|
||||||
|
};
|
||||||
|
// #enddocregion
|
72
aio/content/examples/webpack/ts/config/webpack.common.js
Normal file
72
aio/content/examples/webpack/ts/config/webpack.common.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// #docregion entries
|
||||||
|
entry: {
|
||||||
|
'polyfills': './src/polyfills.ts',
|
||||||
|
'vendor': './src/vendor.ts',
|
||||||
|
'app': './src/main.ts'
|
||||||
|
},
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
// #docregion resolve
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js']
|
||||||
|
},
|
||||||
|
// #enddocregion resolve
|
||||||
|
|
||||||
|
// #docregion loaders
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
loader: 'html-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||||
|
loader: 'file-loader?name=assets/[name].[hash].[ext]'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: helpers.root('src', 'app'),
|
||||||
|
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
include: helpers.root('src', 'app'),
|
||||||
|
loader: 'raw-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// #enddocregion loaders
|
||||||
|
|
||||||
|
// #docregion plugins
|
||||||
|
plugins: [
|
||||||
|
// Workaround for angular/angular#11580
|
||||||
|
new webpack.ContextReplacementPlugin(
|
||||||
|
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||||
|
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
|
||||||
|
helpers.root('./src'), // location of your src
|
||||||
|
{} // a map of your routes
|
||||||
|
),
|
||||||
|
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: ['app', 'vendor', 'polyfills']
|
||||||
|
}),
|
||||||
|
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'src/index.html'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
// #enddocregion plugins
|
||||||
|
};
|
||||||
|
// #enddocregion
|
||||||
|
|
26
aio/content/examples/webpack/ts/config/webpack.dev.js
Normal file
26
aio/content/examples/webpack/ts/config/webpack.dev.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpackMerge = require('webpack-merge');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var commonConfig = require('./webpack.common.js');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
module.exports = webpackMerge(commonConfig, {
|
||||||
|
devtool: 'cheap-module-eval-source-map',
|
||||||
|
|
||||||
|
output: {
|
||||||
|
path: helpers.root('dist'),
|
||||||
|
publicPath: 'http://localhost:8080/',
|
||||||
|
filename: '[name].js',
|
||||||
|
chunkFilename: '[id].chunk.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new ExtractTextPlugin('[name].css')
|
||||||
|
],
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
historyApiFallback: true,
|
||||||
|
stats: 'minimal'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #enddocregion
|
41
aio/content/examples/webpack/ts/config/webpack.prod.js
Normal file
41
aio/content/examples/webpack/ts/config/webpack.prod.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var webpackMerge = require('webpack-merge');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var commonConfig = require('./webpack.common.js');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
|
||||||
|
|
||||||
|
module.exports = webpackMerge(commonConfig, {
|
||||||
|
devtool: 'source-map',
|
||||||
|
|
||||||
|
output: {
|
||||||
|
path: helpers.root('dist'),
|
||||||
|
publicPath: '/',
|
||||||
|
filename: '[name].[hash].js',
|
||||||
|
chunkFilename: '[id].[hash].chunk.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.NoEmitOnErrorsPlugin(),
|
||||||
|
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
|
||||||
|
mangle: {
|
||||||
|
keep_fnames: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new ExtractTextPlugin('[name].[hash].css'),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
'ENV': JSON.stringify(ENV)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.LoaderOptionsPlugin({
|
||||||
|
htmlLoader: {
|
||||||
|
minimize: false // workaround for ng2
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion
|
50
aio/content/examples/webpack/ts/config/webpack.test.js
Normal file
50
aio/content/examples/webpack/ts/config/webpack.test.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// #docregion
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var helpers = require('./helpers');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js']
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
loader: 'html-loader'
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||||
|
loader: 'null-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: helpers.root('src', 'app'),
|
||||||
|
loader: 'null-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
include: helpers.root('src', 'app'),
|
||||||
|
loader: 'raw-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.ContextReplacementPlugin(
|
||||||
|
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||||
|
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
|
||||||
|
helpers.root('./src'), // location of your src
|
||||||
|
{} // a map of your routes
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// #enddocregion
|
4
aio/content/examples/webpack/ts/example-config.json
Normal file
4
aio/content/examples/webpack/ts/example-config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"build": "build:webpack",
|
||||||
|
"run": "http-server:cli"
|
||||||
|
}
|
2
aio/content/examples/webpack/ts/karma.webpack.conf.js
Normal file
2
aio/content/examples/webpack/ts/karma.webpack.conf.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// #docregion
|
||||||
|
module.exports = require('./config/karma.conf.js');
|
50
aio/content/examples/webpack/ts/package.webpack.json
Normal file
50
aio/content/examples/webpack/ts/package.webpack.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "angular2-webpack",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A webpack starter for Angular",
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack-dev-server --inline --progress --port 8080",
|
||||||
|
"test": "karma start",
|
||||||
|
"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/common": "~2.4.0",
|
||||||
|
"@angular/compiler": "~2.4.0",
|
||||||
|
"@angular/core": "~2.4.0",
|
||||||
|
"@angular/forms": "~2.4.0",
|
||||||
|
"@angular/http": "~2.4.0",
|
||||||
|
"@angular/platform-browser": "~2.4.0",
|
||||||
|
"@angular/platform-browser-dynamic": "~2.4.0",
|
||||||
|
"@angular/router": "~3.4.0",
|
||||||
|
"core-js": "^2.4.1",
|
||||||
|
"rxjs": "5.0.1",
|
||||||
|
"zone.js": "^0.7.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^6.0.45",
|
||||||
|
"@types/jasmine": "^2.5.35",
|
||||||
|
"angular2-template-loader": "^0.6.0",
|
||||||
|
"awesome-typescript-loader": "^3.0.0-beta.18",
|
||||||
|
"css-loader": "^0.26.1",
|
||||||
|
"extract-text-webpack-plugin": "2.0.0-beta.5",
|
||||||
|
"file-loader": "^0.9.0",
|
||||||
|
"html-loader": "^0.4.3",
|
||||||
|
"html-webpack-plugin": "^2.16.1",
|
||||||
|
"jasmine-core": "^2.4.1",
|
||||||
|
"karma": "^1.2.0",
|
||||||
|
"karma-jasmine": "^1.0.2",
|
||||||
|
"karma-phantomjs-launcher": "^1.0.2",
|
||||||
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
|
"karma-webpack": "^2.0.1",
|
||||||
|
"null-loader": "^0.1.1",
|
||||||
|
"phantomjs-prebuilt": "^2.1.7",
|
||||||
|
"raw-loader": "^0.5.1",
|
||||||
|
"rimraf": "^2.5.2",
|
||||||
|
"style-loader": "^0.13.1",
|
||||||
|
"typescript": "~2.0.10",
|
||||||
|
"webpack": "2.2.0",
|
||||||
|
"webpack-dev-server": "2.2.0-rc.0",
|
||||||
|
"webpack-merge": "^2.4.0"
|
||||||
|
}
|
||||||
|
}
|
6
aio/content/examples/webpack/ts/public/css/styles.css
Normal file
6
aio/content/examples/webpack/ts/public/css/styles.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/* #docregion */
|
||||||
|
body {
|
||||||
|
background: #0147A7;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
/* #enddocregion */
|
BIN
aio/content/examples/webpack/ts/public/images/angular.png
Normal file
BIN
aio/content/examples/webpack/ts/public/images/angular.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
/* #docregion */
|
||||||
|
main {
|
||||||
|
padding: 1em;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* #enddocregion */
|
@ -0,0 +1,7 @@
|
|||||||
|
<!-- #docregion -->
|
||||||
|
<main>
|
||||||
|
<h1>Hello from Angular App with Webpack</h1>
|
||||||
|
|
||||||
|
<img src="../../public/images/angular.png">
|
||||||
|
</main>
|
||||||
|
<!-- #enddocregion -->
|
@ -0,0 +1,16 @@
|
|||||||
|
// #docregion
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('App', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({ declarations: [AppComponent]});
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should work', () => {
|
||||||
|
let fixture = TestBed.createComponent(AppComponent);
|
||||||
|
expect(fixture.componentInstance instanceof AppComponent).toBe(true, 'should create AppComponent');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// #enddocregion
|
12
aio/content/examples/webpack/ts/src/app/app.component.ts
Normal file
12
aio/content/examples/webpack/ts/src/app/app.component.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// #docregion
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import '../../public/css/styles.css';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent { }
|
||||||
|
// #enddocregion
|
16
aio/content/examples/webpack/ts/src/app/app.module.ts
Normal file
16
aio/content/examples/webpack/ts/src/app/app.module.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// #docregion
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
bootstrap: [ AppComponent ]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
14
aio/content/examples/webpack/ts/src/index.html
Normal file
14
aio/content/examples/webpack/ts/src/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!-- #docregion -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<title>Angular With Webpack</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<my-app>Loading...</my-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<!-- #enddocregion -->
|
14
aio/content/examples/webpack/ts/src/main.ts
Normal file
14
aio/content/examples/webpack/ts/src/main.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// #docregion
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
|
||||||
|
// #docregion enable-prod
|
||||||
|
if (process.env.ENV === 'production') {
|
||||||
|
enableProdMode();
|
||||||
|
}
|
||||||
|
// #enddocregion enable-prod
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
// #enddocregion
|
12
aio/content/examples/webpack/ts/src/polyfills.ts
Normal file
12
aio/content/examples/webpack/ts/src/polyfills.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// #docregion
|
||||||
|
import 'core-js/es6';
|
||||||
|
import 'core-js/es7/reflect';
|
||||||
|
require('zone.js/dist/zone');
|
||||||
|
|
||||||
|
if (process.env.ENV === 'production') {
|
||||||
|
// Production
|
||||||
|
} else {
|
||||||
|
// Development and test
|
||||||
|
Error['stackTraceLimit'] = Infinity;
|
||||||
|
require('zone.js/dist/long-stack-trace-zone');
|
||||||
|
}
|
15
aio/content/examples/webpack/ts/src/vendor.ts
Normal file
15
aio/content/examples/webpack/ts/src/vendor.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// #docregion
|
||||||
|
// Angular
|
||||||
|
import '@angular/platform-browser';
|
||||||
|
import '@angular/platform-browser-dynamic';
|
||||||
|
import '@angular/core';
|
||||||
|
import '@angular/common';
|
||||||
|
import '@angular/http';
|
||||||
|
import '@angular/router';
|
||||||
|
|
||||||
|
// RxJS
|
||||||
|
import 'rxjs';
|
||||||
|
|
||||||
|
// Other vendors for example jQuery, Lodash or Bootstrap
|
||||||
|
// You can import js, ts, css, sass, ...
|
||||||
|
// #enddocregion
|
13
aio/content/examples/webpack/ts/tsconfig.1.json
Normal file
13
aio/content/examples/webpack/ts/tsconfig.1.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"lib": ["es2015", "dom"],
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"suppressImplicitAnyIndexErrors": true
|
||||||
|
}
|
||||||
|
}
|
6
aio/database.rules.json
Normal file
6
aio/database.rules.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
".read": "auth != null",
|
||||||
|
".write": "auth != null"
|
||||||
|
}
|
||||||
|
}
|
22
aio/e2e/app.e2e-spec.ts
Normal file
22
aio/e2e/app.e2e-spec.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { SitePage } from './app.po';
|
||||||
|
|
||||||
|
describe('site App', function() {
|
||||||
|
let page: SitePage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new SitePage();
|
||||||
|
page.navigateTo();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show features text after clicking "Features"', () => {
|
||||||
|
page.featureLink.click().then(() => {
|
||||||
|
expect(page.getDocViewerText()).toContain('Progressive web apps');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert code-example in pipe.html', () => {
|
||||||
|
page.datePipeLink.click().then(() => {
|
||||||
|
expect(page.codeExample.count()).toBeGreaterThan(0, 'should have code-example content');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
19
aio/e2e/app.po.ts
Normal file
19
aio/e2e/app.po.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
export class SitePage {
|
||||||
|
|
||||||
|
links = element.all(by.css('md-toolbar a'));
|
||||||
|
datePipeLink = element(by.css('md-toolbar a[aioNavLink="api/common/date-pipe"]'));
|
||||||
|
docViewer = element(by.css('aio-doc-viewer'));
|
||||||
|
codeExample = element.all(by.css('aio-doc-viewer pre > code'));
|
||||||
|
featureLink = element(by.css('md-toolbar a[aioNavLink="features"]'));
|
||||||
|
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDocViewerText() {
|
||||||
|
return this.docViewer.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
aio/e2e/tsconfig.json
Normal file
16
aio/e2e/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": false,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "../dist/out-tsc-e2e",
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es5",
|
||||||
|
"typeRoots": [
|
||||||
|
"../node_modules/@types"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
9
aio/firebase.json
Normal file
9
aio/firebase.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"rules": "database.rules.json"
|
||||||
|
},
|
||||||
|
"hosting": {
|
||||||
|
"public": "dist",
|
||||||
|
"cleanUrls": true
|
||||||
|
}
|
||||||
|
}
|
34
aio/gulpfile.js
Normal file
34
aio/gulpfile.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||||
|
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||||
|
// NOTE: we are getting the value from the parent `angular/angular` package.json not the `/aio` one.
|
||||||
|
const engines = require('../package.json').engines;
|
||||||
|
require('../tools/check-environment')({
|
||||||
|
requiredNpmVersion: engines.npm,
|
||||||
|
requiredNodeVersion: engines.node
|
||||||
|
});
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
|
||||||
|
// See `tools/gulp-tasks/README.md` for information about task loading.
|
||||||
|
function loadTask(fileName, taskName) {
|
||||||
|
const taskModule = require('./build/' + fileName);
|
||||||
|
const task = taskName ? taskModule[taskName] : taskModule;
|
||||||
|
return task(gulp);
|
||||||
|
}
|
||||||
|
|
||||||
|
gulp.task('docs', ['doc-gen', 'docs-app']);
|
||||||
|
gulp.task('doc-gen', loadTask('docs', 'generate'));
|
||||||
|
gulp.task('doc-gen-test', loadTask('docs', 'test'));
|
||||||
|
gulp.task('docs-app', loadTask('docs-app'));
|
||||||
|
gulp.task('docs-app-test', () => {});
|
||||||
|
gulp.task('docs-test', ['doc-gen-test', 'docs-app-test']);
|
42
aio/karma.conf.js
Normal file
42
aio/karma.conf.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '',
|
||||||
|
frameworks: ['jasmine', '@angular/cli'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-remap-istanbul'),
|
||||||
|
require('@angular/cli/plugins/karma')
|
||||||
|
],
|
||||||
|
files: [
|
||||||
|
{ pattern: './src/test.ts', watched: false }
|
||||||
|
],
|
||||||
|
preprocessors: {
|
||||||
|
'./src/test.ts': ['@angular/cli']
|
||||||
|
},
|
||||||
|
mime: {
|
||||||
|
'text/x-typescript': ['ts','tsx']
|
||||||
|
},
|
||||||
|
remapIstanbulReporter: {
|
||||||
|
reports: {
|
||||||
|
html: 'coverage',
|
||||||
|
lcovonly: './coverage/coverage.lcov'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
angularCli: {
|
||||||
|
config: './angular-cli.json',
|
||||||
|
environment: 'dev'
|
||||||
|
},
|
||||||
|
reporters: config.angularCli && config.angularCli.codeCoverage
|
||||||
|
? ['progress', 'karma-remap-istanbul']
|
||||||
|
: ['progress'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false
|
||||||
|
});
|
||||||
|
};
|
63
aio/package.json
Normal file
63
aio/package.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"name": "angular.io",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": "git@github.com:angular/angular.git",
|
||||||
|
"author": "Angular",
|
||||||
|
"license": "MIT",
|
||||||
|
"angular-cli": {},
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build",
|
||||||
|
"lint": "tslint \"src/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check",
|
||||||
|
"test": "ng test",
|
||||||
|
"pree2e": "webdriver-manager update --standalone false --gecko false",
|
||||||
|
"e2e": "protractor",
|
||||||
|
"deploy-staging": "firebase use staging --token \"$FIREBASE_TOKEN\" && yarn run ~~deploy",
|
||||||
|
"pre~~deploy": "ng build --prod",
|
||||||
|
"~~deploy": "firebase deploy --message \"Commit: $TRAVIS_COMMIT\" --non-interactive --token \"$FIREBASE_TOKEN\""
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/cli": "^1.0.0-beta.31",
|
||||||
|
"@angular/common": "^2.4.7",
|
||||||
|
"@angular/compiler": "^2.4.7",
|
||||||
|
"@angular/core": "^2.4.7",
|
||||||
|
"@angular/forms": "^2.4.7",
|
||||||
|
"@angular/http": "^2.4.7",
|
||||||
|
"@angular/material": "^2.0.0-beta.1",
|
||||||
|
"@angular/platform-browser": "^2.4.7",
|
||||||
|
"@angular/platform-browser-dynamic": "^2.4.7",
|
||||||
|
"@angular/router": "^3.4.7",
|
||||||
|
"core-js": "^2.4.1",
|
||||||
|
"rxjs": "^5.0.1",
|
||||||
|
"ts-helpers": "^1.1.1",
|
||||||
|
"zone.js": "^0.7.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular/compiler-cli": "^2.4.7",
|
||||||
|
"@types/jasmine": "2.5.38",
|
||||||
|
"@types/node": "^6.0.42",
|
||||||
|
"canonical-path": "^0.0.2",
|
||||||
|
"codelyzer": "~2.0.0-beta.1",
|
||||||
|
"dgeni": "^0.4.4",
|
||||||
|
"dgeni-packages": "^0.16.6",
|
||||||
|
"entities": "^1.1.1",
|
||||||
|
"firebase-tools": "^3.2.1",
|
||||||
|
"gulp": "^3.9.1",
|
||||||
|
"jasmine-core": "2.5.2",
|
||||||
|
"jasmine-spec-reporter": "2.5.0",
|
||||||
|
"karma": "1.2.0",
|
||||||
|
"karma-chrome-launcher": "^2.0.0",
|
||||||
|
"karma-cli": "^1.0.1",
|
||||||
|
"karma-jasmine": "^1.0.2",
|
||||||
|
"karma-remap-istanbul": "^0.2.1",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"protractor": "~4.0.13",
|
||||||
|
"rho": "^0.3.0",
|
||||||
|
"ts-node": "1.2.1",
|
||||||
|
"tslint": "^4.3.0",
|
||||||
|
"typescript": "2.0.10"
|
||||||
|
}
|
||||||
|
}
|
36
aio/protractor.conf.js
Normal file
36
aio/protractor.conf.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Protractor configuration file, see link for more information
|
||||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||||
|
|
||||||
|
/*global jasmine */
|
||||||
|
var SpecReporter = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
getPageTimeout: 30000,
|
||||||
|
specs: [
|
||||||
|
'./e2e/**/*.e2e-spec.ts'
|
||||||
|
],
|
||||||
|
capabilities: {
|
||||||
|
browserName: 'chrome',
|
||||||
|
chromeOptions: {
|
||||||
|
binary: process.env.CHROME_BIN
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directConnect: true,
|
||||||
|
baseUrl: 'http://localhost:4200/',
|
||||||
|
framework: 'jasmine',
|
||||||
|
jasmineNodeOpts: {
|
||||||
|
showColors: true,
|
||||||
|
defaultTimeoutInterval: 30000,
|
||||||
|
print: function() {}
|
||||||
|
},
|
||||||
|
useAllAngular2AppRoots: true,
|
||||||
|
beforeLaunch: function() {
|
||||||
|
require('ts-node').register({
|
||||||
|
project: 'e2e'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onPrepare: function() {
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter());
|
||||||
|
}
|
||||||
|
};
|
11
aio/src/app/app.component.html
Normal file
11
aio/src/app/app.component.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<md-toolbar color="primary" class="app-toolbar">
|
||||||
|
<button *ngIf="isHamburgerVisible" class="hamburger" md-button (click)="toggleSideNav()"><md-icon>menu</md-icon></button>
|
||||||
|
<aio-menu></aio-menu>
|
||||||
|
<md-input-container >
|
||||||
|
<input #search md-input placeholder="Search">
|
||||||
|
</md-input-container>
|
||||||
|
<span class="fill-remaining-space"></span>
|
||||||
|
</md-toolbar>
|
||||||
|
|
||||||
|
<aio-sidenav></aio-sidenav>
|
||||||
|
|
22
aio/src/app/app.component.scss
Normal file
22
aio/src/app/app.component.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.fill-remaining-space {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
md-input-container {
|
||||||
|
margin-left: 10px;
|
||||||
|
input {
|
||||||
|
min-width:200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.md-input-element {
|
||||||
|
font-size: 70%;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
aio-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
29
aio/src/app/app.component.spec.ts
Normal file
29
aio/src/app/app.component.spec.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
import { NavEngine } from './nav-engine/nav-engine.service';
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
let component: AppComponent;
|
||||||
|
let fixture: ComponentFixture<AppComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [ AppModule ],
|
||||||
|
providers: [
|
||||||
|
{ provide: NavEngine, useValue: { currentDoc: undefined } }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
TestBed.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AppComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
16
aio/src/app/app.component.ts
Normal file
16
aio/src/app/app.component.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Component, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
|
import { SidenavComponent } from './sidenav/sidenav.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-shell',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.scss']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
isHamburgerVisible = true; // always ... for now
|
||||||
|
|
||||||
|
@ViewChild(SidenavComponent) sidenav: SidenavComponent;
|
||||||
|
|
||||||
|
toggleSideNav() { this.sidenav.toggle(); }
|
||||||
|
}
|
54
aio/src/app/app.module.ts
Normal file
54
aio/src/app/app.module.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { HttpModule } from '@angular/http';
|
||||||
|
|
||||||
|
import { MdToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MdButtonModule} from '@angular/material/button';
|
||||||
|
import { MdIconModule} from '@angular/material/icon';
|
||||||
|
import { MdInputModule } from '@angular/material/input';
|
||||||
|
import { MdSidenavModule } from '@angular/material/sidenav';
|
||||||
|
import { Platform } from '@angular/material/core';
|
||||||
|
|
||||||
|
// Temporary fix for MdSidenavModule issue:
|
||||||
|
// crashes with "missing first" operator when SideNav.mode is "over"
|
||||||
|
import 'rxjs/add/operator/first';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { DocViewerComponent } from './doc-viewer/doc-viewer.component';
|
||||||
|
import { embeddedComponents, EmbeddedComponents } from './embedded';
|
||||||
|
import { Logger } from './logger.service';
|
||||||
|
import { navDirectives, navProviders } from './nav-engine';
|
||||||
|
import { SidenavComponent } from './sidenav/sidenav.component';
|
||||||
|
import { NavItemComponent } from './sidenav/nav-item.component';
|
||||||
|
import { MenuComponent } from './sidenav/menu.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
HttpModule,
|
||||||
|
MdButtonModule.forRoot(),
|
||||||
|
MdIconModule.forRoot(),
|
||||||
|
MdInputModule.forRoot(),
|
||||||
|
MdToolbarModule.forRoot(),
|
||||||
|
MdSidenavModule.forRoot()
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
embeddedComponents,
|
||||||
|
DocViewerComponent,
|
||||||
|
MenuComponent,
|
||||||
|
navDirectives,
|
||||||
|
NavItemComponent,
|
||||||
|
SidenavComponent,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
EmbeddedComponents,
|
||||||
|
Logger,
|
||||||
|
navProviders,
|
||||||
|
Platform
|
||||||
|
],
|
||||||
|
entryComponents: [ embeddedComponents ],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule {
|
||||||
|
}
|
306
aio/src/app/doc-viewer/doc-viewer.component.spec.ts
Normal file
306
aio/src/app/doc-viewer/doc-viewer.component.spec.ts
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { Component, DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ComponentFactoryResolver, ElementRef, Injector, NgModule, OnInit, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
|
import { Doc, DocMetadata } from '../nav-engine';
|
||||||
|
import { DocViewerComponent } from '../doc-viewer/doc-viewer.component';
|
||||||
|
|
||||||
|
import { embeddedComponents, EmbeddedComponents } from '../embedded';
|
||||||
|
|
||||||
|
|
||||||
|
/// Embedded Test Components ///
|
||||||
|
|
||||||
|
///// FooComponent /////
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-foo',
|
||||||
|
template: `Foo Component`
|
||||||
|
})
|
||||||
|
class FooComponent { }
|
||||||
|
|
||||||
|
///// BarComponent /////
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-bar',
|
||||||
|
template: `
|
||||||
|
<hr>
|
||||||
|
<h2>Bar Component</h2>
|
||||||
|
<p #barContent></p>
|
||||||
|
<hr>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class BarComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild('barContent') barContentRef: ElementRef;
|
||||||
|
|
||||||
|
constructor(public elementRef: ElementRef) { }
|
||||||
|
|
||||||
|
// Project content in ngOnInit just like CodeExampleComponent
|
||||||
|
ngOnInit() {
|
||||||
|
// Security: this is a test component; never deployed
|
||||||
|
this.barContentRef.nativeElement.innerHTML = this.elementRef.nativeElement.aioBarContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///// BazComponent /////
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-baz',
|
||||||
|
template: `
|
||||||
|
<div>++++++++++++++</div>
|
||||||
|
<h2>Baz Component</h2>
|
||||||
|
<p #bazContent></p>
|
||||||
|
<div>++++++++++++++</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class BazComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild('bazContent') bazContentRef: ElementRef;
|
||||||
|
|
||||||
|
constructor(public elementRef: ElementRef) { }
|
||||||
|
|
||||||
|
// Project content in ngOnInit just like CodeExampleComponent
|
||||||
|
ngOnInit() {
|
||||||
|
// Security: this is a test component; never deployed
|
||||||
|
this.bazContentRef.nativeElement.innerHTML = this.elementRef.nativeElement.aioBazContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///// Test Module //////
|
||||||
|
|
||||||
|
const embeddedTestComponents = [FooComponent, BarComponent, BazComponent, ...embeddedComponents];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
entryComponents: embeddedTestComponents
|
||||||
|
})
|
||||||
|
class TestModule { }
|
||||||
|
|
||||||
|
//// Test Component //////
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-test',
|
||||||
|
template: `
|
||||||
|
<aio-doc-viewer>Test Component</aio-doc-viewer>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
private currentDoc: Doc;
|
||||||
|
|
||||||
|
@ViewChild(DocViewerComponent) docViewer: DocViewerComponent;
|
||||||
|
|
||||||
|
setDoc(doc: Doc) {
|
||||||
|
if (this.docViewer) {
|
||||||
|
this.docViewer.doc = doc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////// Tests //////////////
|
||||||
|
|
||||||
|
describe('DocViewerComponent', () => {
|
||||||
|
const fakeDocMetadata: DocMetadata = { docId: 'fake', title: 'fake Doc' };
|
||||||
|
let component: TestComponent;
|
||||||
|
let docViewerDE: DebugElement;
|
||||||
|
let docViewerEl: HTMLElement;
|
||||||
|
let fixture: ComponentFixture<TestComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [ TestModule ],
|
||||||
|
declarations: [
|
||||||
|
TestComponent,
|
||||||
|
DocViewerComponent,
|
||||||
|
embeddedTestComponents
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{provide: EmbeddedComponents, useValue: {components: embeddedTestComponents}}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TestComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
docViewerDE = fixture.debugElement.children[0];
|
||||||
|
docViewerEl = docViewerDE.nativeElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a DocViewer', () => {
|
||||||
|
expect(component.docViewer).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should display nothing when set DocViewer.doc to doc w/o content'), () => {
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content: '' };
|
||||||
|
expect(docViewerEl.innerHTML).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should display simple static content doc'), () => {
|
||||||
|
const content = '<p>Howdy, doc viewer</p>';
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
expect(docViewerEl.innerHTML).toEqual(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should display nothing after reset static content doc'), () => {
|
||||||
|
const content = '<p>Howdy, doc viewer</p>';
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content: '' };
|
||||||
|
expect(docViewerEl.innerHTML).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should apply FooComponent'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Above Foo</p>
|
||||||
|
<p><aio-foo></aio-foo></p>
|
||||||
|
<p>Below Foo</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
const fooHtml = docViewerEl.querySelector('aio-foo').innerHTML;
|
||||||
|
expect(fooHtml).toContain('Foo Component');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should apply multiple FooComponents'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Above Foo</p>
|
||||||
|
<p><aio-foo></aio-foo></p>
|
||||||
|
<div style="margin-left: 2em;">
|
||||||
|
Holds a
|
||||||
|
<aio-foo>Ignored text</aio-foo>
|
||||||
|
</div>
|
||||||
|
<p>Below Foo</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
const foos = docViewerEl.querySelectorAll('aio-foo');
|
||||||
|
expect(foos.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should apply BarComponent'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Above Bar</p>
|
||||||
|
<aio-bar></aio-bar>
|
||||||
|
<p>Below Bar</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
const barHtml = docViewerEl.querySelector('aio-bar').innerHTML;
|
||||||
|
expect(barHtml).toContain('Bar Component');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should project bar content into BarComponent'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Above Bar</p>
|
||||||
|
<aio-bar>###bar content###</aio-bar>
|
||||||
|
<p>Below Bar</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
|
||||||
|
// necessary to trigger projection within ngOnInit
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const barHtml = docViewerEl.querySelector('aio-bar').innerHTML;
|
||||||
|
expect(barHtml).toContain('###bar content###');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it(('should include Foo and Bar'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Top</p>
|
||||||
|
<p><aio-foo>ignored</aio-foo></p>
|
||||||
|
<aio-bar>###bar content###</aio-bar>
|
||||||
|
<p><aio-foo></aio-foo></p>
|
||||||
|
<p>Bottom</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
|
||||||
|
// necessary to trigger Bar's projection within ngOnInit
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const foos = docViewerEl.querySelectorAll('aio-foo');
|
||||||
|
expect(foos.length).toBe(2, 'should have 2 foos');
|
||||||
|
|
||||||
|
const barHtml = docViewerEl.querySelector('aio-bar').innerHTML;
|
||||||
|
expect(barHtml).toContain('###bar content###', 'should have bar with projected content');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(('should not include Bar within Foo'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Top</p>
|
||||||
|
<div>
|
||||||
|
<aio-foo>
|
||||||
|
<aio-bar>###bar content###</aio-bar>
|
||||||
|
</aio-foo>
|
||||||
|
</div>
|
||||||
|
<p><aio-foo></aio-foo><p>
|
||||||
|
<p>Bottom</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
|
||||||
|
// necessary to trigger Bar's projection within ngOnInit
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const foos = docViewerEl.querySelectorAll('aio-foo');
|
||||||
|
expect(foos.length).toBe(2, 'should have 2 foos');
|
||||||
|
|
||||||
|
const bars = docViewerEl.querySelectorAll('aio-bar');
|
||||||
|
expect(bars.length).toBe(0, 'did not expect Bar inside Foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
// because FooComponents are processed before BazComponents
|
||||||
|
it(('should include Foo within Bar'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Top</p>
|
||||||
|
<aio-bar>
|
||||||
|
<div style="margin-left: 2em">
|
||||||
|
Inner <aio-foo></aio-foo>
|
||||||
|
</div>
|
||||||
|
</aio-bar>
|
||||||
|
<p><aio-foo></aio-foo></p>
|
||||||
|
<p>Bottom</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
|
||||||
|
// necessary to trigger Bar's projection within ngOnInit
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const foos = docViewerEl.querySelectorAll('aio-foo');
|
||||||
|
expect(foos.length).toBe(2, 'should have 2 foos');
|
||||||
|
|
||||||
|
const bars = docViewerEl.querySelectorAll('aio-bar');
|
||||||
|
expect(bars.length).toBe(1, 'should have a bar');
|
||||||
|
expect(bars[0].innerHTML).toContain('Bar Component', 'should have bar template content');
|
||||||
|
});
|
||||||
|
|
||||||
|
// The <aio-baz> tag and its inner content is copied
|
||||||
|
// But the BazComponent is not created and therefore its template content is not displayed
|
||||||
|
// because BarComponents are processed before BazComponents
|
||||||
|
// and no chance for first Baz inside Bar to be processed by builder.
|
||||||
|
it(('should NOT include Bar within Baz'), () => {
|
||||||
|
const content = `
|
||||||
|
<p>Top</p>
|
||||||
|
<aio-bar>
|
||||||
|
<div style="margin-left: 2em">
|
||||||
|
Inner <aio-baz>---baz stuff---</aio-baz>
|
||||||
|
</div>
|
||||||
|
</aio-bar>
|
||||||
|
<p><aio-baz>---More baz--</aio-baz></p>
|
||||||
|
<p>Bottom</p>
|
||||||
|
`;
|
||||||
|
component.docViewer.doc = { metadata: fakeDocMetadata, content };
|
||||||
|
|
||||||
|
// necessary to trigger Bar's projection within ngOnInit
|
||||||
|
fixture.detectChanges();
|
||||||
|
const bazs = docViewerEl.querySelectorAll('aio-baz');
|
||||||
|
|
||||||
|
// Both baz tags are there ...
|
||||||
|
expect(bazs.length).toBe(2, 'should have 2 bazs');
|
||||||
|
|
||||||
|
expect(bazs[0].innerHTML).not.toContain('Baz Component',
|
||||||
|
'did not expect 1st Baz template content');
|
||||||
|
|
||||||
|
expect(bazs[1].innerHTML).toContain('Baz Component',
|
||||||
|
'expected 2nd Baz template content');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
139
aio/src/app/doc-viewer/doc-viewer.component.ts
Normal file
139
aio/src/app/doc-viewer/doc-viewer.component.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import {
|
||||||
|
Component, ComponentFactory, ComponentFactoryResolver, ComponentRef,
|
||||||
|
DoCheck, ElementRef, Injector, Input, OnDestroy, ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Doc, DocMetadata, DocMetadataService, NavNode } from '../nav-engine';
|
||||||
|
import { EmbeddedComponents } from '../embedded';
|
||||||
|
|
||||||
|
interface EmbeddedComponentFactory {
|
||||||
|
contentPropertyName: string;
|
||||||
|
factory: ComponentFactory<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization prevents flicker once pre-rendering is on
|
||||||
|
const initialDocViewerElement = document.querySelector('aio-doc-viewer');
|
||||||
|
const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElement.innerHTML : '';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-doc-viewer',
|
||||||
|
template: '',
|
||||||
|
providers: [ DocMetadataService ],
|
||||||
|
styles: [ `
|
||||||
|
:host >>> doc-title.not-found h1 {
|
||||||
|
color: white;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
`]
|
||||||
|
// TODO(robwormald): shadow DOM and emulated don't work here (?!)
|
||||||
|
// encapsulation: ViewEncapsulation.Native
|
||||||
|
})
|
||||||
|
export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
|
|
||||||
|
private displayedDoc: DisplayedDoc;
|
||||||
|
private embeddedComponentFactories: Map<string, EmbeddedComponentFactory> = new Map();
|
||||||
|
private hostElement: HTMLElement;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
componentFactoryResolver: ComponentFactoryResolver,
|
||||||
|
elementRef: ElementRef,
|
||||||
|
embeddedComponents: EmbeddedComponents,
|
||||||
|
private docMetadataService: DocMetadataService,
|
||||||
|
private injector: Injector
|
||||||
|
) {
|
||||||
|
this.hostElement = elementRef.nativeElement;
|
||||||
|
// Security: the initialDocViewerContent comes from the prerendered DOM and is considered to be secure
|
||||||
|
this.hostElement.innerHTML = initialDocViewerContent;
|
||||||
|
|
||||||
|
for (const component of embeddedComponents.components) {
|
||||||
|
const factory = componentFactoryResolver.resolveComponentFactory(component);
|
||||||
|
const selector = factory.selector;
|
||||||
|
const contentPropertyName = this.selectorToContentPropertyName(selector);
|
||||||
|
this.embeddedComponentFactories.set(selector, { contentPropertyName, factory });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set doc(newDoc: Doc) {
|
||||||
|
this.ngOnDestroy();
|
||||||
|
if (newDoc) {
|
||||||
|
this.docMetadataService.metadata = newDoc.metadata;
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
this.build(newDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add doc content to host element and build it out with embedded components
|
||||||
|
*/
|
||||||
|
private build(doc: Doc) {
|
||||||
|
|
||||||
|
const displayedDoc = this.displayedDoc = new DisplayedDoc(doc);
|
||||||
|
|
||||||
|
// security: the doc.content is always authored by the documentation team
|
||||||
|
// and is considered to be safe
|
||||||
|
this.hostElement.innerHTML = doc.content || '';
|
||||||
|
|
||||||
|
if (!doc.content) { return; }
|
||||||
|
|
||||||
|
// TODO(i): why can't I use for-of? why doesn't typescript like Map#value() iterators?
|
||||||
|
this.embeddedComponentFactories.forEach(({ contentPropertyName, factory }, selector) => {
|
||||||
|
const embeddedComponentElements = this.hostElement.querySelectorAll(selector);
|
||||||
|
|
||||||
|
// cast due to https://github.com/Microsoft/TypeScript/issues/4947
|
||||||
|
for (const element of embeddedComponentElements as any as HTMLElement[]){
|
||||||
|
// hack: preserve the current element content because the factory will empty it out
|
||||||
|
// security: the source of this innerHTML is always authored by the documentation team
|
||||||
|
// and is considered to be safe
|
||||||
|
element[contentPropertyName] = element.innerHTML;
|
||||||
|
displayedDoc.addEmbeddedComponent(factory.create(this.injector, [], element));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngDoCheck() {
|
||||||
|
if (this.displayedDoc) { this.displayedDoc.detectChanges(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// destroy components otherwise there will be memory leaks
|
||||||
|
if (this.displayedDoc) {
|
||||||
|
this.displayedDoc.destroy();
|
||||||
|
this.displayedDoc = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the component content property name by converting the selector to camelCase and appending
|
||||||
|
* 'Content', e.g. live-example => liveExampleContent
|
||||||
|
*/
|
||||||
|
private selectorToContentPropertyName(selector: string) {
|
||||||
|
return selector.replace(/-(.)/g, (match, $1) => $1.toUpperCase()) + 'Content';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DisplayedDoc {
|
||||||
|
|
||||||
|
metadata: DocMetadata;
|
||||||
|
|
||||||
|
private embeddedComponents: ComponentRef<any>[] = [];
|
||||||
|
|
||||||
|
constructor(doc: Doc) {
|
||||||
|
// ignore doc.content ... don't need to keep it around
|
||||||
|
this.metadata = doc.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
addEmbeddedComponent(component: ComponentRef<any>) {
|
||||||
|
this.embeddedComponents.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
detectChanges() {
|
||||||
|
this.embeddedComponents.forEach(comp => comp.changeDetectorRef.detectChanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
// destroy components otherwise there will be memory leaks
|
||||||
|
this.embeddedComponents.forEach(comp => comp.destroy());
|
||||||
|
this.embeddedComponents.length = 0;
|
||||||
|
}
|
||||||
|
}
|
28
aio/src/app/embedded/code-example.component.spec.ts
Normal file
28
aio/src/app/embedded/code-example.component.spec.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { CodeExampleComponent } from './code-example.component';
|
||||||
|
|
||||||
|
describe('CodeExampleComponent', () => {
|
||||||
|
let component: CodeExampleComponent;
|
||||||
|
let fixture: ComponentFixture<CodeExampleComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CodeExampleComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CodeExampleComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
73
aio/src/app/embedded/code-example.component.ts
Normal file
73
aio/src/app/embedded/code-example.component.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* tslint:disable component-selector */
|
||||||
|
|
||||||
|
import { Component, OnInit, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
|
||||||
|
|
||||||
|
// TODO(i): add clipboard copy functionality
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Angular.io Code Example
|
||||||
|
*
|
||||||
|
* Pretty renders a code block, primarily used in the docs and API reference. Can be used within an Angular app, or
|
||||||
|
* independently, provided that it is dynamically generated by the component resolver.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* <code-example [language]="..." [escape]="..." [format]="..." [showcase]="..." [animated]="...">
|
||||||
|
* console.log('Hello World')
|
||||||
|
* </code-example>
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'code-example',
|
||||||
|
template: '<pre class="{{classes}}"><code class="{{animatedClasses}}" #codeContainer></code></pre>'
|
||||||
|
})
|
||||||
|
export class CodeExampleComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
|
@ViewChild('codeContainer') codeContainerRef: ElementRef;
|
||||||
|
|
||||||
|
language: string; // could be javascript, dart, typescript
|
||||||
|
// TODO(i): escape doesn't seem to be currently supported in the original code
|
||||||
|
escape: string; // could be 'html'
|
||||||
|
format: string; // some css class
|
||||||
|
showcase: string; // a string with the value 'true'
|
||||||
|
animated = false;
|
||||||
|
|
||||||
|
// TODO(i): could we use @HostBinding instead or does the CSS have to be scoped to <pre> and <code>
|
||||||
|
classes: string;
|
||||||
|
animatedClasses: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private elementRef: ElementRef) {
|
||||||
|
// TODO(i): @Input should be supported for host elements and should just do a one off initialization of properties
|
||||||
|
// from the host element => talk to Tobias
|
||||||
|
['language', 'escape', 'format', 'showcase', 'animated'].forEach(inputName => {
|
||||||
|
if (!this[inputName]) {
|
||||||
|
this[inputName] = this.elementRef.nativeElement.getAttribute(inputName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
const showcaseClass = this.showcase === 'true' ? ' is-showcase' : '';
|
||||||
|
this.classes = `
|
||||||
|
prettyprint
|
||||||
|
${this.format ? this.format : ''}
|
||||||
|
${this.language ? 'lang-' + this.language : '' }
|
||||||
|
${showcaseClass ? showcaseClass : ''}
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
this.animatedClasses = `${this.animated ? 'animated fadeIn' : ''}`;
|
||||||
|
|
||||||
|
// Security: the codeExampleContent is the original innerHTML of the host element provided by
|
||||||
|
// docs authors and as such its considered to be safe for innerHTML purposes
|
||||||
|
this.codeContainerRef.nativeElement.innerHTML = this.elementRef.nativeElement.codeExampleContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// TODO(i): import prettify.js from this file so that we don't need to preload it via index.html
|
||||||
|
// whenever a code example is used, use syntax highlighting.
|
||||||
|
// if(prettyPrint) {
|
||||||
|
// prettyPrint();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
14
aio/src/app/embedded/doc-title.component.ts
Normal file
14
aio/src/app/embedded/doc-title.component.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* tslint:disable component-selector */
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { DocMetadataService } from '../nav-engine';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'doc-title',
|
||||||
|
template: '<h1 class="docs-primary-header">{{title}}</h1>'
|
||||||
|
})
|
||||||
|
export class DocTitleComponent {
|
||||||
|
title: string;
|
||||||
|
constructor(metadataService: DocMetadataService) {
|
||||||
|
this.title = metadataService.metadata.title;
|
||||||
|
}
|
||||||
|
}
|
12
aio/src/app/embedded/index.ts
Normal file
12
aio/src/app/embedded/index.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { CodeExampleComponent } from './code-example.component';
|
||||||
|
import { DocTitleComponent } from './doc-title.component';
|
||||||
|
|
||||||
|
/** Components that can be embedded in docs such as CodeExampleComponent, LiveExampleComponent,... */
|
||||||
|
export const embeddedComponents: any[] = [
|
||||||
|
CodeExampleComponent, DocTitleComponent
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Injectable class w/ property returning components that can be embedded in docs */
|
||||||
|
export class EmbeddedComponents {
|
||||||
|
components = embeddedComponents;
|
||||||
|
}
|
17
aio/src/app/logger.service.ts
Normal file
17
aio/src/app/logger.service.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Logger {
|
||||||
|
|
||||||
|
log(value: any, ...rest) {
|
||||||
|
console.log(value, ...rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(value: any, ...rest) {
|
||||||
|
console.error(value, ...rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(value: any, ...rest) {
|
||||||
|
console.warn(value, ...rest);
|
||||||
|
}
|
||||||
|
}
|
2
aio/src/app/nav-engine/doc-fetching.service.spec.ts
Normal file
2
aio/src/app/nav-engine/doc-fetching.service.spec.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { DocFetchingService } from './doc-fetching.service';
|
||||||
|
// Write tests when/if this service is retained.
|
80
aio/src/app/nav-engine/doc-fetching.service.ts
Normal file
80
aio/src/app/nav-engine/doc-fetching.service.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { Http, Response } from '@angular/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import 'rxjs/add/operator/catch';
|
||||||
|
import 'rxjs/add/operator/do';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
|
||||||
|
import { Doc, DocMetadata } from './doc.model';
|
||||||
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DocFetchingService {
|
||||||
|
|
||||||
|
private base = 'content/';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: Http,
|
||||||
|
private logger: Logger) { }
|
||||||
|
|
||||||
|
getPath(docId: string) {
|
||||||
|
return this.base + addPathExtension(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch document from server.
|
||||||
|
* NB: pass 404 response to caller as Doc with empty string content
|
||||||
|
* Other errors and non-OK status responses are thrown errors.
|
||||||
|
* TODO: add timeout and retry for lost connection
|
||||||
|
*/
|
||||||
|
getDocFile(docId: string): Observable<Doc> {
|
||||||
|
|
||||||
|
if (!docId) {
|
||||||
|
const emsg = 'getFile: no document id';
|
||||||
|
this.logger.error(emsg);
|
||||||
|
throw new Error(emsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Metadata will be updated from file
|
||||||
|
const metadata: DocMetadata = { docId, title: docId };
|
||||||
|
const url = this.getPath(docId);
|
||||||
|
|
||||||
|
this.logger.log(`Fetching document file at '${url}'`);
|
||||||
|
|
||||||
|
return this.http.get(url)
|
||||||
|
.map(res => <Doc> { metadata, content: res.text() }) // TODO: It will come as JSON soon
|
||||||
|
.do(content => this.logger.log(`Fetched document file at '${url}'`) )
|
||||||
|
.catch((error: Response) => {
|
||||||
|
if (error.status === 404) {
|
||||||
|
this.logger.error(`Document file not found at '${url}'`);
|
||||||
|
return of({metadata, content: ''} as Doc);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPathExtension(path: string) {
|
||||||
|
if (path) {
|
||||||
|
if (path.endsWith('/')) {
|
||||||
|
return path + 'index.html';
|
||||||
|
} else if (!path.endsWith('.html')) {
|
||||||
|
return path + '.html';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function removePathExtension(path: string) {
|
||||||
|
// if (path) {
|
||||||
|
// if (path.endsWith('/index.html')) {
|
||||||
|
// return path.substring(0, path.length - 10);
|
||||||
|
// } else if (path.endsWith('.html')) {
|
||||||
|
// return path.substring(0, path.length - 5);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return path;
|
||||||
|
// }
|
7
aio/src/app/nav-engine/doc-metadata.service.ts
Normal file
7
aio/src/app/nav-engine/doc-metadata.service.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { DocMetadata } from './doc.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DocMetadataService {
|
||||||
|
metadata: DocMetadata;
|
||||||
|
}
|
54
aio/src/app/nav-engine/doc.model.ts
Normal file
54
aio/src/app/nav-engine/doc.model.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
export interface DocMetadata {
|
||||||
|
docId: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Doc {
|
||||||
|
metadata: DocMetadata;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI navigation node that describes a document or container of documents
|
||||||
|
* Each node corresponds to a link in the UI.
|
||||||
|
*/
|
||||||
|
export interface NavNode {
|
||||||
|
/** unique integer id for this node */
|
||||||
|
id: number;
|
||||||
|
/** Document id if this is a document node */
|
||||||
|
docId: string;
|
||||||
|
/** Document path (calculated from docId) if this is a document node */
|
||||||
|
docPath: string;
|
||||||
|
/** url to an external web page; docPath and url are mutually exclusive. */
|
||||||
|
url?: string;
|
||||||
|
/** Title to display in the navigation link; typically shorter */
|
||||||
|
navTitle: string;
|
||||||
|
/** Tooltip for links */
|
||||||
|
tooltip: string;
|
||||||
|
/** Ids of ancestor nodes higher in the hierarchy */
|
||||||
|
ancestorIds: number[];
|
||||||
|
/** true if should not be displayed in UI. Can still be loaded directly */
|
||||||
|
// hide?: boolean; NO NO. If the JSON says, hide, simply omit it from this map
|
||||||
|
/** If defined, this node is a container of child nodes */
|
||||||
|
children?: NavNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation for the site.
|
||||||
|
* - nodes: the top-level navigation nodes; node can have children.
|
||||||
|
* - docs: find node for a given document id.
|
||||||
|
*/
|
||||||
|
export interface NavMap {
|
||||||
|
/**
|
||||||
|
* Map that drives the UI navigation.
|
||||||
|
* Each node correspond to a navigation link in the UI.
|
||||||
|
* Supports unlimited node nesting.
|
||||||
|
*/
|
||||||
|
nodes: NavNode[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NavNode for a document id in the NavMap.
|
||||||
|
*/
|
||||||
|
docs: Map<string, NavNode>;
|
||||||
|
}
|
83
aio/src/app/nav-engine/doc.service.spec.ts
Normal file
83
aio/src/app/nav-engine/doc.service.spec.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { fakeAsync, tick } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DocService } from './doc.service';
|
||||||
|
import { Doc, DocMetadata, NavNode } from './doc.model';
|
||||||
|
import { DocFetchingService } from './doc-fetching.service';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import 'rxjs/add/observable/throw';
|
||||||
|
import 'rxjs/add/operator/catch';
|
||||||
|
import 'rxjs/add/operator/delay';
|
||||||
|
import 'rxjs/add/operator/first';
|
||||||
|
|
||||||
|
describe('DocService', () => {
|
||||||
|
let docFetchingService: DocFetchingService;
|
||||||
|
let getFileSpy: jasmine.Spy;
|
||||||
|
let loggerSpy: any;
|
||||||
|
let docService: DocService;
|
||||||
|
let testDoc: Doc;
|
||||||
|
let testDocId: string;
|
||||||
|
let testContent: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testDocId = 'fake';
|
||||||
|
testContent = 'fake file contents';
|
||||||
|
testDoc = {
|
||||||
|
metadata: {docId: testDocId, title: 'Fake Title'} as DocMetadata,
|
||||||
|
content: testContent
|
||||||
|
};
|
||||||
|
|
||||||
|
loggerSpy = jasmine.createSpyObj('logger', ['log', 'warn', 'error']);
|
||||||
|
docFetchingService = new DocFetchingService(null, loggerSpy);
|
||||||
|
getFileSpy = spyOn(docFetchingService, 'getDocFile').and
|
||||||
|
.returnValue(of(testDoc).delay(0).first()); // first() -> completes
|
||||||
|
|
||||||
|
docService = new DocService(docFetchingService, loggerSpy);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return fake doc for fake id', fakeAsync(() => {
|
||||||
|
docService.getDoc(testDocId).subscribe(doc =>
|
||||||
|
expect(doc.content).toBe(testContent)
|
||||||
|
);
|
||||||
|
tick();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should retrieve file once for first file request', fakeAsync(() => {
|
||||||
|
let doc: Doc;
|
||||||
|
expect(getFileSpy.calls.count()).toBe(0, 'no call before tick');
|
||||||
|
docService.getDoc(testDocId).subscribe(d => doc = d);
|
||||||
|
tick();
|
||||||
|
expect(getFileSpy.calls.count()).toBe(1, 'one call after tick');
|
||||||
|
expect(doc).toBe(testDoc, 'expected doc');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should retrieve file from cache the second time', fakeAsync(() => {
|
||||||
|
docService.getDoc(testDocId).subscribe(doc =>
|
||||||
|
expect(doc).toBe(testDoc, 'expected doc from server')
|
||||||
|
);
|
||||||
|
tick();
|
||||||
|
expect(getFileSpy.calls.count()).toBe(1, 'one call after 1st request');
|
||||||
|
|
||||||
|
docService.getDoc(testDocId).subscribe(doc =>
|
||||||
|
expect(doc).toBe(testDoc, 'expected doc from cache')
|
||||||
|
);
|
||||||
|
tick();
|
||||||
|
expect(getFileSpy.calls.count()).toBe(1, 'still only one call after 2nd request');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should pass along file error through its getDoc observable result', fakeAsync(() => {
|
||||||
|
|
||||||
|
const err = 'deliberate file error';
|
||||||
|
getFileSpy.and.returnValue(
|
||||||
|
// simulate async error in the file retrieval
|
||||||
|
of('').delay(0).map(() => { throw new Error(err); })
|
||||||
|
);
|
||||||
|
|
||||||
|
docService.getDoc(testDocId).subscribe(
|
||||||
|
doc => expect(false).toBe(true, 'should have failed'),
|
||||||
|
error => expect(error.message).toBe(err)
|
||||||
|
);
|
||||||
|
tick();
|
||||||
|
}));
|
||||||
|
});
|
57
aio/src/app/nav-engine/doc.service.ts
Normal file
57
aio/src/app/nav-engine/doc.service.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import 'rxjs/add/operator/do';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import 'rxjs/add/operator/switchMap';
|
||||||
|
|
||||||
|
import { Doc, NavNode } from './doc.model';
|
||||||
|
import { DocFetchingService } from './doc-fetching.service';
|
||||||
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
|
import { NavMapService } from './nav-map.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DocService {
|
||||||
|
private cache = new Map<string, Doc>();
|
||||||
|
private notFoundContent: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fileService: DocFetchingService,
|
||||||
|
private logger: Logger
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get document for id, from cache if found else server.
|
||||||
|
* Pass server errors along to caller
|
||||||
|
* Constructs and caches a "Not Found" doc when fileservice returns a doc with no content.
|
||||||
|
*/
|
||||||
|
getDoc(docId: string): Observable<Doc> {
|
||||||
|
const cached = this.cache.get(docId);
|
||||||
|
if (cached) {
|
||||||
|
this.logger.log(`Returned cached document for '${docId}'`);
|
||||||
|
return of(cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.fileService.getDocFile(docId)
|
||||||
|
.switchMap(doc => {
|
||||||
|
this.logger.log(`Fetched document for '${docId}'`);
|
||||||
|
return doc.content ? of(doc) :
|
||||||
|
this.getNotFound()
|
||||||
|
.map(nfContent => <Doc> {metadata: {docId, title: docId}, content: nfContent});
|
||||||
|
})
|
||||||
|
.do(doc => this.cache.set(docId, doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotFound(): Observable<string> {
|
||||||
|
if (this.notFoundContent) { return of(this.notFoundContent); }
|
||||||
|
const nfDocId = 'not-found';
|
||||||
|
return this.fileService.getDocFile(nfDocId)
|
||||||
|
.map(doc => {
|
||||||
|
this.logger.log(`Fetched "not found" document for '${nfDocId}'`);
|
||||||
|
this.notFoundContent = doc.content;
|
||||||
|
return doc.content;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
21
aio/src/app/nav-engine/index.ts
Normal file
21
aio/src/app/nav-engine/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { DocService } from './doc.service';
|
||||||
|
import { DocFetchingService } from './doc-fetching.service';
|
||||||
|
import { NavEngine } from './nav-engine.service';
|
||||||
|
import { NavLinkDirective } from './nav-link.directive';
|
||||||
|
import { NavMapService } from './nav-map.service';
|
||||||
|
|
||||||
|
export { Doc, DocMetadata, NavNode, NavMap } from './doc.model';
|
||||||
|
export { DocMetadataService } from './doc-metadata.service';
|
||||||
|
export { NavEngine } from './nav-engine.service';
|
||||||
|
export { NavMapService } from './nav-map.service';
|
||||||
|
|
||||||
|
export const navDirectives = [
|
||||||
|
NavLinkDirective
|
||||||
|
];
|
||||||
|
|
||||||
|
export const navProviders = [
|
||||||
|
DocService,
|
||||||
|
DocFetchingService,
|
||||||
|
NavEngine,
|
||||||
|
NavMapService,
|
||||||
|
];
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user