Compare commits
456 Commits
2.3.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 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -25,3 +25,8 @@ npm-debug.log
|
||||
|
||||
# rollup-test output
|
||||
/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
|
25
.travis.yml
25
.travis.yml
@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '6.6.0'
|
||||
- '6.9.5'
|
||||
|
||||
addons:
|
||||
# firefox: "38.0"
|
||||
@ -17,23 +17,31 @@ branches:
|
||||
- g3_v2_0
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- ./node_modules
|
||||
- ./.chrome/chromium
|
||||
- ./aio/node_modules
|
||||
|
||||
env:
|
||||
global:
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
# 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:
|
||||
# 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
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
- CI_MODE=saucelabs_optional
|
||||
- CI_MODE=browserstack_optional
|
||||
- CI_MODE=e2e EXPERIMENTAL_ES2015_DISTRO=1
|
||||
- CI_MODE=js
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
- CI_MODE=saucelabs_optional
|
||||
- CI_MODE=browserstack_optional
|
||||
- CI_MODE=docs_test
|
||||
- CI_MODE=aio
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
@ -47,5 +55,8 @@ install:
|
||||
script:
|
||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||
|
||||
after_success:
|
||||
- ./scripts/ci-lite/deploy_aio_staging.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/ci-lite/cleanup.sh
|
||||
|
630
CHANGELOG.md
630
CHANGELOG.md
@ -1,3 +1,619 @@
|
||||
<a name="4.0.0-beta.8"></a>
|
||||
## [4.0.0-beta.8](https://github.com/angular/angular/compare/4.0.0-beta.7...4.0.0-beta.8) (2017-02-18)
|
||||
|
||||
|
||||
### Features
|
||||
* **packaging**: allow applications to turn on strictNullChecks mode in TypeScript ([#14382](https://github.com/angular/angular/pull/14382)) ([03e855a](https://github.com/angular/angular/commit/03e855ae8f9e9b86251320c7551a96092bd8b0c4))
|
||||
* **common:** added typed overloaded for `AsyncPipe.transform()` ([#14367](https://github.com/angular/angular/issues/14367)) ([4da7925](https://github.com/angular/angular/commit/4da7925))
|
||||
* **compiler:** add support for source map generation ([#14258](https://github.com/angular/angular/issues/14258)) ([7ac38aa](https://github.com/angular/angular/commit/7ac38aa)), closes [#14125](https://github.com/angular/angular/issues/14125)
|
||||
* **compiler:** add target locale to the translation bundles ([#14290](https://github.com/angular/angular/issues/14290)) ([bb4db2d](https://github.com/angular/angular/commit/bb4db2d))
|
||||
* **compiler:** generate shallow imports compiler generated references ([#14388](https://github.com/angular/angular/issues/14388)) ([8b81bb1](https://github.com/angular/angular/commit/8b81bb1))
|
||||
* **compiler:** implement style encapsulation for new view engine ([#14518](https://github.com/angular/angular/issues/14518)) ([0fa3895](https://github.com/angular/angular/commit/0fa3895))
|
||||
* **compiler:** integrate compiler with view engine - change detection tests work ([#14412](https://github.com/angular/angular/issues/14412)) ([e4e9dbe](https://github.com/angular/angular/commit/e4e9dbe))
|
||||
* **compiler:** integrate compiler with view engine - main integration tests work ([#14284](https://github.com/angular/angular/issues/14284)) ([baa654a](https://github.com/angular/angular/commit/baa654a))
|
||||
* **compiler:** integrate compiler with view engine ([#14487](https://github.com/angular/angular/issues/14487)) ([4e7752a](https://github.com/angular/angular/commit/4e7752a))
|
||||
* **core:** add isStable Observable property to ApplicationRef to indicate when it's stable and unstable ([#14337](https://github.com/angular/angular/issues/14337)) ([c481798](https://github.com/angular/angular/commit/c481798))
|
||||
* **platform-server:** add API to render Module and ModuleFactory to string ([#14381](https://github.com/angular/angular/issues/14381)) ([b4d444a](https://github.com/angular/angular/commit/b4d444a))
|
||||
* **platform-server:** Implement PlatformLocation for platformServer() ([#14405](https://github.com/angular/angular/issues/14405)) ([9e28568](https://github.com/angular/angular/commit/9e28568))
|
||||
* **platform-server:** support [@angular](https://github.com/angular)/http from [@angular](https://github.com/angular)/platform-server ([9559d3e](https://github.com/angular/angular/commit/9559d3e))
|
||||
* **tsc-wrapped:** add an option to `ngc` to bundle metadata ([#14509](https://github.com/angular/angular/issues/14509)) ([3b89670](https://github.com/angular/angular/commit/3b89670))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** disable non-components as an entry component ([#14335](https://github.com/angular/angular/issues/14335)) ([44bb337](https://github.com/angular/angular/commit/44bb337))
|
||||
* **compiler:** improve error message for unknown elements ([#14373](https://github.com/angular/angular/issues/14373)) ([2c6dab9](https://github.com/angular/angular/commit/2c6dab9))
|
||||
* **compiler:** improve error messages in aot compiler ([#14333](https://github.com/angular/angular/issues/14333)) ([a696f4a](https://github.com/angular/angular/commit/a696f4a))
|
||||
* **compiler:** improve error msg for unknown properties on ([#14373](https://github.com/angular/angular/issues/14373)) ([e5a144d](https://github.com/angular/angular/commit/e5a144d)), closes [#14070](https://github.com/angular/angular/issues/14070)
|
||||
* **core:** Remove ChangeDetectorRef Paramter from KeyValueDifferFactory and IterableDifferFactory ([#14311](https://github.com/angular/angular/issues/14311)) ([45cc444](https://github.com/angular/angular/commit/45cc444))
|
||||
* **core:** suppress a Closure Compiler warning ([#14484](https://github.com/angular/angular/issues/14484)) ([2f2b65b](https://github.com/angular/angular/commit/2f2b65b))
|
||||
* **forms:** getRawValue should correctly work with nested FormGroups/Arrays ([#12964](https://github.com/angular/angular/issues/12964)) ([1ece736](https://github.com/angular/angular/commit/1ece736)), closes [#12963](https://github.com/angular/angular/issues/12963)
|
||||
* **http:** REVERT: remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([4676df5](https://github.com/angular/angular/commit/4676df5))
|
||||
* **platform-browser:** allow to mix shadow dom with non shadow dom ([ab26b65](https://github.com/angular/angular/commit/ab26b65))
|
||||
* **platform-browser:** should not throw for debug attrs containing $ ([#14353](https://github.com/angular/angular/issues/14353)) ([1cfbefe](https://github.com/angular/angular/commit/1cfbefe)), closes [#9566](https://github.com/angular/angular/issues/9566)
|
||||
* **platform-browser:** should only add styles with native encapsulation in shadow DOM ([#14313](https://github.com/angular/angular/issues/14313)) ([53cf2ec](https://github.com/angular/angular/commit/53cf2ec)), closes [#7887](https://github.com/angular/angular/issues/7887)
|
||||
* **platform-server:** allow multiple instances of platformServer and platformDynamicServer ([17486fd](https://github.com/angular/angular/commit/17486fd))
|
||||
* **platform-server:** default unspecified sections of the url to empty string ([#14512](https://github.com/angular/angular/issues/14512)) ([612f120](https://github.com/angular/angular/commit/612f120))
|
||||
* **platform-server:** read initial location from INITIAL_CONFIG if present ([56f232c](https://github.com/angular/angular/commit/56f232c))
|
||||
* **platform-server:** reflect properties to attributes for known elements, for serialization ([047cda5](https://github.com/angular/angular/commit/047cda5))
|
||||
* **platform-server:** render styles in app component instead of <head> ([30380d0](https://github.com/angular/angular/commit/30380d0))
|
||||
* **upgrade:** Prevent property renaming for $inject. ([96d06f7](https://github.com/angular/angular/commit/96d06f7))
|
||||
|
||||
|
||||
### DEPRECATIONS
|
||||
|
||||
* core: `KeyValueDifferFactory` and `IterableDifferFactory` no longer have `ChangeDetectorRef` as
|
||||
a parameter. It was not used and has been there for historical reasons. If you call
|
||||
`DifferFactory.create(...)` remove the `ChangeDetectorRef` argument. Introduced by ([#14311](https://github.com/angular/angular/pull/14311)).
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* common: Classes that derive from `AsyncPipe` and override
|
||||
`transform()` might not compile correctly. The much more common use of `async` pipe in
|
||||
templates is unaffected. We expect no or little impact on apps from this change, file an issue if we break you. Introduced by ([#14367](https://github.com/angular/angular/issues/14367)) ([4da7925](https://github.com/angular/angular/commit/4da7925)).
|
||||
|
||||
* Mitigation: Update derived classes of `AsyncPipe` that override `transform()` to include the type parameter overloads.
|
||||
|
||||
|
||||
|
||||
<a name="2.4.8"></a>
|
||||
## [2.4.8](https://github.com/angular/angular/compare/2.4.7...2.4.8) (2017-02-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **forms:** getRawValue should correctly work with nested FormGroups/Arrays ([#12964](https://github.com/angular/angular/issues/12964)) ([ea7737e](https://github.com/angular/angular/commit/ea7737e)), closes [#12963](https://github.com/angular/angular/issues/12963)
|
||||
* **http:** REVERT: remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([9ceb5d1](https://github.com/angular/angular/commit/9ceb5d1))
|
||||
* **platform-browser:** should only add styles with native encapsulation in shadow DOM ([#14313](https://github.com/angular/angular/issues/14313)) ([fadaf1e](https://github.com/angular/angular/commit/fadaf1e)), closes [#7887](https://github.com/angular/angular/issues/7887)
|
||||
* **router:** do not finish bootstrap until all the routes are resolved ([#14327](https://github.com/angular/angular/issues/14327)) ([541de26](https://github.com/angular/angular/commit/541de26)), closes [#12162](https://github.com/angular/angular/issues/12162)
|
||||
* **upgrade:** correctly project content on downgraded components with structural directives ([#14274](https://github.com/angular/angular/issues/14274)) ([74cb575](https://github.com/angular/angular/commit/74cb575)), closes [#14260](https://github.com/angular/angular/issues/14260)
|
||||
* **upgrade:** pass correct values to `ngOnChanges` for interpolation bindings ([#14400](https://github.com/angular/angular/issues/14400)) ([7c87c52](https://github.com/angular/angular/commit/7c87c52))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-beta.7"></a>
|
||||
## [4.0.0-beta.7](https://github.com/angular/angular/compare/4.0.0-beta.6...4.0.0-beta.7) (2017-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* build and test fixes for TS 2.1 ([#13294](https://github.com/angular/angular/issues/13294)) ([ef32e6b](https://github.com/angular/angular/commit/ef32e6b))
|
||||
* **benchmarks:** don’t force gc on the `profile` buttons ([#14345](https://github.com/angular/angular/issues/14345)) ([f6b5965](https://github.com/angular/angular/commit/f6b5965))
|
||||
* **build:** make fsevents an optional dependency in npm ([#13945](https://github.com/angular/angular/issues/13945)) ([4370049](https://github.com/angular/angular/commit/4370049))
|
||||
* **compiler-cli:** prevent ng-xi18n from emitting the compilation output ([#14115](https://github.com/angular/angular/issues/14115)) ([e58d683](https://github.com/angular/angular/commit/e58d683)), closes [#13567](https://github.com/angular/angular/issues/13567)
|
||||
* **tsc-wrapped:** use tsickle's new source map composition feature ([#14150](https://github.com/angular/angular/issues/14150)) ([5bccff0](https://github.com/angular/angular/commit/5bccff0))
|
||||
* **upgrade:** pass correct values to `ngOnChanges` for interpolation bindings ([#14301](https://github.com/angular/angular/issues/14301)) ([1e3dd3d](https://github.com/angular/angular/commit/1e3dd3d))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **upgrade:** unregister `$doCheck` watcher when destroying upgraded component ([#14303](https://github.com/angular/angular/issues/14303)) ([94312f0](https://github.com/angular/angular/commit/94312f0))
|
||||
* Don’t subclass Error; resulting in smaller binary ([#14160](https://github.com/angular/angular/issues/14160)) ([c33fda2](https://github.com/angular/angular/commit/c33fda2))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Angular 4 will support only TypeScript 2.1, so we no longer provide backwards compatibility to TS 1.8.
|
||||
|
||||
|
||||
|
||||
<a name="2.4.7"></a>
|
||||
## [2.4.7](https://github.com/angular/angular/compare/2.4.6...2.4.7) (2017-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **upgrade:** allow non-element selectors for downgraded components ([#14291](https://github.com/angular/angular/issues/14291)) ([5bb47db](https://github.com/angular/angular/commit/5bb47db))
|
||||
|
||||
|
||||
<a name="4.0.0-beta.6"></a>
|
||||
## [4.0.0-beta.6](https://github.com/angular/angular/compare/4.0.0-beta.5...4.0.0-beta.6) (2017-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* suppress some closure compiler warnings ([#14198](https://github.com/angular/angular/issues/14198)) ([2205829](https://github.com/angular/angular/commit/2205829))
|
||||
* **common:** add PopStateEvent interface ([#13400](https://github.com/angular/angular/issues/13400)) ([fe44118](https://github.com/angular/angular/commit/fe44118)), closes [#13378](https://github.com/angular/angular/issues/13378)
|
||||
* **common:** DatePipe shouldn't throw for NaN ([#14117](https://github.com/angular/angular/issues/14117)) ([7ad616a](https://github.com/angular/angular/commit/7ad616a)), closes [#14103](https://github.com/angular/angular/issues/14103)
|
||||
* **common:** DatePipe should parses input string even if it's not a valid date in browser ([#13895](https://github.com/angular/angular/issues/13895)) ([093cc04](https://github.com/angular/angular/commit/093cc04)), closes [#12334](https://github.com/angular/angular/issues/12334) [#13874](https://github.com/angular/angular/issues/13874)
|
||||
* **common:** use Symbol.observable to detect observables rather then presence of .subscribe ([#14067](https://github.com/angular/angular/issues/14067)) ([ff290af](https://github.com/angular/angular/commit/ff290af)), closes [#8848](https://github.com/angular/angular/issues/8848)
|
||||
* **compiler:** allow empty translations for attributes ([#14085](https://github.com/angular/angular/issues/14085)) ([05b2b49](https://github.com/angular/angular/commit/05b2b49)), closes [#13897](https://github.com/angular/angular/issues/13897)
|
||||
* avoid closure compiler warning ([#14229](https://github.com/angular/angular/issues/14229)) ([1bc5368](https://github.com/angular/angular/commit/1bc5368))
|
||||
* **router:** should allow navigation from root component in ngOnInit hook ([#13932](https://github.com/angular/angular/issues/13932)) ([47d41d4](https://github.com/angular/angular/commit/47d41d4)), closes [#13795](https://github.com/angular/angular/issues/13795)
|
||||
* failing integration test ([8270bec](https://github.com/angular/angular/commit/8270bec))
|
||||
* lint errors to make circle-ci green ([e0e5e78](https://github.com/angular/angular/commit/e0e5e78))
|
||||
* **router:** should find guard provided in a lazy loaded module ([#13929](https://github.com/angular/angular/issues/13929)) ([e075b1b](https://github.com/angular/angular/commit/e075b1b)), closes [#13530](https://github.com/angular/angular/issues/13530)
|
||||
* ngModel should use rxjs/symbol/observable to detect observable ([#14236](https://github.com/angular/angular/issues/14236)) ([a7479f6](https://github.com/angular/angular/commit/a7479f6))
|
||||
* **compiler:** allow expressions or functions in extends ([#14158](https://github.com/angular/angular/issues/14158)) ([b4214d6](https://github.com/angular/angular/commit/b4214d6)), closes [#14154](https://github.com/angular/angular/issues/14154)
|
||||
* **compiler:** fix missing translations handling ([#14113](https://github.com/angular/angular/issues/14113)) ([827c3fe](https://github.com/angular/angular/commit/827c3fe))
|
||||
* **compiler:** only lex messages that are needed by angular ([#14208](https://github.com/angular/angular/issues/14208)) ([5921c87](https://github.com/angular/angular/commit/5921c87))
|
||||
* **core:** add bootstrapped modules into platform modules list ([#13740](https://github.com/angular/angular/issues/13740)) ([863285a](https://github.com/angular/angular/commit/863285a)), closes [#12015](https://github.com/angular/angular/issues/12015)
|
||||
* **core:** ViewContainerRef.indexOf should not throw error when empty ([#13220](https://github.com/angular/angular/issues/13220)) ([a277e97](https://github.com/angular/angular/commit/a277e97))
|
||||
* **forms:** async validator cancels previous subscription when input has changed ([#13222](https://github.com/angular/angular/issues/13222)) ([6c7300c](https://github.com/angular/angular/commit/6c7300c)), closes [#12709](https://github.com/angular/angular/issues/12709) [#9120](https://github.com/angular/angular/issues/9120) [#10074](https://github.com/angular/angular/issues/10074) [#8923](https://github.com/angular/angular/issues/8923)
|
||||
* **forms:** fix broken unit test ([#14179](https://github.com/angular/angular/issues/14179)) ([1df9319](https://github.com/angular/angular/commit/1df9319))
|
||||
* **forms:** show a blank line when nothing is selected in IE or Edge ([#13903](https://github.com/angular/angular/issues/13903)) ([029f558](https://github.com/angular/angular/commit/029f558)), closes [#10010](https://github.com/angular/angular/issues/10010)
|
||||
* **forms:** verify functions passed into async validators returns Observable or Promise ([#14053](https://github.com/angular/angular/issues/14053)) ([94f84c5](https://github.com/angular/angular/commit/94f84c5))
|
||||
* **http:** remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([9e5617e](https://github.com/angular/angular/commit/9e5617e))
|
||||
* **http:** use params without RequestOptions ([#14101](https://github.com/angular/angular/issues/14101)) ([5f2b317](https://github.com/angular/angular/commit/5f2b317)), closes [#14100](https://github.com/angular/angular/issues/14100)
|
||||
* **language-service:** do not crash when Angular cannot be located ([#14123](https://github.com/angular/angular/issues/14123)) ([49fb814](https://github.com/angular/angular/commit/49fb814)), closes [#14122](https://github.com/angular/angular/issues/14122)
|
||||
* **platform-browser:** remove style nodes on destroy ([#13744](https://github.com/angular/angular/issues/13744)) ([cd3901f](https://github.com/angular/angular/commit/cd3901f)), closes [#11746](https://github.com/angular/angular/issues/11746)
|
||||
* **router:** fix CanActivateChild guard provided in a lazy loaded module ([#13989](https://github.com/angular/angular/issues/13989)) ([579567c](https://github.com/angular/angular/commit/579567c)), closes [#12275](https://github.com/angular/angular/issues/12275)
|
||||
* **testing:** async/fakeAsync/inject/withModule helpers should pass through context to callback functions ([#13718](https://github.com/angular/angular/issues/13718)) ([5f40e5b](https://github.com/angular/angular/commit/5f40e5b))
|
||||
* **upgrade:** detect async downgrade component changes ([#14039](https://github.com/angular/angular/issues/14039)) ([20b454c](https://github.com/angular/angular/commit/20b454c)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385)
|
||||
* **upgrade:** respect hierarchical injectors for downgraded components ([#14037](https://github.com/angular/angular/issues/14037)) ([1367cd9](https://github.com/angular/angular/commit/1367cd9))
|
||||
* **compiler:** improve error messages in aot compiler ([#14017](https://github.com/angular/angular/issues/14017)) ([665dde2](https://github.com/angular/angular/commit/665dde2))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **aio:** add initial angular-cli scaffold ([#14118](https://github.com/angular/angular/issues/14118)) ([e130bc1](https://github.com/angular/angular/commit/e130bc1))
|
||||
* **common:** rename underlying `NgFor` class and add a type parameter ([#14104](https://github.com/angular/angular/issues/14104)) ([86b2b25](https://github.com/angular/angular/commit/86b2b25))
|
||||
* **compiler:** allow missing translations ([#14113](https://github.com/angular/angular/issues/14113)) ([8775ab9](https://github.com/angular/angular/commit/8775ab9)), closes [#13861](https://github.com/angular/angular/issues/13861)
|
||||
* **compiler:** do not parse xtb messages not needed by angular ([#14111](https://github.com/angular/angular/issues/14111)) ([f7fba74](https://github.com/angular/angular/commit/f7fba74)), closes [#14046](https://github.com/angular/angular/issues/14046)
|
||||
* **compiler:** generate type parameters for generic type references ([#14104](https://github.com/angular/angular/issues/14104)) ([69e14b5](https://github.com/angular/angular/commit/69e14b5))
|
||||
* **core:** add query support to view engine ([1e729d7](https://github.com/angular/angular/commit/1e729d7)), closes [#14084](https://github.com/angular/angular/issues/14084)
|
||||
* **forms:** add email validator ([#13709](https://github.com/angular/angular/issues/13709)) ([d69717c](https://github.com/angular/angular/commit/d69717c)), closes [#13706](https://github.com/angular/angular/issues/13706)
|
||||
* **forms:** add equalsTo validator ([#14052](https://github.com/angular/angular/issues/14052)) ([7b7ae5f](https://github.com/angular/angular/commit/7b7ae5f))
|
||||
* **core:** add NgModule support to NgComponentOutlet directive ([#14088](https://github.com/angular/angular/issues/14088)) ([3ef73c2](https://github.com/angular/angular/commit/3ef73c2)), closes [#14043](https://github.com/angular/angular/issues/14043)
|
||||
* **router:** deprecate preserveQueryParams,add queryParamsHandling ([#14095](https://github.com/angular/angular/issues/14095)) ([c87c3be](https://github.com/angular/angular/commit/c87c3be))
|
||||
* **tsc-wrapped:** record arity of generic classes ([#14104](https://github.com/angular/angular/issues/14104)) ([1079b93](https://github.com/angular/angular/commit/1079b93))
|
||||
* **upgrade:** allow non-element selectors for downgraded components ([#14037](https://github.com/angular/angular/issues/14037)) ([9aafdc7](https://github.com/angular/angular/commit/9aafdc7))
|
||||
* **upgrade:** export VERSION from `upgrade/static` ([#14037](https://github.com/angular/angular/issues/14037)) ([7670cc1](https://github.com/angular/angular/commit/7670cc1))
|
||||
* **upgrade:** return a function (instead of array) from `downgradeInjectable()` ([#14037](https://github.com/angular/angular/issues/14037)) ([1f90f29](https://github.com/angular/angular/commit/1f90f29))
|
||||
* **forms:** provide a method to compare options ([#13349](https://github.com/angular/angular/issues/13349)) ([f89d004](https://github.com/angular/angular/commit/f89d004)), closes [#13268](https://github.com/angular/angular/issues/13268)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* use abstract keyword where possible to decrease file size. ([#14112](https://github.com/angular/angular/issues/14112)) ([670b680](https://github.com/angular/angular/commit/670b680))
|
||||
* **core** simplify ReflectiveInjector by removing code for Dart implementation ([#14126](https://github.com/angular/angular/issues/14126)) ([670b680](https://github.com/angular/angular/commit/670b680))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* common: A definition of `Iterable<T>` is now required to correctly compile
|
||||
Angular applications. Support for `Iterable<T>` is not required at
|
||||
runtime but a type definition `Iterable<T>` must be available.
|
||||
|
||||
`NgFor`, and now `NgForOf<T>`, already supports `Iterable<T>` at
|
||||
runtime. With this change the type definition is updated to reflect
|
||||
this support.
|
||||
|
||||
Migration:
|
||||
- add "es2015.iterable.ts" to your tsconfig.json "libs" fields.
|
||||
|
||||
Part of #12398
|
||||
* upgrade: Previously, `upgrade/static/downgradeInjectable` returned an array of the form:
|
||||
|
||||
```js
|
||||
['dep1', 'dep2', ..., function factory(dep1, dep2, ...) { ... }]
|
||||
```
|
||||
|
||||
Now it returns a function with an `$inject` property:
|
||||
|
||||
```js
|
||||
factory.$inject = ['dep1', 'dep2', ...];
|
||||
function factory(dep1, dep2, ...) { ... }
|
||||
```
|
||||
|
||||
It shouldn't affect the behavior of apps, since both forms are equally suitable
|
||||
to be used for registering AngularJS injectable services, but it is possible
|
||||
that type-checking might fail or that current code breaks if it relies on the
|
||||
returned value being an array.
|
||||
|
||||
|
||||
|
||||
<a name="2.4.6"></a>
|
||||
## [2.4.6](https://github.com/angular/angular/compare/2.4.5...2.4.6) (2017-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** add PopStateEvent interface ([#13400](https://github.com/angular/angular/issues/13400)) ([71567d1](https://github.com/angular/angular/commit/71567d1)), closes [#13378](https://github.com/angular/angular/issues/13378)
|
||||
* **common:** DatePipe does't throw for NaN ([#14117](https://github.com/angular/angular/issues/14117)) ([32cc675](https://github.com/angular/angular/commit/32cc675)), closes [#14103](https://github.com/angular/angular/issues/14103)
|
||||
* **common:** DatePipe parses input string if it's not a valid date in browser ([#13895](https://github.com/angular/angular/issues/13895)) ([e641636](https://github.com/angular/angular/commit/e641636)), closes [#12334](https://github.com/angular/angular/issues/12334) [#13874](https://github.com/angular/angular/issues/13874)
|
||||
* **common:** introduce isObservable method ([#14067](https://github.com/angular/angular/issues/14067)) ([109f0d1](https://github.com/angular/angular/commit/109f0d1)), closes [#8848](https://github.com/angular/angular/issues/8848)
|
||||
* **compiler:** allow empty translations for attributes ([#14085](https://github.com/angular/angular/issues/14085)) ([f3d5506](https://github.com/angular/angular/commit/f3d5506)), closes [#13897](https://github.com/angular/angular/issues/13897)
|
||||
* **core:** add bootstrapped modules into platform modules list ([#13740](https://github.com/angular/angular/issues/13740)) ([250dbc4](https://github.com/angular/angular/commit/250dbc4)), closes [#12015](https://github.com/angular/angular/issues/12015)
|
||||
* **core:** ViewContainerRef.indexOf should not throw error when empty ([#13220](https://github.com/angular/angular/issues/13220)) ([41b8d95](https://github.com/angular/angular/commit/41b8d95))
|
||||
* **forms:** show a blank line when nothing is selected in IE or Edge ([#13903](https://github.com/angular/angular/issues/13903)) ([09e2d20](https://github.com/angular/angular/commit/09e2d20)), closes [#10010](https://github.com/angular/angular/issues/10010)
|
||||
* **forms:** verify functions passed into async validators returns Observable or Promise ([#14053](https://github.com/angular/angular/issues/14053)) ([774e1db](https://github.com/angular/angular/commit/774e1db))
|
||||
* ngModel should use rxjs/symbol/observable to detect observable ([#14236](https://github.com/angular/angular/issues/14236)) ([7e639aa](https://github.com/angular/angular/commit/7e639aa))
|
||||
* **http:** remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([1eece50](https://github.com/angular/angular/commit/1eece50))
|
||||
* **i18n:** parse ICU messages while normalizing templates ([#14153](https://github.com/angular/angular/issues/14153)) ([8d4aa82](https://github.com/angular/angular/commit/8d4aa82))
|
||||
* **language-service:** do not crash when Angular cannot be located ([#14123](https://github.com/angular/angular/issues/14123)) ([a5b4af0](https://github.com/angular/angular/commit/a5b4af0)), closes [#14122](https://github.com/angular/angular/issues/14122)
|
||||
* **platform-browser:** remove style nodes on destroy ([#13744](https://github.com/angular/angular/issues/13744)) ([0614289](https://github.com/angular/angular/commit/0614289)), closes [#11746](https://github.com/angular/angular/issues/11746)
|
||||
* **router:** fix CanActivate redirect to the root on initial load ([#13929](https://github.com/angular/angular/issues/13929)) ([a047124](https://github.com/angular/angular/commit/a047124)), closes [#13530](https://github.com/angular/angular/issues/13530)
|
||||
* **router:** should find guard provided in a lazy loaded module ([#13989](https://github.com/angular/angular/issues/13989)) ([0965636](https://github.com/angular/angular/commit/0965636)), closes [#12275](https://github.com/angular/angular/issues/12275)
|
||||
* **router:** should allow navigation from root component in ngOnInit hook ([#13932](https://github.com/angular/angular/issues/13932)) ([4d2901d](https://github.com/angular/angular/commit/4d2901d)), closes [#13795](https://github.com/angular/angular/issues/13795)
|
||||
* **testing:** async/fakeAsync/inject/withModule helpers should pass through context to callback functions ([#13718](https://github.com/angular/angular/issues/13718)) ([70bbdf5](https://github.com/angular/angular/commit/70bbdf5))
|
||||
* **upgrade:** detect async downgrade component changes ([#14039](https://github.com/angular/angular/issues/14039)) ([117fa79](https://github.com/angular/angular/commit/117fa79)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385)
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-beta.5"></a>
|
||||
## [4.0.0-beta.5](https://github.com/angular/angular/compare/4.0.0-beta.3...4.0.0-beta.5) (2017-01-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([c2aa981](https://github.com/angular/angular/commit/c2aa981))
|
||||
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([f364557](https://github.com/angular/angular/commit/f364557))
|
||||
* **compiler:** [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n ([d02eab4](https://github.com/angular/angular/commit/d02eab4))
|
||||
* **compiler:** fix regexp to support firefox 31 ([#14082](https://github.com/angular/angular/issues/14082)) ([b2f9d56](https://github.com/angular/angular/commit/b2f9d56)), closes [#14029](https://github.com/angular/angular/issues/14029) [#13900](https://github.com/angular/angular/issues/13900)
|
||||
* **core:** export animation classes required for Renderer impl ([#14002](https://github.com/angular/angular/issues/14002)) ([83361d8](https://github.com/angular/angular/commit/83361d8)), closes [#14001](https://github.com/angular/angular/issues/14001)
|
||||
* **core:** fix not declared variable in view engine ([#14045](https://github.com/angular/angular/issues/14045)) ([d3a3a8e](https://github.com/angular/angular/commit/d3a3a8e))
|
||||
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([1200cf2](https://github.com/angular/angular/commit/1200cf2)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([635bf02](https://github.com/angular/angular/commit/635bf02)), closes [#11075](https://github.com/angular/angular/issues/11075)
|
||||
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([e8ea741](https://github.com/angular/angular/commit/e8ea741)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([d6382bf](https://github.com/angular/angular/commit/d6382bf)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
|
||||
* **upgrade/static:** ensure upgraded injector is initialized early enough ([#14065](https://github.com/angular/angular/issues/14065)) ([6152eb2](https://github.com/angular/angular/commit/6152eb2)), closes [#13811](https://github.com/angular/angular/issues/13811)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **build:** optionally build an ES2015 distro ([#13471](https://github.com/angular/angular/issues/13471)) ([be6c95a](https://github.com/angular/angular/commit/be6c95a))
|
||||
* **core:** add event support to view engine ([0adb97b](https://github.com/angular/angular/commit/0adb97b))
|
||||
* **core:** add initial view engine ([#14014](https://github.com/angular/angular/issues/14014)) ([2f87eb5](https://github.com/angular/angular/commit/2f87eb5))
|
||||
* **core:** add pure expression support to view engine ([6541737](https://github.com/angular/angular/commit/6541737))
|
||||
* **core:** Add type information to injector.get() ([#13785](https://github.com/angular/angular/issues/13785)) ([d169c24](https://github.com/angular/angular/commit/d169c24))
|
||||
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([e19bf70](https://github.com/angular/angular/commit/e19bf70))
|
||||
* **tsc-wrapped:** Support of vinyl like config file was added ([#13987](https://github.com/angular/angular/issues/13987)) ([0c7726d](https://github.com/angular/angular/commit/0c7726d))
|
||||
* **upgrade:** Support ng-model in downgraded components ([#10578](https://github.com/angular/angular/issues/10578)) ([e21e9c5](https://github.com/angular/angular/commit/e21e9c5))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* core: - Because `injector.get()` is now parameterize it is possible that code
|
||||
which used to work no longer type checks. Example would be if one
|
||||
injects `Foo` but configures it as `{provide: Foo, useClass: MockFoo}`.
|
||||
The injection instance will be that of `MockFoo` but the type will be
|
||||
`Foo` instead of `any` as in the past. This means that it was possible
|
||||
to call a method on `MockFoo` in the past which now will fail type
|
||||
check. See this example:
|
||||
|
||||
```
|
||||
class Foo {}
|
||||
class MockFoo extends Foo {
|
||||
setupMock();
|
||||
}
|
||||
|
||||
var PROVIDERS = [
|
||||
{provide: Foo, useClass: MockFoo}
|
||||
];
|
||||
|
||||
...
|
||||
|
||||
function myTest(injector: Injector) {
|
||||
var foo = injector.get(Foo);
|
||||
// This line used to work since `foo` used to be `any` before this
|
||||
// change, it will now be `Foo`, and `Foo` does not have `setUpMock()`.
|
||||
// The fix is to downcast: `injector.get(Foo) as MockFoo`.
|
||||
foo.setUpMock();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="2.4.5"></a>
|
||||
## [2.4.5](https://github.com/angular/angular/compare/2.4.4...2.4.5) (2017-01-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n ([5492fad](https://github.com/angular/angular/commit/5492fad))
|
||||
* **compiler:** fix regexp to support firefox 31 ([#14082](https://github.com/angular/angular/issues/14082)) ([bd2eecb](https://github.com/angular/angular/commit/bd2eecb)), closes [#14029](https://github.com/angular/angular/issues/14029) [#13900](https://github.com/angular/angular/issues/13900)
|
||||
* **core:** export animation classes required for Renderer impl ([#14002](https://github.com/angular/angular/issues/14002)) ([fd4f9ac](https://github.com/angular/angular/commit/fd4f9ac)), closes [#14001](https://github.com/angular/angular/issues/14001)
|
||||
* **upgrade:** ensure upgraded injector is initialized early enough ([#14065](https://github.com/angular/angular/issues/14065)) ([3b2fb23](https://github.com/angular/angular/commit/3b2fb23)), closes [#13811](https://github.com/angular/angular/issues/13811)
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-beta.4"></a>
|
||||
## [4.0.0-beta.4](https://github.com/angular/angular/compare/4.0.0-beta.3...4.0.0-beta.4) (2017-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([c2aa981](https://github.com/angular/angular/commit/c2aa981))
|
||||
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([f364557](https://github.com/angular/angular/commit/f364557))
|
||||
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([1200cf2](https://github.com/angular/angular/commit/1200cf2)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([635bf02](https://github.com/angular/angular/commit/635bf02)), closes [#11075](https://github.com/angular/angular/issues/11075)
|
||||
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([e8ea741](https://github.com/angular/angular/commit/e8ea741)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([e19bf70](https://github.com/angular/angular/commit/e19bf70))
|
||||
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([d6382bf](https://github.com/angular/angular/commit/d6382bf)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **build:** optionally build an ES2015 distro ([#13471](https://github.com/angular/angular/issues/13471)) ([be6c95a](https://github.com/angular/angular/commit/be6c95a))
|
||||
* **core:** Add type information to injector.get() ([#13785](https://github.com/angular/angular/issues/13785)) ([d169c24](https://github.com/angular/angular/commit/d169c24))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* core: - Because `injector.get()` is now parameterized it is possible that code
|
||||
which used to work no longer type checks. Example would be if one
|
||||
injects `Foo` but configures it as `{provide: Foo, useClass: MockFoo}`.
|
||||
The injection instance will be that of `MockFoo` but the type will be
|
||||
`Foo` instead of `any` as in the past. This means that it was possible
|
||||
to call a method on `MockFoo` in the past which now will fail type
|
||||
check. See this example:
|
||||
|
||||
```
|
||||
class Foo {}
|
||||
class MockFoo extends Foo {
|
||||
setupMock();
|
||||
}
|
||||
|
||||
var PROVIDERS = [
|
||||
{provide: Foo, useClass: MockFoo}
|
||||
];
|
||||
|
||||
...
|
||||
|
||||
function myTest(injector: Injector) {
|
||||
var foo = injector.get(Foo);
|
||||
// This line used to work since `foo` used to be `any` before this
|
||||
// change, it will now be `Foo`, and `Foo` does not have `setUpMock()`.
|
||||
// The fix is to downcast: `injector.get(Foo) as MockFoo`.
|
||||
foo.setUpMock();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="2.4.4"></a>
|
||||
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
|
||||
|
||||
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
|
||||
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([3d0b1b8](https://github.com/angular/angular/commit/3d0b1b8))
|
||||
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([015878a](https://github.com/angular/angular/commit/015878a)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([2af5862](https://github.com/angular/angular/commit/2af5862)), closes [#11075](https://github.com/angular/angular/issues/11075)
|
||||
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([49c4b0f](https://github.com/angular/angular/commit/49c4b0f)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([bd15110](https://github.com/angular/angular/commit/bd15110))
|
||||
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([2250082](https://github.com/angular/angular/commit/2250082)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-beta.3"></a>
|
||||
## [4.0.0-beta.3](https://github.com/angular/angular/compare/4.0.0-beta.2...4.0.0-beta.3) (2017-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** avoid evaluating arguments to unknown decorators ([d061adc](https://github.com/angular/angular/commit/d061adc)), closes [#13605](https://github.com/angular/angular/issues/13605)
|
||||
* **compiler:** fix template binding parsing (`*directive="-..."`) ([e5c6bb4](https://github.com/angular/angular/commit/e5c6bb4)), closes [#13800](https://github.com/angular/angular/issues/13800)
|
||||
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([5d9cbd7](https://github.com/angular/angular/commit/5d9cbd7)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
|
||||
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([aeed737](https://github.com/angular/angular/commit/aeed737)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
|
||||
* **core:** Add type information to differs ([8c7e93b](https://github.com/angular/angular/commit/8c7e93b)), closes [#13382](https://github.com/angular/angular/issues/13382)
|
||||
* **i18n:** translate attributes inside elements marked for translation ([424e6c4](https://github.com/angular/angular/commit/424e6c4)), closes [#13796](https://github.com/angular/angular/issues/13796) [#13814](https://github.com/angular/angular/issues/13814)
|
||||
* **router:** RouterLink mirrors input `target` as attribute ([d9a22da](https://github.com/angular/angular/commit/d9a22da)), closes [#13837](https://github.com/angular/angular/issues/13837)
|
||||
* **router:** throw an error when navigate to null/undefined path ([46cb04d](https://github.com/angular/angular/commit/46cb04d)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
|
||||
* **router:** fix checking for object intersection ([6d29fae](https://github.com/angular/angular/commit/6d29fae))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** expose the `element` value within transition events ([4bae4b3](https://github.com/angular/angular/commit/4bae4b3))
|
||||
* **animations:** expose the `triggerName` within the transition event ([3f67ab0](https://github.com/angular/angular/commit/3f67ab0)), closes [#13600](https://github.com/angular/angular/issues/13600)
|
||||
* **animations:** support function types in transitions ([9211a22](https://github.com/angular/angular/commit/9211a22)), closes [#13538](https://github.com/angular/angular/issues/13538) [#13537](https://github.com/angular/angular/issues/13537)
|
||||
* **language-service:** support TS2.2 plugin model ([99aa49a](https://github.com/angular/angular/commit/99aa49a))
|
||||
* **NgComponentOutlet:** add NgComponentOutlet directive ([8578682](https://github.com/angular/angular/commit/8578682)), closes [#11168](https://github.com/angular/angular/issues/11168)
|
||||
* **NgTemplateOutlet:** Make NgTemplateOutlet compatible with * syntax ([c0178de](https://github.com/angular/angular/commit/c0178de))
|
||||
* **router:** call resolver when upstream params change ([#12942](https://github.com/angular/angular/issues/12942)) ([d4d3782](https://github.com/angular/angular/commit/d4d3782))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* core: - `IterableChangeRecord` is now an interface and parameterized on `<V>`.
|
||||
This should not be an issue unless your code does
|
||||
`new IterableChangeRecord` which it should not have a reason to do.
|
||||
- `KeyValueChangeRecord` is now an interface and parameterized on `<V>`.
|
||||
This should not be an issue unless your code does
|
||||
`new KeyValueChangeRecord` which it should not have a reason to do.
|
||||
|
||||
### DEPRECATION
|
||||
|
||||
* Deprecate `ngOutletContext`. Use `ngTemplateOutletContext` instead.
|
||||
* `CollectionChangeRecord` is renamed to `IterableChangeRecord`.
|
||||
`CollectionChangeRecord` is aliased to `IterableChangeRecord` and is
|
||||
marked as `@deprecated`. It will be removed in `v6.x.x`.
|
||||
* Deprecate `DefaultIterableDiffer` as it is private class which
|
||||
was erroneously exposed.
|
||||
* Deprecate `KeyValueDiffers#factories` as it is private field which
|
||||
was erroneously exposed.
|
||||
* Deprecate `IterableDiffers#factories` as it is private field which
|
||||
was erroneously exposed.
|
||||
|
||||
|
||||
|
||||
<a name="2.4.3"></a>
|
||||
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** avoid evaluating arguments to unknown decorators ([5e9d3db](https://github.com/angular/angular/commit/5e9d3db)), closes [#13605](https://github.com/angular/angular/issues/13605)
|
||||
* **compiler:** fix template binding parsing (`*directive="-..."`) ([7dc12b9](https://github.com/angular/angular/commit/7dc12b9)), closes [#13800](https://github.com/angular/angular/issues/13800)
|
||||
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([6164eb2](https://github.com/angular/angular/commit/6164eb2)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
|
||||
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([313683f](https://github.com/angular/angular/commit/313683f)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
|
||||
* **i18n:** translate attributes inside elements marked for translation ([d7f2a3c](https://github.com/angular/angular/commit/d7f2a3c))
|
||||
* **router:** RouterLink mirrors input `target` as attribute ([1c82b58](https://github.com/angular/angular/commit/1c82b58)), closes [#13837](https://github.com/angular/angular/issues/13837)
|
||||
* **router:** throw an error when navigate to null/undefined path ([61ba223](https://github.com/angular/angular/commit/61ba223)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
|
||||
* **router:** fix checking for object intersection ([1692265](https://github.com/angular/angular/commit/1692265))
|
||||
|
||||
|
||||
|
||||
<a name="2.4.2"></a>
|
||||
## [2.4.2](https://github.com/angular/angular/compare/2.4.1...2.4.2) (2017-01-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** add link to trackBy docs from the error message ([#13634](https://github.com/angular/angular/issues/13634)) ([f723437](https://github.com/angular/angular/commit/f723437))
|
||||
* **common:** do not override locale provided on bootstrap ([#13654](https://github.com/angular/angular/issues/13654)) ([5f49c3e](https://github.com/angular/angular/commit/5f49c3e)), closes [#13607](https://github.com/angular/angular/issues/13607)
|
||||
* **common:** allow null/undefined values for `NgForTrackBy` ([6be55cc](https://github.com/angular/angular/commit/6be55cc)), closes [#13641](https://github.com/angular/angular/issues/13641)
|
||||
* **compiler:** don’t throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([230e33f](https://github.com/angular/angular/commit/230e33f)), closes [#13565](https://github.com/angular/angular/issues/13565)
|
||||
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([1cd73c7](https://github.com/angular/angular/commit/1cd73c7)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
|
||||
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645) [#13982](https://github.com/angular/angular/issues/13982)
|
||||
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([f1cde43](https://github.com/angular/angular/commit/f1cde43))
|
||||
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([b245b92](https://github.com/angular/angular/commit/b245b92))
|
||||
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([21f5f05](https://github.com/angular/angular/commit/21f5f05))
|
||||
* **core/testing:** improve misleading error message when don't call compileComponents ([#13543](https://github.com/angular/angular/issues/13543)) ([0e7f9f0](https://github.com/angular/angular/commit/0e7f9f0)), closes [#11301](https://github.com/angular/angular/issues/11301)
|
||||
* **forms:** Validators.required properly validate arrays ([#13362](https://github.com/angular/angular/issues/13362)) ([17c5fa9](https://github.com/angular/angular/commit/17c5fa9)), closes [#12274](https://github.com/angular/angular/issues/12274)
|
||||
* **language-service:** support TypeScript 2.1 ([#13655](https://github.com/angular/angular/issues/13655)) ([56b4296](https://github.com/angular/angular/commit/56b4296))
|
||||
* **router:** fix lazy loaded module with wildcard route ([#13649](https://github.com/angular/angular/issues/13649)) ([5754ecc](https://github.com/angular/angular/commit/5754ecc)), closes [#12955](https://github.com/angular/angular/issues/12955)
|
||||
* **router:** routerLink support of null ([#13380](https://github.com/angular/angular/issues/13380)) ([018865e](https://github.com/angular/angular/commit/018865e)), closes [#6971](https://github.com/angular/angular/issues/6971)
|
||||
* **router:** update route snapshot before emit new values ([#13558](https://github.com/angular/angular/issues/13558)) ([9f6a647](https://github.com/angular/angular/commit/9f6a647)), closes [#12912](https://github.com/angular/angular/issues/12912)
|
||||
* **upgrade:** fix/improve support for lifecycle hooks ([#13020](https://github.com/angular/angular/issues/13020)) ([21942a8](https://github.com/angular/angular/commit/21942a8))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-beta.2"></a>
|
||||
## [4.0.0-beta.2](https://github.com/angular/angular/compare/4.0.0-beta.1...4.0.0-beta.2) (2017-01-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** generate less code for bindings to DOM elements ([db49d42](https://github.com/angular/angular/commit/db49d42))
|
||||
* **compiler:** generate proper reexports in `.ngfactory.ts` files to not need transitive deps for compiling `.ngfactory.ts` files. ([#13524](https://github.com/angular/angular/issues/13524)) ([9c69703](https://github.com/angular/angular/commit/9c69703)), closes [#12787](https://github.com/angular/angular/issues/12787)
|
||||
* **router:** add an extra argument to CanDeactivate interface ([#13560](https://github.com/angular/angular/issues/13560)) ([69fa3bb](https://github.com/angular/angular/commit/69fa3bb)), closes [#9853](https://github.com/angular/angular/issues/9853)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** improve error message for undefined providers ([#13546](https://github.com/angular/angular/issues/13546)) ([6b02b80](https://github.com/angular/angular/commit/6b02b80)), closes [#10835](https://github.com/angular/angular/issues/10835)
|
||||
* **compiler:** improve the error when template is not a string ([2c0c86e](https://github.com/angular/angular/commit/2c0c86e)), closes [#8708](https://github.com/angular/angular/issues/8708) [#13377](https://github.com/angular/angular/issues/13377)
|
||||
* **compiler:** throw an error for invalid provider ([#13544](https://github.com/angular/angular/issues/13544)) ([445ed43](https://github.com/angular/angular/commit/445ed43)), closes [#8870](https://github.com/angular/angular/issues/8870)
|
||||
* **i18n:** parse ICU messages while normalizing templates ([e74d8aa](https://github.com/angular/angular/commit/e74d8aa))
|
||||
|
||||
Note: 4.0.0-beta.2 release also contains all the changes present in the 2.4.2 release.
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **core**: `SimpleChange` now takes an additional argument that defines whether this is the first
|
||||
change or not. This is a low profile API and we don't expect anyone to be affected by this change.
|
||||
If you are impacted by this change please file an issue. ([465516b](https://github.com/angular/angular/commit/465516b))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-beta.1"></a>
|
||||
## [4.0.0-beta.1](https://github.com/angular/angular/compare/2.4.0-marker...4.0.0-beta.1) (2016-12-22)
|
||||
|
||||
### Features
|
||||
|
||||
* **upgrade:** support the `$doCheck()` lifecycle hook in `UpgradeComponent` ([#13015](https://github.com/angular/angular/issues/13015)) ([9da4c25](https://github.com/angular/angular/commit/9da4c25))
|
||||
|
||||
Note: 4.0.0-beta.1 release also contains all the changes present in the 2.4.0 and the 2.4.1 releases.
|
||||
|
||||
<a name="2.4.1"></a>
|
||||
## [2.4.1](https://github.com/angular/angular/compare/2.4.0...2.4.1) (2016-12-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always quote string map key values in AOT code ([#13602](https://github.com/angular/angular/issues/13602)) ([6a5e46c](https://github.com/angular/angular/commit/6a5e46c))
|
||||
* **animations:** always recover from a failed animation step ([#13604](https://github.com/angular/angular/issues/13604)) ([d788c67](https://github.com/angular/angular/commit/d788c67))
|
||||
* **compiler:** ignore `@import` in comments ([#13368](https://github.com/angular/angular/issues/13368)) ([6316e5d](https://github.com/angular/angular/commit/6316e5d)), closes [#12196](https://github.com/angular/angular/issues/12196)
|
||||
* **core:** improve error message when component factory cannot be found ([#13541](https://github.com/angular/angular/issues/13541)) ([b9e979e](https://github.com/angular/angular/commit/b9e979e)), closes [#12678](https://github.com/angular/angular/issues/12678)
|
||||
* **router:** should reset location if a navigation by location is successful ([#13545](https://github.com/angular/angular/issues/13545)) ([a38f14b](https://github.com/angular/angular/commit/a38f14b)), closes [#13491](https://github.com/angular/angular/issues/13491)
|
||||
|
||||
|
||||
|
||||
<a name="2.4.0"></a>
|
||||
# [2.4.0 stability-interjection](https://github.com/angular/angular/compare/2.3.1...2.4.0) (2016-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** allow players to be destroyed before initialized ([#13346](https://github.com/angular/angular/issues/13346)) ([b36f4bc](https://github.com/angular/angular/commit/b36f4bc)), closes [#13293](https://github.com/angular/angular/issues/13293)
|
||||
* **build:** use bash string comparison operator ([#13502](https://github.com/angular/angular/issues/13502)) ([50afbe0](https://github.com/angular/angular/commit/50afbe0))
|
||||
* **compiler:** do not lex `}}` when interpolation is disabled ([#13531](https://github.com/angular/angular/issues/13531)) ([9b87bb6](https://github.com/angular/angular/commit/9b87bb6)), closes [#13525](https://github.com/angular/angular/issues/13525)
|
||||
* **compiler-cli:** produce metadata for .d.ts files without metadata ([#13526](https://github.com/angular/angular/issues/13526)) ([debb0c9](https://github.com/angular/angular/commit/debb0c9)), closes [#13307](https://github.com/angular/angular/issues/13307) [#13473](https://github.com/angular/angular/issues/13473) [#13521](https://github.com/angular/angular/issues/13521)
|
||||
* **i18n:** add a default example to xmb placeholders ([#13507](https://github.com/angular/angular/issues/13507)) ([3f17841](https://github.com/angular/angular/commit/3f17841))
|
||||
* **upgrade:** fix `registerForNg1Tests` ([#13522](https://github.com/angular/angular/issues/13522)) ([c26c24c](https://github.com/angular/angular/commit/c26c24c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update to `rxjs@5.0.1` and unpin the rxjs peerDeps via `^5.0.1` ([#13572](https://github.com/angular/angular/issues/13572)) ([8d5da1e](https://github.com/angular/angular/commit/8d5da1e)), closes [#13561](https://github.com/angular/angular/issues/13561) [#13478](https://github.com/angular/angular/issues/13478)
|
||||
|
||||
|
||||
<a name="4.0.0-beta.0"></a>
|
||||
## [4.0.0-beta.0](https://github.com/angular/angular/compare/2.3.0...4.0.0-beta.0) (2016-12-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** add a `titlecase` pipe ([#13324](https://github.com/angular/angular/issues/13324)) ([61d7c1e](https://github.com/angular/angular/commit/61d7c1e)), closes [#11436](https://github.com/angular/angular/issues/11436)
|
||||
* **common:** export NgLocaleLocalization ([#13367](https://github.com/angular/angular/issues/13367)) ([56dce0e](https://github.com/angular/angular/commit/56dce0e)), closes [#11921](https://github.com/angular/angular/issues/11921)
|
||||
* **compiler:** add id property to i18nMessage ([6dd5201](https://github.com/angular/angular/commit/6dd5201))
|
||||
* **compiler:** digest methods return i18nMessage id if sets ([562f7a2](https://github.com/angular/angular/commit/562f7a2))
|
||||
* **forms:** add novalidate by default ([#13092](https://github.com/angular/angular/issues/13092)) ([4c35be3](https://github.com/angular/angular/commit/4c35be3))
|
||||
* **http:** simplify URLSearchParams creation ([#13338](https://github.com/angular/angular/issues/13338)) ([90c2235](https://github.com/angular/angular/commit/90c2235)), closes [#8858](https://github.com/angular/angular/issues/8858)
|
||||
* **language-service:** warn when a method isn't called in an event ([#13437](https://github.com/angular/angular/issues/13437)) ([9ec0a4e](https://github.com/angular/angular/commit/9ec0a4e))
|
||||
* **platform browser:** introduce Meta service ([#12322](https://github.com/angular/angular/issues/12322)) ([72361fb](https://github.com/angular/angular/commit/72361fb))
|
||||
* **router:** routerLink add tabindex attribute ([#13094](https://github.com/angular/angular/issues/13094)) ([a006c14](https://github.com/angular/angular/commit/a006c14)), closes [#10895](https://github.com/angular/angular/issues/10895)
|
||||
* **testing:** add overrideTemplate method ([#13372](https://github.com/angular/angular/issues/13372)) ([169ed82](https://github.com/angular/angular/commit/169ed82)), closes [#10685](https://github.com/angular/angular/issues/10685)
|
||||
* **common** ngIf now supports else; saves condition to local var ([b4db73d](https://github.com/angular/angular/commit/b4db73d)), closes [#13061](https://github.com/angular/angular/issues/13061) [#13297](https://github.com/angular/angular/issues/13297)
|
||||
|
||||
Note: 4.0.0-beta.0 release also contains all the changes present in the 2.3.1 release.
|
||||
|
||||
<a name="2.3.1"></a>
|
||||
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always cleanup players after they have finished internally ([#13334](https://github.com/angular/angular/issues/13334)) ([a26e054](https://github.com/angular/angular/commit/a26e054)), closes [#13333](https://github.com/angular/angular/issues/13333)
|
||||
* **animations:** throw errors and normalize offset beyond the range of [0,1] ([6557bc3](https://github.com/angular/angular/commit/6557bc3)), closes [#13348](https://github.com/angular/angular/issues/13348) [#13440](https://github.com/angular/angular/issues/13440)
|
||||
* **compiler:** emit quoted object literal keys if the source is quoted ([ecfad46](https://github.com/angular/angular/commit/ecfad46)), closes [#13249](https://github.com/angular/angular/issues/13249) [#13356](https://github.com/angular/angular/issues/13356)
|
||||
* **compiler:** fix merge error in compiler_host ([69b52eb](https://github.com/angular/angular/commit/69b52eb))
|
||||
* **compiler:** fix PR 13322 ([#13331](https://github.com/angular/angular/issues/13331)) ([79728b4](https://github.com/angular/angular/commit/79728b4))
|
||||
* **compiler:** fix simplify a reference without a name ([1c279b3](https://github.com/angular/angular/commit/1c279b3)), closes [#13470](https://github.com/angular/angular/issues/13470)
|
||||
* **compiler:** fix transpiled ES5 code ([#13322](https://github.com/angular/angular/issues/13322)) ([6c1d790](https://github.com/angular/angular/commit/6c1d790)), closes [#13301](https://github.com/angular/angular/issues/13301)
|
||||
* **compiler:** generated CSS files suffixed with ngstyle. ([#13353](https://github.com/angular/angular/issues/13353)) ([c8a9b70](https://github.com/angular/angular/commit/c8a9b70)), closes [#13141](https://github.com/angular/angular/issues/13141)
|
||||
* **compiler:** make sure provider values with `name` property don’t break. ([efa2d80](https://github.com/angular/angular/commit/efa2d80)), closes [#13394](https://github.com/angular/angular/issues/13394) [#13445](https://github.com/angular/angular/issues/13445)
|
||||
* **compiler:** narrow the span reported for invalid pipes ([307d305](https://github.com/angular/angular/commit/307d305)), closes [#13326](https://github.com/angular/angular/issues/13326) [#13411](https://github.com/angular/angular/issues/13411)
|
||||
* **compiler:** propagate exports when upgrading metadata to v2 ([f6ef7d6](https://github.com/angular/angular/commit/f6ef7d6))
|
||||
* **compiler:** resolver should merge host bindings and listeners ([#13474](https://github.com/angular/angular/issues/13474)) ([6aeaca3](https://github.com/angular/angular/commit/6aeaca3)), closes [#13327](https://github.com/angular/angular/issues/13327)
|
||||
* **compiler:** support dotted property binding ([8db184d](https://github.com/angular/angular/commit/8db184d)), closes [angular/flex-layout#34](https://github.com/angular/flex-layout/issues/34)
|
||||
* **compiler:** update to metadata version 3 ([#13464](https://github.com/angular/angular/issues/13464)) ([b9b557c](https://github.com/angular/angular/commit/b9b557c))
|
||||
* **core:** detectChanges() doesn't work on detached instance ([4d6ac9d](https://github.com/angular/angular/commit/4d6ac9d)), closes [#13426](https://github.com/angular/angular/issues/13426) [#13472](https://github.com/angular/angular/issues/13472)
|
||||
* **core:** properly destroy embedded Views attatched to ApplicationRef ([#13459](https://github.com/angular/angular/issues/13459)) ([d40bbf4](https://github.com/angular/angular/commit/d40bbf4)), closes [#13062](https://github.com/angular/angular/issues/13062)
|
||||
* **core:** remove logError from logGroup ([#12925](https://github.com/angular/angular/issues/12925)) ([5fab871](https://github.com/angular/angular/commit/5fab871))
|
||||
* **forms:** ensure `select[multiple]` retains selections ([b3dcff0](https://github.com/angular/angular/commit/b3dcff0)), closes [#12527](https://github.com/angular/angular/issues/12527) [#12654](https://github.com/angular/angular/issues/12654)
|
||||
* **forms:** fix Validators.min/maxLength with FormArray ([#13095](https://github.com/angular/angular/issues/13095)) ([7383e4a](https://github.com/angular/angular/commit/7383e4a)), closes [#13089](https://github.com/angular/angular/issues/13089)
|
||||
* **forms:** introduce checkbox required validator ([124267c](https://github.com/angular/angular/commit/124267c)), closes [#11459](https://github.com/angular/angular/issues/11459) [#13364](https://github.com/angular/angular/issues/13364)
|
||||
* **http:** check response body text against undefined ([#13017](https://github.com/angular/angular/issues/13017)) ([f106a18](https://github.com/angular/angular/commit/f106a18))
|
||||
* **http:** create a copy of headers when merge options ([#13365](https://github.com/angular/angular/issues/13365)) ([65c9b5b](https://github.com/angular/angular/commit/65c9b5b)), closes [#11980](https://github.com/angular/angular/issues/11980)
|
||||
* **language-service:** correctly type `undefined` ([0a7364f](https://github.com/angular/angular/commit/0a7364f)), closes [#13412](https://github.com/angular/angular/issues/13412) [#13414](https://github.com/angular/angular/issues/13414)
|
||||
* **compiler**: better error when directive not listed in NgModule.declarations ([b0cd514](https://github.com/angular/angular/commit/b0cd514))
|
||||
* **language-service:** treat string unions as strings ([#13406](https://github.com/angular/angular/issues/13406)) ([14dd2b3](https://github.com/angular/angular/commit/14dd2b3)), closes [#13403](https://github.com/angular/angular/issues/13403)
|
||||
* **router:** add support for query params with multiple values ([e4d5a5f](https://github.com/angular/angular/commit/e4d5a5f)), closes [#11373](https://github.com/angular/angular/issues/11373)
|
||||
* **router:** Use T type in Resolve interface ([#13242](https://github.com/angular/angular/issues/13242)) ([5ee8155](https://github.com/angular/angular/commit/5ee8155))
|
||||
* **selector:** SelectorMatcher match elements with :not selector ([#12977](https://github.com/angular/angular/issues/12977)) ([392c9ac](https://github.com/angular/angular/commit/392c9ac))
|
||||
* **tsc-wrapped:** generate metadata for exports without module specifier ([cd03c77](https://github.com/angular/angular/commit/cd03c77)), closes [#13327](https://github.com/angular/angular/issues/13327)
|
||||
* **upgrade:** fix downgrade content projection and injector inheritance ([86c5098](https://github.com/angular/angular/commit/86c5098)), closes [#6629](https://github.com/angular/angular/issues/6629) [#7727](https://github.com/angular/angular/issues/7727) [#8729](https://github.com/angular/angular/issues/8729) [#9643](https://github.com/angular/angular/issues/9643) [#9649](https://github.com/angular/angular/issues/9649) [#12675](https://github.com/angular/angular/issues/12675)
|
||||
* **upgrade:** enable AngularJS unit testing of upgrade module ([2fc0560](https://github.com/angular/angular/commit/2fc0560)), closes [#5462](https://github.com/angular/angular/issues/5462) [#12675](https://github.com/angular/angular/issues/12675)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **animations:** always run the animation queue outside of zones ([e2622ad](https://github.com/angular/angular/commit/e2622ad)), closes [#13440](https://github.com/angular/angular/issues/13440)
|
||||
|
||||
### Note ###
|
||||
|
||||
Due to regression in the 2.3.0 release that was fixed by [#13464](https://github.com/angular/angular/pull/13464),
|
||||
components that have been compiled using 2.3.0 and published to npm will need to be recompiled and republished.
|
||||
|
||||
The >=2.3.1 compiler will issue is the following error if it encounters components compiled with 2.3.0:
|
||||
`Unsupported metadata version 2 for module ${module}. This module should be compiled with a newer version of ngc`.
|
||||
|
||||
We are adding more tests to our test suite to catch these kinds of problems before we cut a release.
|
||||
|
||||
<a name="2.3.0"></a>
|
||||
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)
|
||||
|
||||
@ -15,12 +631,12 @@
|
||||
* **language-service:** do not throw for invalid metadata ([#13261](https://github.com/angular/angular/issues/13261)) ([4a09c81](https://github.com/angular/angular/commit/4a09c81)), closes [#13255](https://github.com/angular/angular/issues/13255)
|
||||
* **language-service:** remove incompletely used parameter from `createLanguageServiceFromTypescript()` ([#13278](https://github.com/angular/angular/issues/13278)) ([25c2141](https://github.com/angular/angular/commit/25c2141)), closes [#13277](https://github.com/angular/angular/issues/13277)
|
||||
* **language-service:** update to use `CompilerHost` from compiler-cli ([#13189](https://github.com/angular/angular/issues/13189)) ([3ff6554](https://github.com/angular/angular/commit/3ff6554))
|
||||
* **router:** allow specifying a matcher wihtout specifying a path ([bbb7a39](https://github.com/angular/angular/commit/bbb7a39)), closes [#12972](https://github.com/angular/angular/issues/12972)
|
||||
* **router:** allow specifying a matcher without specifying a path ([bbb7a39](https://github.com/angular/angular/commit/bbb7a39)), closes [#12972](https://github.com/angular/angular/issues/12972)
|
||||
* **router:** fix replaceUrl on RouterLink directives ([349ad75](https://github.com/angular/angular/commit/349ad75))
|
||||
* **router:** fix skipLocationChanges on RouterLink directives ([f562cbf](https://github.com/angular/angular/commit/f562cbf)), closes [#13156](https://github.com/angular/angular/issues/13156)
|
||||
* **router:** make setUpLocationChangeListener idempotent ([25e5b2f](https://github.com/angular/angular/commit/25e5b2f))
|
||||
* **router:** runs guards every time when unsuccessfully navigating to the same url over and over again ([#13209](https://github.com/angular/angular/issues/13209)) ([d46b8de](https://github.com/angular/angular/commit/d46b8de))
|
||||
* **router:** throw a better error message when angular 1 is not bootstraped ([c767df0](https://github.com/angular/angular/commit/c767df0))
|
||||
* **router:** throw a better error message when AngularJS is not bootstraped ([c767df0](https://github.com/angular/angular/commit/c767df0))
|
||||
* **router:** validate nested routes ([#13224](https://github.com/angular/angular/issues/13224)) ([2893c2c](https://github.com/angular/angular/commit/2893c2c)), closes [#12827](https://github.com/angular/angular/issues/12827)
|
||||
* **tsc-wrapped:** have UserError display the actual error ([393c100](https://github.com/angular/angular/commit/393c100))
|
||||
|
||||
@ -1648,7 +2264,7 @@ The likelihood of anyone actually depending on this property is very low.
|
||||
### Known Issues
|
||||
|
||||
*** SECURITY WARNING ***
|
||||
Contextual escaping is not yet implemented in Angular 2. This will be fixed in the upcoming RC.
|
||||
Contextual escaping is not yet implemented in Angular. This will be fixed in the upcoming RC.
|
||||
In the meantime make sure to correctly escape all values that go into the DOM.
|
||||
*** SECURITY WARNING ***
|
||||
|
||||
@ -2011,7 +2627,7 @@ it('should wait for returned promises', async(() => {
|
||||
* **payload:** increase payload size limit temporarily ([28e657d](https://github.com/angular/angular/commit/28e657d))
|
||||
* **RouterLink:** ignore optional parameters when checking for active routes ([5e2bc5c](https://github.com/angular/angular/commit/5e2bc5c)), closes [#6459](https://github.com/angular/angular/issues/6459) [#7834](https://github.com/angular/angular/issues/7834)
|
||||
* **select:** set value individually from ngModel ([e1e44a9](https://github.com/angular/angular/commit/e1e44a9)), closes [#7975](https://github.com/angular/angular/issues/7975) [#7978](https://github.com/angular/angular/issues/7978)
|
||||
* **upgrade:** make upgradeAdapter upgrade angular 1 components correctly ([247964a](https://github.com/angular/angular/commit/247964a)), closes [#7951](https://github.com/angular/angular/issues/7951)
|
||||
* **upgrade:** make upgradeAdapter upgrade AngularJS components correctly ([247964a](https://github.com/angular/angular/commit/247964a)), closes [#7951](https://github.com/angular/angular/issues/7951)
|
||||
|
||||
### Features
|
||||
|
||||
@ -2588,7 +3204,7 @@ setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS,
|
||||
* `angular2.umd.js` -> `angular2-all.umd.js`
|
||||
* `angular2-testing.umd.js` -> `angular2-all-testing.umd.js`
|
||||
|
||||
* RxJS used to be bundled with Angular 2 code and this is not the case
|
||||
* RxJS used to be bundled with Angular code and this is not the case
|
||||
any more. RxJS needs to be loaded explicitly.
|
||||
|
||||
* Previously `angular2.js`, `angular2.min.js` and `angular2.dev.js` bundles
|
||||
@ -3755,8 +4371,8 @@ class HelloCmp implements OnInit {
|
||||
* **docs:** export type info for var and const exports ([9262727](https://github.com/angular/angular/commit/9262727)), closes [#3700](https://github.com/angular/angular/issues/3700)
|
||||
* **facade:** add maximum method for ListWrapper ([b5c4d8b](https://github.com/angular/angular/commit/b5c4d8b))
|
||||
* **http:** xhr error listener invokes throw on EventEmitter ([f2d3bdb](https://github.com/angular/angular/commit/f2d3bdb)), closes [#2667](https://github.com/angular/angular/issues/2667)
|
||||
* **router:** add angular 1.x router ([fde026a](https://github.com/angular/angular/commit/fde026a))
|
||||
* **router:** add reuse support for angular 1.x router ([ddb62fe](https://github.com/angular/angular/commit/ddb62fe)), closes [#3698](https://github.com/angular/angular/issues/3698)
|
||||
* **router:** add AngularJS router ([fde026a](https://github.com/angular/angular/commit/fde026a))
|
||||
* **router:** add reuse support for AngularJS router ([ddb62fe](https://github.com/angular/angular/commit/ddb62fe)), closes [#3698](https://github.com/angular/angular/issues/3698)
|
||||
* **url_resolver:** Allow a developer to customize their package prefix ([9cc1cd2](https://github.com/angular/angular/commit/9cc1cd2)), closes [#3794](https://github.com/angular/angular/issues/3794)
|
||||
|
||||
|
||||
|
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)
|
||||
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.
|
||||
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
|
||||
Angular committers and also prevent breakages on master.
|
||||
Change approvals in our monorepo are managed via [pullapprove.com](https://about.pullapprove.com/) and are configured via the `.pullapprove.yaml` file.
|
||||
|
||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
||||
so the caretaker reviews the changes manually.
|
||||
|
||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
||||
is notified that the label was added by an authorized person,
|
||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
||||
# Merging
|
||||
|
||||
(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,
|
||||
Travis will re-base the commits, merge to master, and close the PR automatically.
|
||||
# Who is the Caretaker?
|
||||
|
||||
Finally, after merge `mary-poppins` removes the presubmit branch.
|
||||
|
||||
## 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)
|
||||
See [this explanation](https://twitter.com/IgorMinar/status/799365744806854656).
|
||||
|
@ -191,21 +191,44 @@ If the commit reverts a previous commit, it should begin with `revert: `, follow
|
||||
### Type
|
||||
Must be one of the following:
|
||||
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
* **docs**: Documentation only changes
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug 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)
|
||||
* **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
|
||||
The scope could be anything specifying place of the commit change. For example
|
||||
`Compiler`, `ElementInjector`, etc.
|
||||
The scope should be the name of the npm package affected (as perceived by person reading changelog generated from commit messages.
|
||||
|
||||
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
|
||||
The subject contains succinct description of the change:
|
||||
|
58
DEVELOPER.md
58
DEVELOPER.md
@ -1,6 +1,6 @@
|
||||
# Building and Testing Angular 2 for JS
|
||||
# Building and Testing Angular
|
||||
|
||||
This document describes how to set up your development environment to build and test Angular 2 JS version.
|
||||
This document describes how to set up your development environment to build and test Angular.
|
||||
It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||
|
||||
* [Prerequisite Software](#prerequisite-software)
|
||||
@ -71,9 +71,24 @@ 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
|
||||
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`.
|
||||
|
||||
## Installing Bower Modules
|
||||
|
||||
Now run `bower` to install additional dependencies:
|
||||
|
||||
```shell
|
||||
# Install other Angular project dependencies (bower.json)
|
||||
bower install
|
||||
```
|
||||
|
||||
## Windows only
|
||||
|
||||
In order to create the right symlinks, run **as administrator**:
|
||||
@ -124,7 +139,8 @@ If you happen to modify the public API of Angular, API golden files must be upda
|
||||
$ gulp public-api:update
|
||||
```
|
||||
|
||||
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
|
||||
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
|
||||
|
||||
@ -137,4 +153,38 @@ You can automatically format your code by running:
|
||||
$ 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
|
||||
```
|
||||
|
||||
## Publishing snapshot builds
|
||||
|
||||
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
|
||||
|
||||
You may find that your un-merged change needs some validation from external participants.
|
||||
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]
|
||||
```
|
||||
|
||||
For subsequent snapshots, just run
|
||||
|
||||
``` shell
|
||||
$ ./scripts/publish/publish-build-artifacts.sh [github username]
|
||||
```
|
||||
|
||||
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.
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
|
13
README.md
13
README.md
@ -5,16 +5,13 @@
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
|
||||
[](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 is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
||||
|
||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript (JS) and other languages.
|
||||
|
||||
|
||||
## Quickstart
|
||||
@ -29,9 +26,5 @@ guidelines for [contributing][contributing] and then check out one of our issues
|
||||
|
||||
[browserstack]: https://www.browserstack.com/
|
||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
[dart]: http://www.dartlang.org
|
||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
[ng2dart]: https://github.com/dart-lang/angular2
|
||||
[ng]: http://angular.io
|
||||
|
2
TOOLS.md
2
TOOLS.md
@ -1,4 +1,4 @@
|
||||
# Developer Tools for Angular 2
|
||||
# Developer Tools for Angular
|
||||
|
||||
Here you will find a collection of tools and tips for keeping your application
|
||||
perform well and contain fewer bugs.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 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. The basic idea of the process is that
|
||||
@ -24,8 +24,9 @@ with it.
|
||||
* `comp: forms`: `@kara`
|
||||
* `comp: http`: `@jeffbcross`
|
||||
* `comp: i18n`: `@vicb`
|
||||
* `comp: language service`: `@chuckjaz`
|
||||
* `comp: metadata-extractor`: `@chuckjaz`
|
||||
* `comp: router`: `@vsavkin`
|
||||
* `comp: router`: `@vicb`
|
||||
* `comp: testing`: `@juliemr`
|
||||
* `comp: upgrade`: `@mhevery`
|
||||
* `comp: web-worker`: `@vicb`
|
||||
@ -70,7 +71,7 @@ issues within the component will be resolved.
|
||||
|
||||
Several owners have adopted the issue categorization based on
|
||||
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
|
||||
used by Angular 1. In this system every issue is assigned frequency and
|
||||
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:
|
||||
|
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']});
|
||||
}
|
||||
};
|
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,
|
||||
];
|
39
aio/src/app/nav-engine/nav-engine.service.spec.ts
Normal file
39
aio/src/app/nav-engine/nav-engine.service.spec.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { fakeAsync, tick} from '@angular/core/testing';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import 'rxjs/add/operator/delay';
|
||||
|
||||
import { DocService } from './doc.service';
|
||||
import { Doc, DocMetadata, NavNode } from './doc.model';
|
||||
|
||||
import { NavEngine } from './nav-engine.service';
|
||||
|
||||
describe('NavEngine', () => {
|
||||
|
||||
let fakeDoc: Doc;
|
||||
let navEngine: NavEngine;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeDoc = {
|
||||
metadata: {
|
||||
docId: 'fake',
|
||||
title: 'All about the fake'
|
||||
},
|
||||
content: 'fake content'
|
||||
};
|
||||
|
||||
const docService: any = jasmine.createSpyObj('docService', ['getDoc']);
|
||||
docService.getDoc.and.returnValue(of(fakeDoc).delay(0));
|
||||
|
||||
navEngine = new NavEngine(docService);
|
||||
});
|
||||
|
||||
it('should set currentDoc to fake doc when navigate to fake id', fakeAsync(() => {
|
||||
navEngine.navigate('fake');
|
||||
navEngine.currentDoc.subscribe(doc =>
|
||||
expect(doc.content).toBe(fakeDoc.content)
|
||||
);
|
||||
tick();
|
||||
}));
|
||||
});
|
34
aio/src/app/nav-engine/nav-engine.service.ts
Normal file
34
aio/src/app/nav-engine/nav-engine.service.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { Doc } from './doc.model';
|
||||
import { DocService } from './doc.service';
|
||||
|
||||
@Injectable()
|
||||
export class NavEngine implements OnDestroy {
|
||||
|
||||
private docSubject = new ReplaySubject<Doc>(1);
|
||||
private subscription: Subscription;
|
||||
|
||||
/** Observable of the most recent document from a `navigate` call */
|
||||
currentDoc = this.docSubject.asObservable();
|
||||
|
||||
constructor(private docService: DocService) {}
|
||||
|
||||
/**
|
||||
* Navigate pushes new doc for the given `id` into the `currentDoc` observable.
|
||||
* TODO: handle document retrieval error
|
||||
*/
|
||||
navigate(docId: string) {
|
||||
this.ngOnDestroy();
|
||||
this.subscription = this.docService.getDoc(docId).subscribe(doc => this.docSubject.next(doc));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription) { this.subscription.unsubscribe(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
19
aio/src/app/nav-engine/nav-link.directive.ts
Normal file
19
aio/src/app/nav-engine/nav-link.directive.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Directive, HostListener, Input } from '@angular/core';
|
||||
import { NavEngine } from './nav-engine.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[aioNavLink]'
|
||||
})
|
||||
export class NavLinkDirective {
|
||||
|
||||
@Input()
|
||||
aioNavLink: string;
|
||||
|
||||
constructor(private navEngine: NavEngine) { }
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
onClick($event) {
|
||||
this.navEngine.navigate(this.aioNavLink);
|
||||
return false;
|
||||
}
|
||||
}
|
199
aio/src/app/nav-engine/nav-map.service.spec.ts
Normal file
199
aio/src/app/nav-engine/nav-map.service.spec.ts
Normal file
@ -0,0 +1,199 @@
|
||||
import { fakeAsync, tick } from '@angular/core/testing';
|
||||
import { Http, Response } from '@angular/http';
|
||||
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/delay';
|
||||
|
||||
import { DocFetchingService } from './doc-fetching.service';
|
||||
import { NavNode, NavMap } from './doc.model';
|
||||
import { NavMapService } from './nav-map.service';
|
||||
|
||||
import { getTestNavMapResponse } from '../../testing/nav-map-json-response';
|
||||
|
||||
describe('NavMapService', () => {
|
||||
let httpSpy: any;
|
||||
let loggerSpy: any;
|
||||
let navMapService: NavMapService;
|
||||
let navMap: NavMap;
|
||||
|
||||
beforeEach(done => {
|
||||
httpSpy = jasmine.createSpyObj('http', ['get']);
|
||||
httpSpy.get.and.returnValue(of(getTestNavMapResponse()).delay(0).first()); // first() -> completes
|
||||
loggerSpy = jasmine.createSpyObj('logger', ['log', 'warn', 'error']);
|
||||
|
||||
navMapService = new NavMapService(new DocFetchingService(null, null), httpSpy, loggerSpy);
|
||||
|
||||
navMapService.navMap.first().subscribe(
|
||||
nm => navMap = nm,
|
||||
null,
|
||||
done);
|
||||
});
|
||||
|
||||
it('should return a navMap', () => {
|
||||
expect(navMap).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have filtered away the "cli-quickstart" because `hide`===true', () => {
|
||||
const item = navMap.nodes.find(n => n.docId === 'guide/cli-quickstart');
|
||||
expect(item).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('Quickstart', () => {
|
||||
let item: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
item = navMap.nodes.find(n => n.navTitle === 'Quickstart');
|
||||
});
|
||||
|
||||
it('should have expected item', () => {
|
||||
expect(item).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have expected docId', () => {
|
||||
expect(item.docId).toBe('guide/quickstart');
|
||||
});
|
||||
|
||||
it('should have calculated expected docPath', () => {
|
||||
expect(item.docPath).toBe('content/guide/quickstart.html');
|
||||
});
|
||||
|
||||
it('should have no ancestors because it is a top-level item', () => {
|
||||
expect(item.ancestorIds).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Getting Started', () => {
|
||||
let section: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
section = navMap.nodes.find(n => n.navTitle === 'Getting started');
|
||||
});
|
||||
|
||||
it('should have an id', () => {
|
||||
expect(section.id).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have distinct tooltip', () => {
|
||||
expect(section.tooltip).not.toBe(section.navTitle);
|
||||
});
|
||||
|
||||
it('should have 2 children', () => {
|
||||
expect(section.children.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should have itself as ancestor because it has children', () => {
|
||||
expect(section.ancestorIds).toEqual([section.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tutorial', () => {
|
||||
let section: NavNode;
|
||||
let intro: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
section = navMap.nodes.find(n => n.navTitle === 'Tutorial');
|
||||
if (section && section.children) {
|
||||
intro = section.children.find(n => n.navTitle === 'Introduction');
|
||||
}
|
||||
});
|
||||
|
||||
it('should have 2 children', () => {
|
||||
expect(section.children.length).toBe(2);
|
||||
});
|
||||
|
||||
it('intro child\'s docId ends in "/"', () => {
|
||||
expect(intro.docId[intro.docId.length - 1]).toEqual('/');
|
||||
});
|
||||
|
||||
it('intro child\'s calculated docPath ends in "index.html"', () => {
|
||||
expect(intro.docPath).toMatch(/index.html$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Core (3-level)', () => {
|
||||
let section: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
section = navMap.nodes.find(n => n.navTitle === 'Core');
|
||||
});
|
||||
|
||||
it('should have 2 children', () => {
|
||||
expect(section.children.length).toBe(2);
|
||||
});
|
||||
|
||||
describe('->directives', () => {
|
||||
let directives: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
directives = section.children.find(n => n.navTitle === 'Directives');
|
||||
});
|
||||
|
||||
it('should have a heading docId', () => {
|
||||
expect(directives.docId).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have calculated expected docPath', () => {
|
||||
expect(directives.docPath).toBe('content/guide/directives.html');
|
||||
});
|
||||
|
||||
it('should have 2 children', () => {
|
||||
expect(directives.children.length).toBe(2);
|
||||
});
|
||||
|
||||
it('children should have two ancestor ids in lineal order', () => {
|
||||
const expectedAncestors = [section.id, directives.id];
|
||||
expect(directives.children[0].ancestorIds).toEqual(expectedAncestors, '#1');
|
||||
expect(directives.children[1].ancestorIds).toEqual(expectedAncestors, '#2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Empty Heading', () => {
|
||||
let section: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
section = navMap.nodes.find(n => n.navTitle === 'Empty Heading');
|
||||
});
|
||||
|
||||
it('should have no children', () => {
|
||||
expect(section.children.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should have itself as ancestor because it has a `children` array', () => {
|
||||
expect(section.ancestorIds).toEqual([section.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('External', () => {
|
||||
let section: NavNode;
|
||||
let gitter: NavNode;
|
||||
|
||||
beforeEach(() => {
|
||||
section = navMap.nodes.find(n => n.navTitle === 'External');
|
||||
if (section && section.children) {
|
||||
gitter = section.children[0];
|
||||
}
|
||||
});
|
||||
|
||||
it('should have one child (gitter)', () => {
|
||||
expect(section.children.length).toBe(1);
|
||||
});
|
||||
|
||||
it('child should have a url', () => {
|
||||
expect(gitter.url).toBeTruthy();
|
||||
});
|
||||
|
||||
it('child should not have a docId', () => {
|
||||
expect(gitter.docId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('child should not have a docPath', () => {
|
||||
expect(gitter.docPath).toBeUndefined();
|
||||
});
|
||||
|
||||
it('child should have parent as only ancestor id', () => {
|
||||
expect(gitter.ancestorIds).toEqual([section.id]);
|
||||
});
|
||||
});
|
||||
});
|
88
aio/src/app/nav-engine/nav-map.service.ts
Normal file
88
aio/src/app/nav-engine/nav-map.service.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { Doc, NavNode, NavMap } from './doc.model';
|
||||
import { DocFetchingService } from './doc-fetching.service';
|
||||
import { Logger } from '../logger.service';
|
||||
|
||||
const navMapUrl = 'content/navmap.json';
|
||||
|
||||
@Injectable()
|
||||
export class NavMapService {
|
||||
|
||||
private getDocPath: (string) => string;
|
||||
private navMapSubject: ReplaySubject<NavMap>;
|
||||
private nextNodeId = 1;
|
||||
|
||||
constructor(
|
||||
docFetchingService: DocFetchingService,
|
||||
private http: Http,
|
||||
private logger: Logger) {
|
||||
this.getDocPath = docFetchingService.getPath.bind(docFetchingService);
|
||||
}
|
||||
|
||||
get navMap(): Observable<NavMap> {
|
||||
return (this.navMapSubject ? this.navMapSubject : this.createNavMapSubject()).asObservable() ;
|
||||
}
|
||||
|
||||
private createNavMapSubject(): ReplaySubject<NavMap> {
|
||||
this.navMapSubject = new ReplaySubject<NavMap>(1);
|
||||
|
||||
this.http.get(navMapUrl)
|
||||
.map(res => res.json().nodes)
|
||||
.do(() => this.logger.log(`Fetched navigation map JSON at '${navMapUrl}'`))
|
||||
.subscribe(
|
||||
navNodes => this.navMapSubject.next(this.createNavMap(navNodes))
|
||||
);
|
||||
|
||||
return this.navMapSubject;
|
||||
}
|
||||
|
||||
|
||||
////// private helper functions ////
|
||||
|
||||
private createNavMap(nodes: NavNode[]) {
|
||||
nodes = this.removeHidden(nodes);
|
||||
const navMap: NavMap = { nodes, docs: new Map<string, NavNode>()};
|
||||
nodes.forEach(node => this.adjustNode(node, navMap, []));
|
||||
return navMap;
|
||||
}
|
||||
|
||||
// Adjust properties of a node from JSON and build navMap.docs
|
||||
private adjustNode(node: NavNode, navMap: NavMap, ancestorIds: number[] ) {
|
||||
node.id = this.nextNodeId++;
|
||||
node.ancestorIds = ancestorIds;
|
||||
if ( node.tooltip === undefined ) { node.tooltip = node.navTitle; }
|
||||
|
||||
if (node.docId) {
|
||||
// This node is associated with a document
|
||||
node.docId = node.docId.toLocaleLowerCase();
|
||||
node.docPath = this.getDocPath(node.docId);
|
||||
navMap.docs.set(node.docId, node);
|
||||
}
|
||||
|
||||
|
||||
if (node.children) {
|
||||
// Ancestors include self when this node has children
|
||||
node.ancestorIds = ancestorIds.concat(node.id);
|
||||
node.children.forEach(n => this.adjustNode(n, navMap, node.ancestorIds));
|
||||
}
|
||||
}
|
||||
|
||||
private removeHidden(nodes: NavNode[]) {
|
||||
return nodes.filter(node => {
|
||||
if (node['hide'] === true ) { return false; }
|
||||
if (node.children) {
|
||||
node.children = this.removeHidden(node.children);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
111
aio/src/app/search/search-worker-client.ts
Normal file
111
aio/src/app/search/search-worker-client.ts
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2016 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 http://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgZone} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subscriber} from 'rxjs/Subscriber';
|
||||
import 'rxjs/add/observable/fromPromise';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
|
||||
|
||||
/**
|
||||
* We will use this client from a component with something like...
|
||||
*
|
||||
* ngOnInit() {
|
||||
* const searchWorker = new SearchWorkerClient('app/search-worker.js', this.zone);
|
||||
* this.indexReady = searchWorker.ready;
|
||||
* this.searchInput = new FormControl();
|
||||
* this.searchResult$ = this.searchInput.valueChanges
|
||||
* .switchMap((searchText: string) => searchWorker.search(searchText));
|
||||
* }
|
||||
*
|
||||
* TODO(petebd): do we need a fallback for browsers that do not support service workers?
|
||||
*/
|
||||
|
||||
type QueryResults = Object[];
|
||||
|
||||
export interface ResultsReadyMessage {
|
||||
type: 'query-results';
|
||||
id: number;
|
||||
query: string;
|
||||
results: QueryResults;
|
||||
}
|
||||
|
||||
export class SearchWorkerClient {
|
||||
ready: Promise<boolean>;
|
||||
worker: Worker;
|
||||
private _queryId = 0;
|
||||
|
||||
constructor(url: string, private zone: NgZone) {
|
||||
this.worker = new Worker(url);
|
||||
this.ready = this._waitForIndex(this.worker);
|
||||
}
|
||||
|
||||
search(query: string) {
|
||||
return Observable.fromPromise(this.ready)
|
||||
.switchMap(() => this._createQuery(query));
|
||||
}
|
||||
|
||||
private _waitForIndex(worker: Worker) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
worker.onmessage = (e) => {
|
||||
if (e.data.type === 'index-ready') {
|
||||
resolve(true);
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = (e) => {
|
||||
reject(e);
|
||||
cleanup();
|
||||
};
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
worker.onmessage = null;
|
||||
worker.onerror = null;
|
||||
}
|
||||
}
|
||||
|
||||
private _createQuery(query: string) {
|
||||
return new Observable<QueryResults>((subscriber: Subscriber<QueryResults>) => {
|
||||
|
||||
// get a new identifier for this query that we can match to results
|
||||
const id = this._queryId++;
|
||||
|
||||
const handleMessage = (message: MessageEvent) => {
|
||||
const {type, id: queryId, results} = message.data as ResultsReadyMessage;
|
||||
if (type === 'query-results' && id === queryId) {
|
||||
this.zone.run(() => {
|
||||
subscriber.next(results);
|
||||
subscriber.complete();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleError = (error: ErrorEvent) => {
|
||||
this.zone.run(() => {
|
||||
subscriber.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
// Wire up the event listeners for this query
|
||||
this.worker.addEventListener('message', handleMessage);
|
||||
this.worker.addEventListener('error', handleError);
|
||||
|
||||
// Post the query to the web worker
|
||||
this.worker.postMessage({query, id});
|
||||
|
||||
// At completion/error unwire the event listeners
|
||||
return () => {
|
||||
this.worker.removeEventListener('message', handleMessage);
|
||||
this.worker.removeEventListener('error', handleError);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user