Compare commits
972 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da984ad187 | |||
| 4015357ce5 | |||
| dc3d11ad19 | |||
| 0e1545eb04 | |||
| ec7cabf5c9 | |||
| 3051beba2f | |||
| 92304323b1 | |||
| c28123a872 | |||
| d798423813 | |||
| 2583e77cc7 | |||
| f66836fee4 | |||
| 0ccc445d53 | |||
| b7d5fa1cbe | |||
| 8bb3942453 | |||
| 51a79cebcb | |||
| 36bcf64008 | |||
| 5c6630605b | |||
| 125827406c | |||
| 62c21422a6 | |||
| 98d489712e | |||
| 2cab2d8ef1 | |||
| 8fa2bb72bc | |||
| d151f94937 | |||
| 5b74b7185b | |||
| 4f0be2ae4e | |||
| bb9badeb2a | |||
| c287c8361d | |||
| ade7127c79 | |||
| d341483f1f | |||
| b36acbc857 | |||
| a4fea38b94 | |||
| 300c5c0c99 | |||
| 152537c4e9 | |||
| 8b46bf6bc9 | |||
| aef861eb41 | |||
| f61d36861d | |||
| 2af0348cea | |||
| 78c5743494 | |||
| 2cb9fbd043 | |||
| e9dad5dbf4 | |||
| 54895fc2a1 | |||
| 60a12b4161 | |||
| cd7e58ba41 | |||
| 9391475dc3 | |||
| 7840803add | |||
| 7d77de2834 | |||
| ab044cada6 | |||
| d010e0cc7d | |||
| 40f728b1aa | |||
| 23abb26405 | |||
| fd55bc8e1d | |||
| 541aaa4e08 | |||
| f22c422547 | |||
| 0e461f0c07 | |||
| 5074448443 | |||
| 8d66af11e6 | |||
| 169948bb47 | |||
| 58d9469574 | |||
| 8d858a2360 | |||
| 5540748890 | |||
| f8a52be817 | |||
| 3b5f1105f6 | |||
| 663ccc5449 | |||
| 263f47819f | |||
| 6b75475ce3 | |||
| 07c354a8c0 | |||
| 1391579599 | |||
| 5d2bd1d84c | |||
| bf77e212af | |||
| eef2f9c31e | |||
| 438627c2c3 | |||
| d3952b79c7 | |||
| 5ef9ed87d8 | |||
| 8c81a0f372 | |||
| bde931afd5 | |||
| 6553fe68d1 | |||
| 13b5fd1b9d | |||
| 17209d5b4a | |||
| 6f9a5721bc | |||
| 31c825607d | |||
| ab6937e251 | |||
| fbfda241f6 | |||
| 206371b737 | |||
| b6b92bd866 | |||
| 79f2d843a8 | |||
| 64a9cd8f4f | |||
| 7f6e1326f3 | |||
| 1fd2b3d402 | |||
| d56d69cc83 | |||
| 01e726b2fa | |||
| 1613621645 | |||
| 92a3d28218 | |||
| 4c58501956 | |||
| c076fe08cf | |||
| 2473412ba5 | |||
| 1f2d50000e | |||
| 5026315d6f | |||
| a8b04004e3 | |||
| f0a090ddf2 | |||
| 6d9313a68d | |||
| 212f685e06 | |||
| 35706ba482 | |||
| 9bef436b22 | |||
| 3f14a45aa5 | |||
| ffb270130a | |||
| 0d57f1373f | |||
| 869143ec7d | |||
| 1e6d4d5f54 | |||
| 3da4194f98 | |||
| ad5d2f2991 | |||
| 519bef4f3d | |||
| 6dfe5be155 | |||
| 6593a3e082 | |||
| 0f44964e5e | |||
| f6b09b9139 | |||
| 7fa1995e08 | |||
| da94b03af7 | |||
| f158d81d21 | |||
| 9af7a9198e | |||
| 74fa65ecb7 | |||
| ee6014a3aa | |||
| d9ff5fd432 | |||
| f16150d5f1 | |||
| fc0b2b5715 | |||
| f3f090da8a | |||
| 068f2f9d43 | |||
| 2d48733723 | |||
| d37d595b67 | |||
| 5d70e4a89c | |||
| b5bba65a93 | |||
| fb99b539b4 | |||
| 10f80d7d29 | |||
| c3a41ff9fe | |||
| 5c95b8cccc | |||
| 9be82d942f | |||
| 2491319575 | |||
| bcc3a021eb | |||
| a57141fd1d | |||
| 1904596e0c | |||
| 22143381d8 | |||
| ddefb42445 | |||
| 676d6e0040 | |||
| 8024a5742c | |||
| 073e76f835 | |||
| 7019f142ab | |||
| 0532aabcf9 | |||
| f0be543614 | |||
| a3a37c2063 | |||
| 0f5259c5a2 | |||
| 321a4a6b1f | |||
| 41d26db32c | |||
| dd38ce6585 | |||
| 2e90cdc3d4 | |||
| 581f93ae56 | |||
| 6933fb7924 | |||
| f5afcca99d | |||
| 2356c21650 | |||
| 275e5335dc | |||
| 92a2e18076 | |||
| 8aa18f0ad0 | |||
| 85632cb44c | |||
| 0a6e464a93 | |||
| 7c24282188 | |||
| 885fb0dd07 | |||
| 4361efb03b | |||
| 416a783040 | |||
| bbaf9a2870 | |||
| 7e70463da1 | |||
| 4235ee9ad6 | |||
| 3fdb29242b | |||
| b5fb18ae35 | |||
| 5fdf42ce39 | |||
| bf6a0b7289 | |||
| 989446ecee | |||
| 5214c1d0cb | |||
| 4511d39cc7 | |||
| 15b8f205bb | |||
| 1d388676e3 | |||
| 84542d2431 | |||
| 91db99208e | |||
| acf095d178 | |||
| 301d8f233b | |||
| d70223e53e | |||
| 8ad02bb5a8 | |||
| ec1c5dfaee | |||
| 24e7da4f19 | |||
| 7b739c9702 | |||
| c1533ef576 | |||
| 679cb8a74a | |||
| 4e65635f85 | |||
| aa02534865 | |||
| b99f65f64d | |||
| f76474823a | |||
| 8ba1fd87e1 | |||
| 4d2dd46483 | |||
| b24cc63bcb | |||
| 49dfdf8f02 | |||
| 5bcb749abb | |||
| 499a76a08c | |||
| 8e2675029f | |||
| d0159454df | |||
| 7f0eb15161 | |||
| c4fa487250 | |||
| cef3535c16 | |||
| fbb499e0a8 | |||
| e40f8d829f | |||
| 9c0418cf1a | |||
| 1564b82b49 | |||
| b431ee3850 | |||
| a44d3dcd6a | |||
| ee579a071a | |||
| 5df7e6fae5 | |||
| fff31d8d61 | |||
| 9cba23a588 | |||
| 705f4bbf11 | |||
| bd530e2257 | |||
| 843f762c57 | |||
| beea3a4bed | |||
| 3bd3cc571d | |||
| c7f1101520 | |||
| 76afa406b1 | |||
| f3c77858be | |||
| 96758c1c52 | |||
| 006fb4fbeb | |||
| 075c089b5c | |||
| 2b87c814ab | |||
| 2b1b257034 | |||
| 73caf76225 | |||
| dbb92efd13 | |||
| 1214084e9d | |||
| a18926f986 | |||
| b806b30861 | |||
| 43d15f830f | |||
| 1d26acb874 | |||
| 983c309542 | |||
| 904b69c745 | |||
| c65c34ebfe | |||
| 8ebe5ccd9a | |||
| e61fd1b43a | |||
| ce15a3e049 | |||
| 46bb08a9d0 | |||
| 94dd685709 | |||
| dc32ea627e | |||
| eafe15f54c | |||
| 666f326c5d | |||
| 908785960d | |||
| 5cc245dd80 | |||
| 0bd0ef7813 | |||
| 0c7252f929 | |||
| b94fb5c8c1 | |||
| c322735f83 | |||
| 9260b4937d | |||
| e9ccec76a6 | |||
| 2037facc99 | |||
| b2d0a386f6 | |||
| 6d7e7fdea6 | |||
| df72852f34 | |||
| c4f6ccb065 | |||
| 0c49bbdc38 | |||
| 7d074a3775 | |||
| dceafd32ee | |||
| 0a5050eb3c | |||
| 7c430c5ed0 | |||
| 93d62860e9 | |||
| 5bcd719866 | |||
| e1743cc837 | |||
| 52ee1ab5eb | |||
| fcc556df37 | |||
| 5c0ec9d06d | |||
| ac2f0cece6 | |||
| fbaa1968b7 | |||
| 13d5528a5f | |||
| b5406d276d | |||
| 0f89383d98 | |||
| 10daefc6f4 | |||
| dc7b764d4d | |||
| 82d90a4096 | |||
| 7468bcb80b | |||
| bd4a4d390c | |||
| 94fca76a08 | |||
| 1c8c083404 | |||
| 0f2de12273 | |||
| 1bbc67ef6c | |||
| 637817e3ba | |||
| 86182a9415 | |||
| 15ecc6f366 | |||
| 53b2254ea7 | |||
| 6336b6e89e | |||
| fdf17d729f | |||
| 85776c0d37 | |||
| 02cf958a07 | |||
| 8fe4295a06 | |||
| dcb8e0767f | |||
| 21b77ad5c2 | |||
| 2f5dba488e | |||
| 7e86eacf30 | |||
| 15c1fe3929 | |||
| 428f2b5636 | |||
| 199ac26986 | |||
| 5f70d615a5 | |||
| 06d0955074 | |||
| a22e0699be | |||
| 28ff7c3a66 | |||
| 59ae8adb3c | |||
| c0b78478a0 | |||
| 59fa40ec0e | |||
| a1f7f5d4d0 | |||
| 20687aa5f6 | |||
| fc52b81d52 | |||
| ae1aee2b6c | |||
| 423242017e | |||
| 95c5df5958 | |||
| 2cb907a836 | |||
| 2f2fd465a4 | |||
| 6da355c3e1 | |||
| f2106692b1 | |||
| 4557881cf8 | |||
| af0ad6561c | |||
| 35125d2513 | |||
| 87f5c6e5b7 | |||
| a8a750ab05 | |||
| 13a95ae499 | |||
| da9f4dfcf4 | |||
| ac4318a2fa | |||
| bb2fa6f63f | |||
| ba59ef4950 | |||
| 8b93541522 | |||
| 7b22d59b4a | |||
| 798bca62c6 | |||
| 8218c4b60b | |||
| 2430f52bb9 | |||
| 944098a4e0 | |||
| 2ce0485e6f | |||
| a08cbc02e7 | |||
| 55027132f3 | |||
| 09e175f02c | |||
| 5c5b1183c8 | |||
| f04142ea28 | |||
| aaedefb92e | |||
| d54dfecb00 | |||
| 4b8d926062 | |||
| 74c84501ed | |||
| 4581b79bbd | |||
| 2be8847ef6 | |||
| cb2ad9abf2 | |||
| 73c8593077 | |||
| ac75079e21 | |||
| 5390fb37d2 | |||
| 8d7e694849 | |||
| 5fdab52dd7 | |||
| 541bedd1a9 | |||
| 98e18a64aa | |||
| 0a45bff472 | |||
| 263524d381 | |||
| 52c59cf0ce | |||
| c5f8edfe03 | |||
| 69f0aa899d | |||
| e7cd0bcc5a | |||
| ade6c45275 | |||
| 9eafd10fcd | |||
| 3436c027f2 | |||
| 6a8749e65a | |||
| 1a5bebd927 | |||
| 83155e8fbe | |||
| 6d6f875345 | |||
| a4fe51da3b | |||
| ee5a5352fd | |||
| 9cb2195e61 | |||
| 15213ec212 | |||
| 9171c76bb4 | |||
| 64fb1f2620 | |||
| f49eaf8bf2 | |||
| f701ce08f9 | |||
| 1cc0e4173d | |||
| d4ae7988da | |||
| 5ac14f633a | |||
| 9918b748be | |||
| 6ecac8e71a | |||
| 823adb2319 | |||
| 21e74c2d2e | |||
| 6c5a05ad49 | |||
| 192ff61f5d | |||
| 935c1018da | |||
| 78a6291666 | |||
| 53b6f522a5 | |||
| 1faafa3158 | |||
| 08bfea183a | |||
| f13dd3393d | |||
| bca96e7c7c | |||
| 9b1aff905b | |||
| 252d4548f9 | |||
| 6abafcb424 | |||
| 2315d9b361 | |||
| 8fd1b74872 | |||
| 02091b2c1e | |||
| 25cd774abf | |||
| c70ead0aa1 | |||
| 716b5fd3e2 | |||
| 8b8fdddc0b | |||
| ce4b630524 | |||
| 13f31602f3 | |||
| 7b52586f7c | |||
| e9e3ee012b | |||
| de9464c143 | |||
| 31cd580310 | |||
| d34f3bc7a6 | |||
| 027801a00a | |||
| 66e6c1ce2c | |||
| 4806d28a29 | |||
| 089c0f8b0e | |||
| b6ae6e52f9 | |||
| 9277d12fc0 | |||
| ac5151a469 | |||
| 63be222326 | |||
| a29c2cf70c | |||
| afe617a647 | |||
| f59e4b11f1 | |||
| 5e6ba25201 | |||
| 9134f5ce5a | |||
| 4e6b065a2f | |||
| 5e3db61b1d | |||
| a9ed5745a0 | |||
| 48096048cf | |||
| 317adb36a4 | |||
| 1b9277bf6f | |||
| cce31d4c93 | |||
| 3e5377f4f3 | |||
| 488a03631e | |||
| 716dd5f3f9 | |||
| 83314913e7 | |||
| e0cc84ad7b | |||
| 4a94bb9b34 | |||
| 53aacb35fa | |||
| f4d338d393 | |||
| 0bfaa579c0 | |||
| 00d4427388 | |||
| e0c9551fd7 | |||
| fae84463e4 | |||
| 8fb34f008e | |||
| 5d09a1efd3 | |||
| f54db2ccda | |||
| dd7b0f56fc | |||
| b3750103cc | |||
| b348347dad | |||
| 512db03cc0 | |||
| ee7209fe26 | |||
| 772ddb983b | |||
| 7f6c1093f5 | |||
| 1b4289ce76 | |||
| af21233820 | |||
| 08ad4b6a46 | |||
| 2acd60df4d | |||
| e0ace15cd3 | |||
| 6a98c52c84 | |||
| 6aa3cfc31b | |||
| 64912069ca | |||
| b49ddf9848 | |||
| 1084ccf7ef | |||
| c2989f6cc6 | |||
| 4f797fe5f3 | |||
| bbd3a3fd76 | |||
| e86bafecd2 | |||
| e68c02c537 | |||
| 25d207c48c | |||
| 4370d756e4 | |||
| 4e83399570 | |||
| e7d6106811 | |||
| c4c60c25b4 | |||
| 139e1b09a9 | |||
| 60743fc52a | |||
| 9486590e1b | |||
| e31d1c287d | |||
| f16bd2f747 | |||
| ef7346ff70 | |||
| f6fb31e8ad | |||
| 21c725f1a1 | |||
| e23fa768aa | |||
| d656d11489 | |||
| b37e8a2b14 | |||
| 4c1c50fd9b | |||
| d1558d7924 | |||
| 5b0d068358 | |||
| 230f29d0a7 | |||
| 3171f21591 | |||
| d6e3e1baab | |||
| ffa8441886 | |||
| 5d8528cc2e | |||
| 80edcadb1d | |||
| c27a56f4da | |||
| fbcb7fdd14 | |||
| fa69d10122 | |||
| dd321c5f4d | |||
| 656a495e50 | |||
| 6d0ca95fa0 | |||
| 3df7b8e57f | |||
| 7bd69d0f5b | |||
| 3773323e46 | |||
| 78656fe0df | |||
| cb10ccc44f | |||
| 4a051efb89 | |||
| 1752c8c44a | |||
| 6216dc0465 | |||
| 761b2ed85a | |||
| c8ee631c19 | |||
| cae9ad4ba9 | |||
| 85b2084f57 | |||
| 13b21aaf5a | |||
| 22c1db1744 | |||
| 292a5dae07 | |||
| 6e635012fb | |||
| eb92735c9e | |||
| 776739299b | |||
| 3173d8603d | |||
| 6c4f1391bc | |||
| 58d6da556a | |||
| b6f61a89cf | |||
| 8b32900d72 | |||
| 18a1e860a3 | |||
| 39b3297fc3 | |||
| 1268fc1a44 | |||
| 4804c83b7d | |||
| e2b1d9e994 | |||
| 9ee2cdff44 | |||
| 8af4fde182 | |||
| 5001c1a121 | |||
| 0f6b2ef982 | |||
| 1e258d11d0 | |||
| 81a6601e05 | |||
| 1e96d0af8c | |||
| 97dae0d0a0 | |||
| 84823b2eff | |||
| 517811764d | |||
| 1354718365 | |||
| 56bcc04c54 | |||
| b2052d08a1 | |||
| 7da2bdb82a | |||
| ed78f0d830 | |||
| dbffbefb7c | |||
| 0196411dbe | |||
| 992c790f07 | |||
| f5343c9fd3 | |||
| 0470ff04b4 | |||
| efe33a5e21 | |||
| 7046d6053d | |||
| afc241bd28 | |||
| a507fb7bb3 | |||
| 0ce139c42d | |||
| b00262fffe | |||
| 3f98d6ac99 | |||
| 22309c312f | |||
| fcf95a47d1 | |||
| e1e7aca9a6 | |||
| 039041e3ae | |||
| 3da441b580 | |||
| f9502d2ad3 | |||
| 0356c90af8 | |||
| 0d4def452e | |||
| 897d0f1424 | |||
| 92af30ce6e | |||
| 54581d36df | |||
| b587091b6e | |||
| c49b8a2db5 | |||
| 5cdfe45aa3 | |||
| 16a40c626f | |||
| b7f4d8c3c3 | |||
| 939c8e8fac | |||
| d2ba4c5170 | |||
| 46691c2721 | |||
| e7a23e4b65 | |||
| 15fd735793 | |||
| 985d3d7558 | |||
| 249c89c091 | |||
| 5164ae545b | |||
| e1e0ddb910 | |||
| d648d709f3 | |||
| 9a8dbfef51 | |||
| 28114de8dc | |||
| c6ea1be053 | |||
| 5143e7bf06 | |||
| afd25446d2 | |||
| 3c3e6980b3 | |||
| e0b4b107ee | |||
| 614fd3d55a | |||
| 7146f70636 | |||
| 11cb9423a7 | |||
| c76a120bfe | |||
| b8960c3710 | |||
| 67338ce061 | |||
| 23f8da7cbb | |||
| b911e303ec | |||
| a13b5ed3bc | |||
| 63cca9afbc | |||
| d47ec772c3 | |||
| 5c19766063 | |||
| f2119c7524 | |||
| 08029c7b72 | |||
| 0bf611087b | |||
| acb4338b70 | |||
| cd9a7b9608 | |||
| 1dccaaaaa2 | |||
| 9632f5c1c7 | |||
| bb3be87606 | |||
| 174952e443 | |||
| 6f91ffeb91 | |||
| c594f75b4c | |||
| 50eb7f15b8 | |||
| 212a6ff29a | |||
| 871252ab4c | |||
| 0c534644bc | |||
| c28662d28d | |||
| b97c6e5f74 | |||
| 4e3c05b99e | |||
| 5e4d59adf0 | |||
| fd38655e6c | |||
| b9001e9147 | |||
| d1e7a5394a | |||
| 2090136dd8 | |||
| c9f2b1eec5 | |||
| 163e05ed36 | |||
| 2986a09c0d | |||
| bb2e7488fa | |||
| 44b2f44f93 | |||
| 1d14760c6d | |||
| baa7af0df0 | |||
| f43c226c67 | |||
| 0e1fa2aefe | |||
| 3d0ce0ebe9 | |||
| b00da987a9 | |||
| 188bdf7768 | |||
| dbd880cc0a | |||
| bf8e0540f8 | |||
| 78b6e8a446 | |||
| b656552d68 | |||
| 1cdfa3b960 | |||
| 16363d8000 | |||
| 92995bbce9 | |||
| b9707d910e | |||
| 5bbd64ac65 | |||
| caeb1bf899 | |||
| 9b4efa73f9 | |||
| 4aaa2f7f6b | |||
| 6290bd4587 | |||
| e9f81b6631 | |||
| afbe073121 | |||
| 7b705df2b7 | |||
| a4c8ac7126 | |||
| e3e2e4436e | |||
| 972c3e9be0 | |||
| feacf608ee | |||
| fe633dd0cf | |||
| fdcc2dbfd3 | |||
| 5ad0c7d0e4 | |||
| 540701a8d8 | |||
| 4d2d70e7fb | |||
| cd28a2e952 | |||
| 59adadca08 | |||
| 497839f583 | |||
| 5487bdb3d1 | |||
| 3ae3ccf3da | |||
| e9b57f9df8 | |||
| 45f47ff6cd | |||
| 0c8b35681e | |||
| a035e88397 | |||
| 3548fe3139 | |||
| 29f9e2665d | |||
| 8d1944851d | |||
| 3e1a6688c3 | |||
| aba9bb2a24 | |||
| 5857c44e0c | |||
| 8adae2fdf2 | |||
| 955551141d | |||
| 94e1a07b28 | |||
| ac73e8877e | |||
| e88dfb734a | |||
| 8d6dc0b9a7 | |||
| acbd7cdf32 | |||
| 035c751076 | |||
| 186a840cd3 | |||
| b09595a3c1 | |||
| f6d98f1472 | |||
| 5279de0e70 | |||
| 8fe77b69e8 | |||
| 1cc6bee4ce | |||
| a8aa193c6b | |||
| e45b013143 | |||
| ea18f4548d | |||
| 57c37a21d1 | |||
| 74fac45f48 | |||
| f0fa5e6376 | |||
| c283bf6035 | |||
| b3c17f3fdc | |||
| 9c06394376 | |||
| 085e3c611f | |||
| 4b35a59c6a | |||
| 7cb03c5ab9 | |||
| 78c7066422 | |||
| 923da410bd | |||
| a87f2fb9e4 | |||
| c27aba4354 | |||
| dd9151e522 | |||
| 3972d2a89b | |||
| cb6f832f38 | |||
| 6022f3df39 | |||
| 7c11531902 | |||
| c6d2549a52 | |||
| bee6060e4b | |||
| 16597e8b52 | |||
| f684f20c99 | |||
| bd04316a89 | |||
| ed36b9da3b | |||
| c925f8a657 | |||
| 4c10d33eb4 | |||
| 9062996a0e | |||
| 411c1ae77e | |||
| d12df0d360 | |||
| d9b58f23f6 | |||
| 03dd8c4f4c | |||
| 48697a2b86 | |||
| 93b777c916 | |||
| 5c70ff72e2 | |||
| 5e663c3dc7 | |||
| 260725efcd | |||
| 4afad1da29 | |||
| eb01fe593d | |||
| fc7834f9ac | |||
| e4303a1f3a | |||
| 1e00db8daa | |||
| aaa0179758 | |||
| f5ef3724ce | |||
| e60601be4f | |||
| e2663f62b0 | |||
| 9f9ed4c5ff | |||
| 66fc268aeb | |||
| 1d966f8a65 | |||
| ddf6f1143f | |||
| 2636105c5e | |||
| c0b557a96c | |||
| 84873e7f4e | |||
| 95fdb1231f | |||
| ef875ad0cf | |||
| 615841a5d3 | |||
| 7d0c256ecd | |||
| 6cbe096dbf | |||
| 21602b5cd6 | |||
| 4ae671ac88 | |||
| 28ed5ba465 | |||
| 02dc81bae0 | |||
| 445680f601 | |||
| bf729d550b | |||
| dc8ffa51b7 | |||
| d7ba5bc83b | |||
| 950d02b4d4 | |||
| 578e38e0af | |||
| af7c51ee1d | |||
| 25d1822bd8 | |||
| 3945f884c5 | |||
| d5ccabce60 | |||
| bb948176aa | |||
| 163c799eff | |||
| 7da70af1ae | |||
| 836e4c1428 | |||
| eabedba34d | |||
| b4add97c17 | |||
| bacc31bea9 | |||
| ad90c3574f | |||
| e28171d5e4 | |||
| ce73ed091b | |||
| 90ac8d57b0 | |||
| 6eb1179505 | |||
| 9b85757102 | |||
| c6c3949b14 | |||
| e175db37c6 | |||
| f38010d3a2 | |||
| 7fc18b263d | |||
| fabc9f77a3 | |||
| c17c731fdc | |||
| 3692885810 | |||
| 5d43439dbe | |||
| a46f2a0db3 | |||
| 3217a249e1 | |||
| 78f394fd17 | |||
| e82e64d57b | |||
| 8978e066b5 | |||
| 833eb3c844 | |||
| 07926ff1ef | |||
| e801faba2e | |||
| ee6af9a978 | |||
| 74379df6c4 | |||
| fe65dd926c | |||
| 669b53ede2 | |||
| b0c3f28e8f | |||
| 9810dc0993 | |||
| ab5df20dfa | |||
| d83a92c121 | |||
| d0425de29e | |||
| ad5e42cf82 | |||
| 9ed1126adb | |||
| 7a19eb84aa | |||
| 718741acab | |||
| ec8bb675b4 | |||
| 8e32f3fd35 | |||
| 02332107e5 | |||
| afc81b554e | |||
| 26e8ab3693 | |||
| 28ccc76aa1 | |||
| b3c4cb7cff | |||
| 4af4378b11 | |||
| 8611ebe6a0 | |||
| 8f46a3c9ac | |||
| 66fdb36ecb | |||
| f0f5ffa9aa | |||
| 2bc7afd3ba | |||
| a4b45397e0 | |||
| de4e06ed73 | |||
| fd822bdaf9 | |||
| 4f78fd692c | |||
| df6d2ba326 | |||
| ccda436f94 | |||
| e86c435349 | |||
| 1942861472 | |||
| b96e978178 | |||
| bda2bba2be | |||
| ca08c004c8 | |||
| 25a62b58db | |||
| 97e3ec4d1b | |||
| 75f11f1fc4 | |||
| e134a8335f | |||
| 8ee32a75f0 | |||
| f6a8ad87ee | |||
| 7e6ff401b8 | |||
| 7aeb6a24f7 | |||
| e1ecc34edd | |||
| 29d36e94e1 | |||
| 091c173632 | |||
| c115fa9924 | |||
| b7a7fc7065 | |||
| 21b2a5bd21 | |||
| ca1e45beaf | |||
| 084b83ffa9 | |||
| bf5e5f7bc9 | |||
| 2e9fed7b6c | |||
| ea3228e311 | |||
| 2eb49147d6 | |||
| 13f92de624 | |||
| 2bc39bb0b4 | |||
| 62ae7fccbc | |||
| 3ace81b92a | |||
| 9acf45127e | |||
| 6883e8c7a0 | |||
| 7ae536d053 | |||
| 2170c06924 | |||
| 0e5a24c584 | |||
| 4e8f0d6e9f | |||
| f9b6b61468 | |||
| 555f415290 | |||
| 3800d17703 | |||
| 009059dd1b | |||
| 6b7ddf414d | |||
| 8259f10138 | |||
| ab407de54d | |||
| 0d7fe97aff | |||
| a2a830e227 | |||
| 8336f3f0ba | |||
| e14ac2c3b0 | |||
| a13653c814 | |||
| 8017340cd1 | |||
| 17d1aef66a | |||
| 1856f62cb1 | |||
| 7e1f364177 | |||
| aac68bf2ba | |||
| 4b4292edb8 | |||
| 292d5d1421 | |||
| 66dec77555 | |||
| 8fa79066e2 | |||
| 909415d5ed | |||
| 4421f3d435 | |||
| 22cb600280 | |||
| 5ba227c7cd | |||
| f37f0ea16e | |||
| 91ccb4ba6e | |||
| d0f459c56f | |||
| cbedf55641 | |||
| 988ed451b5 | |||
| fc2f188d4d | |||
| 4b1913c5ec | |||
| 06534413d3 | |||
| e54909f5ef | |||
| 79f2512ba7 | |||
| b0eb831bce | |||
| 7f0b97e02c | |||
| fc7f1ef6a0 | |||
| 8bae2a5ecb | |||
| 30e5f6274a | |||
| bc6e0cc954 | |||
| 8d11db0757 | |||
| 2a8fe56997 | |||
| 622c3ec974 | |||
| db78aa1ce1 | |||
| 986608fe76 | |||
| 31b8624121 | |||
| a5607e3061 | |||
| ff2cb86d5d | |||
| 825cbadf80 | |||
| 93f96a16f6 | |||
| c763b009ac | |||
| e5da0c956b | |||
| 3c80cf3df6 | |||
| ad3cc16eef | |||
| 08d09ecbaa | |||
| 456c7f62c5 | |||
| c9e7fb894b | |||
| 4a9ccc0abc | |||
| bceadd8e30 | |||
| 545b31aa2e | |||
| e068addadb | |||
| 0da4902e9d | |||
| 3ba90003b4 | |||
| 5927b23ef3 | |||
| 452607fc64 | |||
| 19401280ae | |||
| 08a33e7bb3 | |||
| 30753cb131 | |||
| dbf8afcba0 | |||
| 446f6b233f | |||
| e3fad0feb3 | |||
| 0f6e199d98 | |||
| 15e6105779 | |||
| ee38918059 | |||
| 94e3b28d24 | |||
| 38e6d1e313 | |||
| 607de75fa4 | |||
| db04241beb | |||
| 774db0aecb | |||
| dc0b0c77c7 | |||
| 6114c8f504 | |||
| b99b0a8072 | |||
| 431b748cac | |||
| de34ca0b64 | |||
| 793ecb4817 | |||
| 1d45e65f4a | |||
| 2bbef363e4 | |||
| 035ad72726 | |||
| 9d808239b3 | |||
| ef01362e44 | |||
| 6c30601ad8 | |||
| 37b5c5cfe9 | |||
| 966cbd4cf8 | |||
| 8534b7c7c0 | |||
| 6802a76007 | |||
| 7ec8a89362 | |||
| 34f174066f | |||
| 530dc412c4 | |||
| 3f99cdbdc3 | |||
| 13e7df68a6 | |||
| 42062dab34 | |||
| 1c9fc1e1de | |||
| 8bc7beacd8 | |||
| 842741ee99 | |||
| 40ad543d27 | |||
| 06835a462a | |||
| 142cffcf64 | |||
| 862d78c1d9 | |||
| 6f8904e027 | |||
| 4dc9e6416a | |||
| 4f6e947e49 | |||
| 15efbbdc1f | |||
| 67a3315e1d | |||
| 8f0dcbab80 | |||
| 1f4b417184 | |||
| 2d8d5aef29 | |||
| eb758bc605 | |||
| 761997e082 |
@@ -0,0 +1,14 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
@@ -10,3 +10,6 @@ performance/temp*.html
|
||||
*~
|
||||
angular.js.tmproj
|
||||
node_modules
|
||||
jsTestDriver*.conf
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
|
||||
Generated
-67
@@ -1,67 +0,0 @@
|
||||
IMPORTANT: THIS IS TO CERTIFY THE RIGHT TO USE THE JETBRAINS SOFTWARE PRODUCT, GRANTED BY JETBRAINS S.R.O. UNDER THE TERMS AND CONDITIONS OF THE LICENSE AGREEMENT INCLUDED WITH THE SOFTWARE. PLEASE SAVE A COPY OF THIS EMAIL FOR FUTURE REFERENCES.
|
||||
|
||||
========LICENSE DETAILS========
|
||||
|
||||
Type: Open Source License
|
||||
Reference No*: LC-93161-D352729080
|
||||
Date of Issue: 3 November 2010
|
||||
Expiration Date: 3 November 2011
|
||||
Number of Authorized Users: not limited
|
||||
|
||||
* Please quote this reference when contacting JetBrains
|
||||
|
||||
===========LICENSEE============
|
||||
|
||||
Name: angularjs
|
||||
Customer ID: 93161
|
||||
Address:
|
||||
|
||||
=======SOFTWARE PRODUCT========
|
||||
|
||||
Product Name: WebStorm
|
||||
Licensed Version: 1.0 and any new product release which is made generally available before 3 November 2011
|
||||
|
||||
The software is shipped electronically and is available for download from:
|
||||
http://www.jetbrains.com/webstorm/download/
|
||||
|
||||
Your WebStorm license includes a 1-year upgrade subscription qualifying you for free upgrades to any new versions of WebStorm released by JetBrains during your upgrade subscription period starting on your license issue date and ending on 3 November 2011. To check availability of the new versions of WebStorm, please visit http://www.jetbrains.com.
|
||||
|
||||
For running any new version of WebStorm released by JetBrains during your upgrade subscription period, please use the included below licensing details.
|
||||
|
||||
You can renew your upgrade subscription before or after its expiration. Your new subscription period will start on the date following the expiration date of your current upgrade subscription.
|
||||
|
||||
=========INSTALLATION==========
|
||||
|
||||
Run WebStorm and follow the Installation Wizard's instructions. To register for use of the software or change your existing registration details, go to Help/Register menu of the software and enter the included below the User Name and License Key(s) into the registration dialog:
|
||||
|
||||
User Name: angularjs
|
||||
|
||||
===== LICENSE BEGIN =====
|
||||
93161-03112010
|
||||
00000jBsEx59XVlc79fV"aAqWXQ09e
|
||||
jQsg5TNp5X4HGhc10LNBdu!!ejRcFG
|
||||
7h3S6T09YcRWs23TH0RgaM87!HqmQo
|
||||
===== LICENSE END =====
|
||||
|
||||
===DOCUMENTATION AND SUPPORT===
|
||||
|
||||
WebStorm documentation:
|
||||
http://www.jetbrains.com/webstorm/documentation/
|
||||
|
||||
Available support resources:
|
||||
http://www.jetbrains.com/support/
|
||||
|
||||
Technical support contact:
|
||||
support@jetbrains.com
|
||||
|
||||
Contact for the license renewal requests:
|
||||
opensource@jetbrains.com
|
||||
|
||||
For questions, please contact:
|
||||
sales@jetbrains.com
|
||||
|
||||
|
||||
JetBrains Sales Team
|
||||
http://www.jetbrains.com
|
||||
"Develop with pleasure!"
|
||||
|
||||
Generated
-9
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
Generated
-5
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
</project>
|
||||
|
||||
Generated
-11
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DependencyValidationManager">
|
||||
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" />
|
||||
<component name="SvnBranchConfigurationManager">
|
||||
<option name="mySupportsUserInfoFilter" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
Generated
-9
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/angular.js.iml" filepath="$PROJECT_DIR$/.idea/angular.js.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
Generated
-68
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value>
|
||||
<option name="USE_SAME_INDENTS" value="true" />
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
</value>
|
||||
</option>
|
||||
<ADDITIONAL_INDENT_OPTIONS fileType="js">
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="8" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
</ADDITIONAL_INDENT_OPTIONS>
|
||||
<ADDITIONAL_INDENT_OPTIONS fileType="jsp">
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="8" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
</ADDITIONAL_INDENT_OPTIONS>
|
||||
<ADDITIONAL_INDENT_OPTIONS fileType="sass">
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="8" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
</ADDITIONAL_INDENT_OPTIONS>
|
||||
<ADDITIONAL_INDENT_OPTIONS fileType="xml">
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="8" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
</ADDITIONAL_INDENT_OPTIONS>
|
||||
<ADDITIONAL_INDENT_OPTIONS fileType="yml">
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="8" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
</ADDITIONAL_INDENT_OPTIONS>
|
||||
</value>
|
||||
</option>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
Generated
-15
@@ -1,15 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="gen_docs" type="BashConfigurationType" factoryName="Bash">
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/bash" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs />
|
||||
<module name="angular.js" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/gen_docs.sh" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<RunnerSettings RunnerId="BashRunner" />
|
||||
<ConfigurationWrapper RunnerId="BashRunner" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
Generated
-15
@@ -1,15 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="rake compile" type="BashConfigurationType" factoryName="Bash">
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="INTERPRETER_PATH" value="/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs />
|
||||
<module name="angular.js" />
|
||||
<option name="SCRIPT_NAME" value="/usr/bin/rake" />
|
||||
<option name="PARAMETERS" value="compile" />
|
||||
<RunnerSettings RunnerId="BashRunner" />
|
||||
<ConfigurationWrapper RunnerId="BashRunner" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
Generated
-7
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
+1416
-45
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
Angular
|
||||
======
|
||||
AngularJS
|
||||
=========
|
||||
|
||||
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets use
|
||||
good old HTML (or HAML, Jade and friends!) as your template language and lets you extend HTML’s
|
||||
syntax to express your application’s components clearly and succinctly. It automatically
|
||||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||
binding. To help you structure your application better and make it easy to test AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
* API Docs: http://docs.angularjs.org
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
|
||||
Compiling
|
||||
---------
|
||||
@@ -7,6 +22,8 @@ Compiling
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
rake server:start
|
||||
rake test
|
||||
./server.sh # start the server
|
||||
open http://localhost:9876/capture # capture browser
|
||||
./test.sh # run all unit tests
|
||||
|
||||
|
||||
|
||||
@@ -1,64 +1,22 @@
|
||||
require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
ANGULAR = [
|
||||
'src/Angular.js',
|
||||
'src/JSON.js',
|
||||
'src/Compiler.js',
|
||||
'src/Scope.js',
|
||||
'src/Injector.js',
|
||||
'src/parser.js',
|
||||
'src/Resource.js',
|
||||
'src/Browser.js',
|
||||
'src/sanitizer.js',
|
||||
'src/jqLite.js',
|
||||
'src/apis.js',
|
||||
'src/filters.js',
|
||||
'src/formatters.js',
|
||||
'src/validators.js',
|
||||
'src/service/cookieStore.js',
|
||||
'src/service/cookies.js',
|
||||
'src/service/defer.js',
|
||||
'src/service/document.js',
|
||||
'src/service/exceptionHandler.js',
|
||||
'src/service/hover.js',
|
||||
'src/service/invalidWidgets.js',
|
||||
'src/service/location.js',
|
||||
'src/service/log.js',
|
||||
'src/service/resource.js',
|
||||
'src/service/route.js',
|
||||
'src/service/updateView.js',
|
||||
'src/service/window.js',
|
||||
'src/service/xhr.bulk.js',
|
||||
'src/service/xhr.cache.js',
|
||||
'src/service/xhr.error.js',
|
||||
'src/service/xhr.js',
|
||||
'src/directives.js',
|
||||
'src/markups.js',
|
||||
'src/widgets.js',
|
||||
'src/AngularPublic.js',
|
||||
]
|
||||
|
||||
ANGULAR_SCENARIO = [
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/Application.js',
|
||||
'src/scenario/Describe.js',
|
||||
'src/scenario/Future.js',
|
||||
'src/scenario/ObjectModel.js',
|
||||
'src/scenario/Describe.js',
|
||||
'src/scenario/Runner.js',
|
||||
'src/scenario/SpecRunner.js',
|
||||
'src/scenario/dsl.js',
|
||||
'src/scenario/matchers.js',
|
||||
'src/scenario/output/Html.js',
|
||||
'src/scenario/output/Json.js',
|
||||
'src/scenario/output/Xml.js',
|
||||
'src/scenario/output/Object.js'
|
||||
]
|
||||
## High level flow of the build:
|
||||
##
|
||||
## clean -> init -> concat -> minify -> package
|
||||
##
|
||||
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').
|
||||
gsub(/angularFiles = /, '').
|
||||
gsub(/:/, '=>').
|
||||
gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
task :default => [:compile, :test]
|
||||
task :default => [:package]
|
||||
|
||||
|
||||
desc 'Init the build workspace'
|
||||
@@ -68,12 +26,13 @@ task :init do
|
||||
v = YAML::load( File.open( 'version.yaml' ) )
|
||||
match = v['version'].match(/^([^-]*)(-snapshot)?$/)
|
||||
|
||||
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename).
|
||||
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename, :stable).
|
||||
new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''),
|
||||
match[1].split('.')[0],
|
||||
match[1].split('.')[1],
|
||||
match[1].split('.')[2],
|
||||
v['codename'])
|
||||
match[1].split('.')[2].sub(/\D+.*$/, ''),
|
||||
v['codename'],
|
||||
v['stable'])
|
||||
end
|
||||
|
||||
|
||||
@@ -84,40 +43,27 @@ task :clean do
|
||||
end
|
||||
|
||||
|
||||
desc 'Compile Scenario'
|
||||
task :compile_scenario => :init do
|
||||
desc 'Concat Scenario'
|
||||
task :concat_scenario => :init do
|
||||
|
||||
deps = [
|
||||
'lib/jquery/jquery-1.4.2.js',
|
||||
'src/scenario/angular.prefix',
|
||||
ANGULAR,
|
||||
ANGULAR_SCENARIO,
|
||||
'src/scenario/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('angular-scenario.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
f.write(gen_css('css/angular.css') + "\n")
|
||||
f.write(gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
concat_file('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/ngScenario/angular.suffix',
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
desc 'Compile JSTD Scenario Adapter'
|
||||
task :compile_jstd_scenario_adapter => :init do
|
||||
|
||||
deps = [
|
||||
'src/jstd-scenario-adapter/angular.prefix',
|
||||
'src/jstd-scenario-adapter/Adapter.js',
|
||||
'src/jstd-scenario-adapter/angular.suffix',
|
||||
]
|
||||
desc 'Concat JSTD Scenario Adapter'
|
||||
task :concat_jstd_scenario_adapter => :init do
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('jstd-scenario-adapter.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
end
|
||||
concat_file('jstd-scenario-adapter.js', [
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
|
||||
])
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
# (instead of including jstd-adapter-config.js)
|
||||
@@ -129,166 +75,92 @@ task :compile_jstd_scenario_adapter => :init do
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate IE css js patch'
|
||||
task :generate_ie_compat => :init do
|
||||
css = File.open('css/angular.css', 'r') {|f| f.read }
|
||||
|
||||
# finds all css rules that contain backround images and extracts the rule name(s), content type of
|
||||
# the image and base64 encoded image data
|
||||
r = /\n([^\{\n]+)\s*\{[^\}]*background-image:\s*url\("data:([^;]+);base64,([^"]+)"\);[^\}]*\}/
|
||||
desc 'Concat AngularJS files'
|
||||
task :concat => :init do
|
||||
concat_file('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
images = css.scan(r)
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
# create a js file with multipart header containing the extracted images. the entire file *must*
|
||||
# be CRLF (\r\n) delimited
|
||||
File.open(path_to('angular-ie-compat.js'), 'w') do |f|
|
||||
f.write("/*\r\n" +
|
||||
"Content-Type: multipart/related; boundary=\"_\"\r\n" +
|
||||
"\r\n")
|
||||
concat_file('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'])
|
||||
|
||||
images.each_index do |idx|
|
||||
f.write("--_\r\n" +
|
||||
"Content-Location:img#{idx}\r\n" +
|
||||
"Content-Transfer-Encoding:base64\r\n" +
|
||||
"\r\n" +
|
||||
images[idx][2] + "\r\n")
|
||||
end
|
||||
|
||||
f.write("--_--\r\n" +
|
||||
"*/\r\n")
|
||||
concat_module('sanitize', [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js'])
|
||||
|
||||
# generate a css string containing *background-image rules for IE that point to the mime type
|
||||
# images in the header
|
||||
cssString = ''
|
||||
images.each_index do |idx|
|
||||
cssString += "#{images[idx][0]}{*background-image:url(\"mhtml:' + jsUri + '!img#{idx}\")}"
|
||||
end
|
||||
concat_module('resource', ['src/ngResource/resource.js'])
|
||||
concat_module('cookies', ['src/ngCookies/cookies.js'])
|
||||
concat_module('bootstrap', ['src/bootstrap/bootstrap.js'])
|
||||
concat_module('bootstrap-prettify', ['src/bootstrap/bootstrap-prettify.js',
|
||||
'src/bootstrap/google-prettify/prettify.js'],
|
||||
gen_css('src/bootstrap/google-prettify/prettify.css', true))
|
||||
|
||||
# generate a javascript closure that contains a function which will append the generated css
|
||||
# string as a stylesheet to the current html document
|
||||
jsString = "(function(){ \r\n" +
|
||||
" var jsUri = document.location.href.replace(/\\/[^\\\/]+(#.*)?$/, '/') + \r\n" +
|
||||
" document.getElementById('ng-ie-compat').src,\r\n" +
|
||||
" css = '#{cssString}',\r\n" +
|
||||
" s = document.createElement('style'); \r\n" +
|
||||
"\r\n" +
|
||||
" s.setAttribute('type', 'text/css'); \r\n" +
|
||||
"\r\n" +
|
||||
" if (s.styleSheet) { \r\n" +
|
||||
" s.styleSheet.cssText = css; \r\n" +
|
||||
" } else { \r\n" +
|
||||
" s.appendChild(document.createTextNode(css)); \r\n" +
|
||||
" } \r\n" +
|
||||
" document.getElementsByTagName('head')[0].appendChild(s); \r\n" +
|
||||
"})();\r\n"
|
||||
|
||||
f.write(jsString)
|
||||
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
|
||||
|
||||
rewrite_file(path_to('angular-mocks.js')) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Compile JavaScript'
|
||||
task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter, :generate_ie_compat] do
|
||||
|
||||
deps = [
|
||||
'src/angular.prefix',
|
||||
ANGULAR,
|
||||
'src/angular.suffix',
|
||||
]
|
||||
|
||||
File.open(path_to('angular.js'), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
gsub(/'USE STRICT'/, "'use strict'") # rename the placeholder in angular.prefix
|
||||
|
||||
f.write(content)
|
||||
f.write(gen_css('css/angular.css', true))
|
||||
desc 'Minify JavaScript'
|
||||
task :minify => [:init, :concat, :concat_scenario, :concat_jstd_scenario_adapter] do
|
||||
[ 'angular.js',
|
||||
'angular-cookies.js',
|
||||
'angular-loader.js',
|
||||
'angular-resource.js',
|
||||
'angular-sanitize.js',
|
||||
'angular-bootstrap.js',
|
||||
'angular-bootstrap-prettify.js'
|
||||
].each do |file|
|
||||
closure_compile(file)
|
||||
end
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular.js')} \
|
||||
--js_output_file #{path_to('angular.min.js')})
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs => [:init] do
|
||||
`node docs/src/gen-docs.js`
|
||||
File.open(path_to('docs/.htaccess'), File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
|
||||
[ path_to('docs/.htaccess'),
|
||||
path_to('docs/index.html'),
|
||||
path_to('docs/index-debug.html'),
|
||||
path_to('docs/index-nocache.html'),
|
||||
path_to('docs/index-jq.html'),
|
||||
path_to('docs/index-jq-debug.html'),
|
||||
path_to('docs/index-jq-nocache.html'),
|
||||
path_to('docs/docs-scenario.html')
|
||||
].each do |src|
|
||||
rewrite_file(src) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
sub('"NG_VERSION_STABLE"', NG_VERSION.stable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Create angular distribution'
|
||||
task :package => [:clean, :compile, :docs] do
|
||||
tarball = "angular-#{NG_VERSION.full}.tgz"
|
||||
task :package => [:clean, :minify, :docs] do
|
||||
zip_dir = "angular-#{NG_VERSION.full}"
|
||||
zip_file = "#{zip_dir}.zip"
|
||||
|
||||
pkg_dir = path_to("pkg/angular-#{NG_VERSION.full}")
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
FileUtils.ln_s BUILD_DIR, zip_dir
|
||||
%x(zip -r #{zip_file} #{zip_dir})
|
||||
FileUtils.rm zip_dir
|
||||
|
||||
['src/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-ie-compat.js'),
|
||||
path_to('angular-scenario.js'),
|
||||
path_to('jstd-scenario-adapter.js'),
|
||||
path_to('jstd-scenario-adapter-config.js'),
|
||||
].each do |src|
|
||||
dest = src.gsub(/^[^\/]+\//, '').gsub(/((\.min)?\.js)$/, "-#{NG_VERSION.full}\\1")
|
||||
FileUtils.cp(src, pkg_dir + '/' + dest)
|
||||
end
|
||||
FileUtils.mv zip_file, path_to(zip_file)
|
||||
|
||||
FileUtils.cp_r path_to('docs'), "#{pkg_dir}/docs-#{NG_VERSION.full}"
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/docs-scenario.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular-scenario.js', "angular-scenario-#{NG_VERSION.full}.js")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/appcache.manifest", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/appcache-offline.manifest", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
|
||||
%x(tar -czf #{path_to(tarball)} -C #{path_to('pkg')} .)
|
||||
|
||||
FileUtils.cp path_to(tarball), pkg_dir
|
||||
FileUtils.mv pkg_dir, path_to(['pkg', NG_VERSION.full])
|
||||
|
||||
puts "Package created: #{path_to(tarball)}"
|
||||
puts "Package created: #{path_to(zip_file)}"
|
||||
end
|
||||
|
||||
|
||||
@@ -366,3 +238,61 @@ end
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
|
||||
def closure_compile(filename)
|
||||
puts "Minifying #{filename} ..."
|
||||
|
||||
min_path = path_to(filename.gsub(/\.js$/, '.min.js'))
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to(filename)} \
|
||||
--js_output_file #{min_path})
|
||||
|
||||
rewrite_file(min_path) do |content|
|
||||
content.sub!("'use strict';", "").
|
||||
sub!(/\(function\([^)]*\)\{/, "\\0'use strict';")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_file(filename, deps, footer='')
|
||||
puts "Creating #{filename} ..."
|
||||
File.open(path_to(filename), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
|
||||
|
||||
f.write(content)
|
||||
f.write(footer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_module(name, files, footer='')
|
||||
concat_file('angular-' + name + '.js', ['src/module.prefix'] + files + ['src/module.suffix'], footer)
|
||||
end
|
||||
|
||||
|
||||
def rewrite_file(filename)
|
||||
File.open(filename, File::RDWR) do |f|
|
||||
content = f.read
|
||||
|
||||
content = yield content
|
||||
|
||||
raise "File rewrite failed - No content!" unless content
|
||||
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write content
|
||||
end
|
||||
end
|
||||
|
||||
Vendored
+231
@@ -0,0 +1,231 @@
|
||||
angularFiles = {
|
||||
'angularSrc': [
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/AngularPublic.js',
|
||||
'src/jqLite.js',
|
||||
'src/apis.js',
|
||||
|
||||
'src/auto/injector.js',
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/browser.js',
|
||||
'src/ng/cacheFactory.js',
|
||||
'src/ng/compile.js',
|
||||
'src/ng/controller.js',
|
||||
'src/ng/document.js',
|
||||
'src/ng/exceptionHandler.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/route.js',
|
||||
'src/ng/routeParams.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/window.js',
|
||||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/timeout.js',
|
||||
|
||||
'src/ng/filter.js',
|
||||
'src/ng/filter/filter.js',
|
||||
'src/ng/filter/filters.js',
|
||||
'src/ng/filter/limitTo.js',
|
||||
'src/ng/filter/orderBy.js',
|
||||
|
||||
'src/ng/directive/directives.js',
|
||||
'src/ng/directive/a.js',
|
||||
'src/ng/directive/booleanAttrs.js',
|
||||
'src/ng/directive/form.js',
|
||||
'src/ng/directive/input.js',
|
||||
'src/ng/directive/ngBind.js',
|
||||
'src/ng/directive/ngClass.js',
|
||||
'src/ng/directive/ngCloak.js',
|
||||
'src/ng/directive/ngController.js',
|
||||
'src/ng/directive/ngCsp.js',
|
||||
'src/ng/directive/ngEventDirs.js',
|
||||
'src/ng/directive/ngInclude.js',
|
||||
'src/ng/directive/ngInit.js',
|
||||
'src/ng/directive/ngNonBindable.js',
|
||||
'src/ng/directive/ngPluralize.js',
|
||||
'src/ng/directive/ngRepeat.js',
|
||||
'src/ng/directive/ngShowHide.js',
|
||||
'src/ng/directive/ngStyle.js',
|
||||
'src/ng/directive/ngSwitch.js',
|
||||
'src/ng/directive/ngTransclude.js',
|
||||
'src/ng/directive/ngView.js',
|
||||
'src/ng/directive/script.js',
|
||||
'src/ng/directive/select.js',
|
||||
'src/ng/directive/style.js'
|
||||
],
|
||||
|
||||
'angularSrcModules': [
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
|
||||
'src/bootstrap/bootstrap.js'
|
||||
],
|
||||
|
||||
'angularScenario': [
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/Application.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Future.js',
|
||||
'src/ngScenario/ObjectModel.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Runner.js',
|
||||
'src/ngScenario/SpecRunner.js',
|
||||
'src/ngScenario/dsl.js',
|
||||
'src/ngScenario/matchers.js',
|
||||
'src/ngScenario/output/Html.js',
|
||||
'src/ngScenario/output/Json.js',
|
||||
'src/ngScenario/output/Xml.js',
|
||||
'src/ngScenario/output/Object.js'
|
||||
],
|
||||
|
||||
'angularTest': [
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/bootstrap/*.js',
|
||||
'test/ng/*.js',
|
||||
'test/ng/directive/*.js',
|
||||
'test/ng/filter/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js',
|
||||
'test/ngMock/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdScenario': [
|
||||
'build/angular-scenario.js',
|
||||
'build/jstd-scenario-adapter-config.js',
|
||||
'build/jstd-scenario-adapter.js',
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'build/angular.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'test/matchers.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js'
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
'perf/*.js'
|
||||
],
|
||||
|
||||
'jstdPerfExclude': [
|
||||
'src/ng/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js'
|
||||
]
|
||||
};
|
||||
|
||||
// Execute only in slim-jim
|
||||
if (typeof JASMINE_ADAPTER !== 'undefined') {
|
||||
// Testacular config
|
||||
var mergedFiles = [];
|
||||
angularFiles.jstd.forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
if (!deps) {
|
||||
console.log('No dependency:' + file)
|
||||
}
|
||||
mergedFiles = mergedFiles.concat(deps);
|
||||
} else {
|
||||
mergedFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
|
||||
mergedFiles.forEach(function(file){
|
||||
if (/jstd|jasmine/.test(file)) return;
|
||||
files.push(file);
|
||||
});
|
||||
|
||||
|
||||
exclude = angularFiles.jstdExclude;
|
||||
|
||||
autoWatch = true;
|
||||
autoWatchInterval = 1;
|
||||
logLevel = LOG_INFO;
|
||||
logColors = true;
|
||||
}
|
||||
Executable
+205
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// TODO(vojta): pre-commit hook for validating messages
|
||||
// TODO(vojta): report errors, currently Q silence everything which really sucks
|
||||
|
||||
var child = require('child_process');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var q = require('qq');
|
||||
|
||||
var GIT_LOG_CMD = 'git log --grep="%s" -E --format=%s %s..HEAD';
|
||||
var GIT_TAG_CMD = 'git describe --tags --abbrev=0';
|
||||
|
||||
var HEADER_TPL = '<a name="%s"></a>\n# %s (%s)\n\n';
|
||||
var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
|
||||
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
|
||||
|
||||
var EMPTY_COMPONENT = '$$';
|
||||
var MAX_SUBJECT_LENGTH = 80;
|
||||
|
||||
|
||||
var warn = function() {
|
||||
console.log('WARNING:', util.format.apply(null, arguments));
|
||||
};
|
||||
|
||||
|
||||
var parseRawCommit = function(raw) {
|
||||
if (!raw) return null;
|
||||
|
||||
var lines = raw.split('\n');
|
||||
var msg = {}, match;
|
||||
|
||||
msg.hash = lines.shift();
|
||||
msg.subject = lines.shift();
|
||||
msg.closes = [];
|
||||
msg.breaks = [];
|
||||
|
||||
lines.forEach(function(line) {
|
||||
match = line.match(/Closes\s#(\d+)/);
|
||||
if (match) msg.closes.push(parseInt(match[1]));
|
||||
});
|
||||
|
||||
match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
|
||||
if (match) {
|
||||
console.log('found!!!')
|
||||
msg.breaks.push(match[1]);
|
||||
}
|
||||
|
||||
|
||||
msg.body = lines.join('\n');
|
||||
match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
|
||||
|
||||
if (!match || !match[1] || !match[3]) {
|
||||
warn('Incorrect message: %s %s', msg.hash, msg.subject);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (match[3].length > MAX_SUBJECT_LENGTH) {
|
||||
warn('Too long subject: %s %s', msg.hash, msg.subject);
|
||||
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
|
||||
}
|
||||
|
||||
msg.type = match[1];
|
||||
msg.component = match[2];
|
||||
msg.subject = match[3];
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
var linkToIssue = function(issue) {
|
||||
return util.format(LINK_ISSUE, issue, issue);
|
||||
};
|
||||
|
||||
|
||||
var linkToCommit = function(hash) {
|
||||
return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
|
||||
};
|
||||
|
||||
|
||||
var currentDate = function() {
|
||||
var now = new Date();
|
||||
var pad = function(i) {
|
||||
return ('0' + i).substr(-2);
|
||||
};
|
||||
|
||||
return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
|
||||
};
|
||||
|
||||
|
||||
var printSection = function(stream, title, section) {
|
||||
var components = Object.getOwnPropertyNames(section).sort();
|
||||
|
||||
if (!components.length) return;
|
||||
|
||||
stream.write(util.format('\n## %s\n\n', title));
|
||||
|
||||
components.forEach(function(name) {
|
||||
var prefix = '-';
|
||||
var nested = section[name].length > 1;
|
||||
|
||||
if (name !== EMPTY_COMPONENT) {
|
||||
if (nested) {
|
||||
stream.write(util.format('- **%s:**\n', name));
|
||||
prefix = ' -';
|
||||
} else {
|
||||
prefix = util.format('- **%s:**', name);
|
||||
}
|
||||
}
|
||||
|
||||
section[name].forEach(function(commit) {
|
||||
stream.write(util.format('%s %s (%s', prefix, commit.subject, linkToCommit(commit.hash)));
|
||||
if (commit.closes.length) {
|
||||
stream.write(', closes ' + commit.closes.map(linkToIssue).join(', '));
|
||||
}
|
||||
stream.write(')\n');
|
||||
});
|
||||
});
|
||||
|
||||
stream.write('\n');
|
||||
};
|
||||
|
||||
|
||||
var readGitLog = function(grep, from) {
|
||||
var deffered = q.defer();
|
||||
|
||||
// TODO(vojta): if it's slow, use spawn and stream it instead
|
||||
child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
|
||||
var commits = [];
|
||||
|
||||
stdout.split('\n==END==\n').forEach(function(rawCommit) {
|
||||
var commit = parseRawCommit(rawCommit);
|
||||
if (commit) commits.push(commit);
|
||||
});
|
||||
|
||||
deffered.resolve(commits);
|
||||
});
|
||||
|
||||
return deffered.promise;
|
||||
};
|
||||
|
||||
|
||||
var writeChangelog = function(stream, commits, version) {
|
||||
var sections = {
|
||||
fix: {},
|
||||
feat: {},
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
sections.breaks[EMPTY_COMPONENT] = [];
|
||||
|
||||
commits.forEach(function(commit) {
|
||||
var section = sections[commit.type];
|
||||
var component = commit.component || EMPTY_COMPONENT;
|
||||
|
||||
if (section) {
|
||||
section[component] = section[component] || [];
|
||||
section[component].push(commit);
|
||||
}
|
||||
|
||||
commit.breaks.forEach(function(breakMsg) {
|
||||
sections.breaks[EMPTY_COMPONENT].push({
|
||||
subject: breakMsg,
|
||||
hash: commit.hash,
|
||||
closes: []
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
printSection(stream, 'Bug Fixes', sections.fix);
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks);
|
||||
}
|
||||
|
||||
|
||||
var getPreviousTag = function() {
|
||||
var deffered = q.defer();
|
||||
child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
|
||||
if (code) deffered.reject('Cannot get the previous tag.');
|
||||
else deffered.resolve(stdout.replace('\n', ''));
|
||||
});
|
||||
return deffered.promise;
|
||||
};
|
||||
|
||||
|
||||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// publish for testing
|
||||
exports.parseRawCommit = parseRawCommit;
|
||||
|
||||
// hacky start if not run by jasmine :-D
|
||||
if (process.argv.join('').indexOf('jasmine-node') === -1) {
|
||||
generate(process.argv[2], process.argv[3]);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
describe('changelog.js', function() {
|
||||
var ch = require('./changelog');
|
||||
|
||||
describe('parseRawCommit', function() {
|
||||
it('should parse raw commit', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'9b1aff905b638aa274a5fc8f88662df446d374bd\n' +
|
||||
'feat(scope): broadcast $destroy event on scope destruction\n' +
|
||||
'perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n');
|
||||
|
||||
expect(msg.type).toBe('feat');
|
||||
expect(msg.hash).toBe('9b1aff905b638aa274a5fc8f88662df446d374bd');
|
||||
expect(msg.subject).toBe('broadcast $destroy event on scope destruction');
|
||||
expect(msg.body).toBe('perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n')
|
||||
expect(msg.component).toBe('scope');
|
||||
});
|
||||
|
||||
|
||||
it('should parse closed issues', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
|
||||
'feat(ng-list): Allow custom separator\n' +
|
||||
'bla bla bla\n\n' +
|
||||
'Closes #123\nCloses #25\n');
|
||||
|
||||
expect(msg.closes).toEqual([123, 25]);
|
||||
});
|
||||
|
||||
|
||||
it('should parse breaking changes', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
|
||||
'feat(ng-list): Allow custom separator\n' +
|
||||
'bla bla bla\n\n' +
|
||||
'Breaks first breaking change\nsomething else\n' +
|
||||
'Breaks another breaking change\n');
|
||||
|
||||
expect(msg.breaks).toEqual(['first breaking change', 'another breaking change']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
rake compile
|
||||
gzip -c < build/angular.min.js > build/angular.min.js.gzip
|
||||
ls -l build/angular.min.*
|
||||
+5
-82
@@ -1,87 +1,10 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
.ng-format-negative {
|
||||
color: red;
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ng-exception {
|
||||
border: 2px solid #FF0000;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.ng-validation-error {
|
||||
border: 2px solid #FF0000;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* TIP
|
||||
*****************/
|
||||
#ng-callout {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||
vertical-align: baseline;
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#ng-callout .ng-arrow-left{
|
||||
background-image: url("data:image/gif;base64,R0lGODlhCwAXAKIAAMzMzO/v7/f39////////wAAAAAAAAAAACH5BAUUAAQALAAAAAALABcAAAMrSLoc/AG8FeUUIN+sGebWAnbKSJodqqlsOxJtqYooU9vvk+vcJIcTkg+QAAA7");
|
||||
background-repeat: no-repeat;
|
||||
background-position: left top;
|
||||
position: absolute;
|
||||
z-index:101;
|
||||
left:-12px;
|
||||
height:23px;
|
||||
width:10px;
|
||||
top:-3px;
|
||||
}
|
||||
|
||||
#ng-callout .ng-arrow-right{
|
||||
background-image: url("data:image/gif;base64,R0lGODlhCwAXAKIAAMzMzO/v7/f39////////wAAAAAAAAAAACH5BAUUAAQALAAAAAALABcAAAMrCLTcoM29yN6k9socs91e5X3EyJloipYrO4ohTMqA0Fn2XVNswJe+H+SXAAA7");
|
||||
background-repeat: no-repeat;
|
||||
background-position: left top;
|
||||
position: absolute;
|
||||
z-index:101;
|
||||
height:23px;
|
||||
width:11px;
|
||||
top:-2px;
|
||||
}
|
||||
|
||||
#ng-callout {
|
||||
position: absolute;
|
||||
z-index:100;
|
||||
border: 2px solid #CCCCCC;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#ng-callout .ng-content{
|
||||
padding:10px 10px 10px 10px;
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
#ng-callout .ng-title{
|
||||
background-color: #CCCCCC;
|
||||
text-align: left;
|
||||
padding-left: 8px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 2px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* indicators
|
||||
*****************/
|
||||
.ng-input-indicator-wait {
|
||||
background-image: url("data:image/png;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==");
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.service
|
||||
@description
|
||||
|
||||
The services API provides objects for carrying out common web app tasks. Service objects are
|
||||
managed by angular's {@link guide/dev_guide.di dependency injection system}.
|
||||
|
||||
* {@link angular.service.$browser $browser } - Provides an instance of a browser object
|
||||
* {@link angular.service.$cookieStore $cookieStore } - Provides key / value storage backed by
|
||||
session cookies
|
||||
* {@link angular.service.$cookies $cookies } - Provides read / write access to browser cookies
|
||||
* {@link angular.service.$defer $defer } - Defers function execution and try / catch block
|
||||
* {@link angular.service.$document $document } - Provides reference to `window.document` element
|
||||
* {@link angular.service.$exceptionHandler $exceptionHandler } - Receives uncaught angular
|
||||
exceptions
|
||||
* {@link angular.service.$hover $hover } -
|
||||
* {@link angular.service.$invalidWidgets $invalidWidgets } - Holds references to invalid widgets
|
||||
* {@link angular.service.$location $location } - Parses the browser location URL
|
||||
* {@link angular.service.$log $log } - Provides logging service
|
||||
* {@link angular.service.$resource $resource } - Creates objects for interacting with RESTful
|
||||
server-side data sources
|
||||
* {@link angular.service.$route $route } - Provides deep-linking services
|
||||
* {@link angular.service.$updateView $updateView } - Queues view updates
|
||||
* {@link angular.service.$window $window } - References the browsers `window` object
|
||||
* {@link angular.service.$xhr $xhr} - Generates an XHR request.
|
||||
|
||||
For information on how angular services work and how to write your own services, see {@link
|
||||
guide/dev_guide.services Angular Services} in the angular Developer Guide.
|
||||
@@ -2,73 +2,6 @@
|
||||
@name API Reference
|
||||
@description
|
||||
|
||||
## Angular Compiler API
|
||||
|
||||
* {@link angular.widget Widgets} - Angular custom DOM element
|
||||
* {@link angular.directive Directives} - Angular DOM element attributes
|
||||
* {@link angular.markup Markup} and {@link angular.attrMarkup Attribute Markup}
|
||||
* {@link angular.filter Filters} - Angular output filters
|
||||
* {@link angular.formatter Formatters} - Angular converters for form elements
|
||||
* {@link angular.validator Validators} - Angular input validators
|
||||
* {@link angular.compile angular.compile()} - Template compiler
|
||||
|
||||
## Angular Scope API
|
||||
|
||||
* {@link angular.scope Scope Object} - Angular scope object
|
||||
|
||||
|
||||
## Angular Services & Dependency Injection API
|
||||
|
||||
* {@link angular.service Angular Services}
|
||||
* {@link angular.injector angular.injector() }
|
||||
|
||||
|
||||
## Angular Testing API
|
||||
|
||||
* {@link angular.mock Testing Mocks API} - Mock objects for testing
|
||||
* {@link
|
||||
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en_US
|
||||
Angular Scenario Runner} - Automated scenario testing documentation
|
||||
|
||||
|
||||
## Angular Utility Functions
|
||||
|
||||
### HTML & DOM Manipulation
|
||||
|
||||
* {@link angular.element angular.element()}
|
||||
|
||||
### Misc
|
||||
|
||||
* {@link angular.bind angular.bind() }
|
||||
* {@link angular.extend angular.extend() }
|
||||
* {@link angular.forEach angular.forEach() }
|
||||
* {@link angular.identity angular.identity() }
|
||||
* {@link angular.noop angular.noop() }
|
||||
|
||||
|
||||
## Type Identification
|
||||
|
||||
* {@link angular.isArray angular.isArray() }
|
||||
* {@link angular.isDate angular.isDate() }
|
||||
* {@link angular.isDefined angular.isDefined() }
|
||||
* {@link angular.isFunction angular.isFunction() }
|
||||
* {@link angular.isNumber angular.isNumber() }
|
||||
* {@link angular.isObject angular.isObject() }
|
||||
* {@link angular.isString angular.isString() }
|
||||
* {@link angular.isUndefined angular.isUndefined() }
|
||||
|
||||
## Strings
|
||||
|
||||
* {@link angular.lowercase angular.lowercase() }
|
||||
* {@link angular.uppercase angular.uppercase() }
|
||||
|
||||
### JSON
|
||||
|
||||
* {@link angular.fromJson angular.fromJson() }
|
||||
* {@link angular.toJson angular.toJson() }
|
||||
|
||||
|
||||
|
||||
## Utility methods for JavaScript types
|
||||
* {@link angular.Object Object API} - Utility functions for JavaScript objects
|
||||
* {@link angular.Array Array API} - Utility functions for JavaScript arrays
|
||||
Use the API Reference documentation when you need more information about a specific feature. Check out
|
||||
{@link guide/ Developer Guide} for AngularJS concepts. If you are new to AngularJS we recomend the
|
||||
{@link tutorial/ Tutorial}.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@ngdoc overview
|
||||
@name ng
|
||||
@description
|
||||
|
||||
The `ng` is an angular module which contains all of the core angular services.
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Advanced Form
|
||||
@description
|
||||
@@ -9,12 +8,8 @@ detection, and preventing invalid form submission.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
UserForm.$inject = ['$invalidWidgets'];
|
||||
function UserForm($invalidWidgets){
|
||||
this.$invalidWidgets = $invalidWidgets;
|
||||
this.state = /^\w\w$/;
|
||||
this.zip = /^\d\d\d\d\d$/;
|
||||
this.master = {
|
||||
function UserForm($scope) {
|
||||
var master = {
|
||||
name: 'John Smith',
|
||||
address:{
|
||||
line1: '123 Main St.',
|
||||
@@ -26,56 +21,81 @@ detection, and preventing invalid form submission.
|
||||
{type:'phone', value:'1(234) 555-1212'}
|
||||
]
|
||||
};
|
||||
this.cancel();
|
||||
|
||||
$scope.state = /^\w\w$/;
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy(master);
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
master = $scope.form;
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.addContact = function() {
|
||||
$scope.form.contacts.push({type:'', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
var contacts = $scope.form.contacts;
|
||||
for (var i = 0, ii = contacts.length; i < ii; i++) {
|
||||
if (contact === contacts[i]) {
|
||||
contacts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isCancelDisabled = function() {
|
||||
return angular.equals(master, $scope.form);
|
||||
};
|
||||
|
||||
$scope.isSaveDisabled = function() {
|
||||
return $scope.myForm.$invalid || angular.equals(master, $scope.form);
|
||||
};
|
||||
|
||||
$scope.cancel();
|
||||
}
|
||||
|
||||
UserForm.prototype = {
|
||||
cancel: function(){
|
||||
this.form = angular.copy(this.master);
|
||||
},
|
||||
|
||||
save: function(){
|
||||
this.master = this.form;
|
||||
this.cancel();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div ng:controller="UserForm">
|
||||
<div ng-controller="UserForm">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="form.name" ng:required/> <br/><br/>
|
||||
<form name="myForm">
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" name="form.address.line1" size="33" ng:required/> <br/>
|
||||
<input type="text" name="form.address.city" size="12" ng:required/>,
|
||||
<input type="text" name="form.address.state" size="2" ng:required ng:validate="regexp:state"/>
|
||||
<input type="text" name="form.address.zip" size="5" ng:required
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" ng-model="form.name" required/> <br/><br/>
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select name="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" name="contact.value" ng:required/>
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng:click="cancel()" ng:disabled="{{master.$equals(form)}}">Cancel</button>
|
||||
<button ng:click="save()" ng:disabled="{{$invalidWidgets.visible() ||
|
||||
master.$equals(form)}}">Save</button>
|
||||
<label>Address:</label> <br/>
|
||||
<input type="text" ng-model="form.address.line1" size="33" required/> <br/>
|
||||
<input type="text" ng-model="form.address.city" size="12" required/>,
|
||||
<input type="text" ng-model="form.address.state" size="2"
|
||||
ng-pattern="state" required/>
|
||||
<input type="text" ng-model="form.address.zip" size="5"
|
||||
ng-pattern="zip" required/><br/><br/>
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
<div ng-repeat="contact in form.contacts">
|
||||
<select ng-model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>form={{form}}
|
||||
master={{master}}</pre>
|
||||
<pre>form={{form}}</pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enable save button', function(){
|
||||
it('should enable save button', function() {
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
@@ -84,13 +104,13 @@ master.$equals(form)}}">Save</button>
|
||||
element(':button:contains(Save)').click();
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
});
|
||||
it('should enable cancel button', function(){
|
||||
it('should enable cancel button', function() {
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Cancel)').click();
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
expect(element(':input[name=form.name]').val()).toEqual('John Smith');
|
||||
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Resources - Buzz
|
||||
@description
|
||||
@@ -13,47 +12,48 @@ to retrieve Buzz activity and comments.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
BuzzController.$inject = ['$resource'];
|
||||
function BuzzController($resource) {
|
||||
this.Activity = $resource(
|
||||
BuzzController.$inject = ['$scope', '$resource'];
|
||||
function BuzzController($scope, $resource) {
|
||||
$scope.userId = 'googlebuzz';
|
||||
$scope.Activity = $resource(
|
||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
||||
{alt: 'json', callback: 'JSON_CALLBACK'},
|
||||
{ get: {method: 'JSON', params: {visibility: '@self'}},
|
||||
replies: {method: 'JSON', params: {visibility: '@self', comments: '@comments'}}
|
||||
{ get: {method: 'JSONP', params: {visibility: '@self'}},
|
||||
replies: {method: 'JSONP', params: {visibility: '@self', comments: '@comments'}}
|
||||
});
|
||||
}
|
||||
BuzzController.prototype = {
|
||||
fetch: function() {
|
||||
this.activities = this.Activity.get({userId:this.userId});
|
||||
$scope.activities = $scope.Activity.get({userId:this.userId});
|
||||
},
|
||||
expandReplies: function(activity) {
|
||||
activity.replies = this.Activity.replies({userId: this.userId, activityId: activity.id});
|
||||
activity.replies = $scope.Activity.replies({userId: this.userId, activityId: activity.id});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div ng:controller="BuzzController">
|
||||
<input name="userId" value="googlebuzz"/>
|
||||
<button ng:click="fetch()">fetch</button>
|
||||
<div ng-controller="BuzzController">
|
||||
<input ng-model="userId"/>
|
||||
<button ng-click="fetch()">fetch</button>
|
||||
<hr/>
|
||||
<div class="buzz" ng:repeat="item in activities.data.items">
|
||||
<div class="buzz" ng-repeat="item in activities.data.items">
|
||||
<h1 style="font-size: 15px;">
|
||||
<img ng:src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng:href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href ng:click="expandReplies(item)" style="float: right;">
|
||||
<img ng-src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng-href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href ng-click="expandReplies(item)" style="float: right;">
|
||||
Expand replies: {{item.links.replies[0].count}}
|
||||
</a>
|
||||
</h1>
|
||||
{{item.object.content | html}}
|
||||
<div class="reply" ng:repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
||||
<img ng:src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng:href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
|
||||
<div class="reply" ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
||||
<img ng-src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng-href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
|
||||
{{reply.content | html}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('fetch buzz and expand', function() {
|
||||
xit('fetch buzz and expand', function() {
|
||||
element(':button:contains(fetch)').click();
|
||||
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
|
||||
element('.buzz a:contains(Expand replies):first').click();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Deep Linking
|
||||
@description
|
||||
@@ -6,7 +5,7 @@
|
||||
Deep linking allows you to encode the state of the application in the URL so that it can be
|
||||
bookmarked and the application can be restored from the URL to the same state.
|
||||
|
||||
While <angular/> does not force you to deal with bookmarks in any particular way, it has services
|
||||
While angular does not force you to deal with bookmarks in any particular way, it has services
|
||||
which make the common case described here very easy to implement.
|
||||
|
||||
# Assumptions
|
||||
@@ -28,88 +27,125 @@ controller.
|
||||
|
||||
In this example we have a simple app which consist of two screens:
|
||||
|
||||
* Welcome: url `#` Show the user contact information.
|
||||
* Settings: url `#/settings` Show an edit screen for user contact information.
|
||||
* Welcome: url `welcome` Show the user contact information.
|
||||
* Settings: url `settings` Show an edit screen for user contact information.
|
||||
|
||||
<example module="deepLinking" deps="angular-sanitize.js">
|
||||
<file name="script.js">
|
||||
angular.module('deepLinking', ['ngSanitize'])
|
||||
.config(function($routeProvider) {
|
||||
$routeProvider.
|
||||
when("/welcome", {templateUrl:'welcome.html', controller:WelcomeCntl}).
|
||||
when("/settings", {templateUrl:'settings.html', controller:SettingsCntl});
|
||||
});
|
||||
|
||||
The two partials are defined in the following URLs:
|
||||
AppCntl.$inject = ['$scope', '$route']
|
||||
function AppCntl($scope, $route) {
|
||||
$scope.$route = $route;
|
||||
|
||||
* {@link ./examples/settings.html}
|
||||
* {@link ./examples/welcome.html}
|
||||
// initialize the model to something useful
|
||||
$scope.person = {
|
||||
name:'anonymous',
|
||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
||||
};
|
||||
}
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
AppCntl.$inject = ['$route']
|
||||
function AppCntl($route) {
|
||||
// define routes
|
||||
$route.when("", {template:'./examples/welcome.html', controller:WelcomeCntl});
|
||||
$route.when("/settings", {template:'./examples/settings.html', controller:SettingsCntl});
|
||||
$route.parent(this);
|
||||
function WelcomeCntl($scope) {
|
||||
$scope.greet = function() {
|
||||
alert("Hello " + $scope.person.name);
|
||||
};
|
||||
}
|
||||
|
||||
// initialize the model to something useful
|
||||
this.person = {
|
||||
name:'anonymous',
|
||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
||||
};
|
||||
}
|
||||
function SettingsCntl($scope, $location) {
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy($scope.person);
|
||||
};
|
||||
|
||||
function WelcomeCntl($route){}
|
||||
WelcomeCntl.prototype = {
|
||||
greet: function(){
|
||||
alert("Hello " + this.person.name);
|
||||
}
|
||||
};
|
||||
$scope.save = function() {
|
||||
angular.copy($scope.form, $scope.person);
|
||||
$location.path('/welcome');
|
||||
};
|
||||
|
||||
function SettingsCntl(){
|
||||
this.cancel();
|
||||
}
|
||||
SettingsCntl.prototype = {
|
||||
cancel: function(){
|
||||
this.form = angular.copy(this.person);
|
||||
},
|
||||
$scope.cancel();
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
[ng-view] {
|
||||
border: 1px solid blue;
|
||||
margin: 0;
|
||||
padding:1em;
|
||||
}
|
||||
|
||||
save: function(){
|
||||
angular.copy(this.form, this.person);
|
||||
window.location.hash = "#";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div ng:controller="AppCntl">
|
||||
.partial-info {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="AppCntl">
|
||||
<h1>Your App Chrome</h1>
|
||||
[ <a href="#">Welcome</a> | <a href="#/settings">Settings</a> ]
|
||||
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
|
||||
<hr/>
|
||||
<span style="background-color: blue; color: white; padding: 3px;">
|
||||
<span class="partial-info">
|
||||
Partial: {{$route.current.template}}
|
||||
</span>
|
||||
<ng:view style="border: 1px solid blue; margin: 0; display:block; padding:1em;"></ng:view>
|
||||
<div ng-view></div>
|
||||
<small>Your app footer </small>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should navigate to URL', function(){
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('ng\\:view').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
input('form.name').enter('yourname');
|
||||
element(':button:contains(Save)').click();
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('ng\\:view').text()).toMatch(/Hello yourname/);
|
||||
</file>
|
||||
<file name="settings.html">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng:model="form.name" required>
|
||||
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select ng:model="contact.type">
|
||||
<option>url</option>
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
</select>
|
||||
<input type="text" ng:model="contact.url">
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<div>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
</div>
|
||||
|
||||
<button ng:click="cancel()">Cancel</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
</file>
|
||||
<file name="welcome.html">
|
||||
Hello {{person.name}},
|
||||
<div>
|
||||
Your contact information:
|
||||
<div ng:repeat="contact in person.contacts">{{contact.type}}:
|
||||
<span ng-bind-html="contact.url|linky"></span>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should navigate to URL', function() {
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
input('form.name').enter('yourname');
|
||||
element(':button:contains(Save)').click();
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the
|
||||
initialization of the {@link api/angular.service.$route $route} service with the proper URL
|
||||
routes.
|
||||
* The {@link api/angular.service.$route $route} service then watches the URL and instantiates the
|
||||
initialization of the {@link api/ng.$route $route} service with the proper URL
|
||||
routes.
|
||||
* The {@link api/ng.$route $route} service then watches the URL and instantiates the
|
||||
appropriate controller when the URL changes.
|
||||
* The {@link api/angular.widget.ng:view ng:view} widget loads the view when the URL changes. It
|
||||
also
|
||||
sets the view scope to the newly instantiated controller.
|
||||
* The {@link api/ng.directive:ngView ngView} widget loads the
|
||||
view when the URL changes. It also sets the view scope to the newly instantiated controller.
|
||||
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
|
||||
the URL is changed programatically or by the user.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Form
|
||||
@description
|
||||
@@ -11,76 +10,91 @@ allow a user to enter data.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function FormController(){
|
||||
this.user = {
|
||||
function FormController($scope) {
|
||||
var user = $scope.user = {
|
||||
name: 'John Smith',
|
||||
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
|
||||
contacts:[{type:'phone', value:'1(234) 555-1212'}]
|
||||
};
|
||||
this.state = /^\w\w$/;
|
||||
this.zip = /^\d\d\d\d\d$/;
|
||||
$scope.state = /^\w\w$/;
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.addContact = function() {
|
||||
user.contacts.push({type:'email', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
for (var i = 0, ii = user.contacts.length; i < ii; i++) {
|
||||
if (contact === user.contacts[i]) {
|
||||
$scope.user.contacts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng:controller="FormController" class="example">
|
||||
<div ng-controller="FormController" class="example">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="user.name" ng:required/> <br/><br/>
|
||||
<label>Name:</label><br>
|
||||
<input type="text" ng-model="user.name" required/> <br><br>
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" name="user.address.line1" size="33" ng:required/> <br/>
|
||||
<input type="text" name="user.address.city" size="12" ng:required/>,
|
||||
<input type="text" name="user.address.state" size="2" ng:required ng:validate="regexp:state"/>
|
||||
<input type="text" name="user.address.zip" size="5" ng:required
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
<label>Address:</label><br>
|
||||
<input type="text" ng-model="user.address.line1" size="33" required> <br>
|
||||
<input type="text" ng-model="user.address.city" size="12" required>,
|
||||
<input type="text" ng-model="user.address.state"
|
||||
ng-pattern="state" size="2" required>
|
||||
<input type="text" ng-model="user.address.zip" size="5"
|
||||
ng-pattern="zip" required><br><br>
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng:click="user.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in user.contacts">
|
||||
<select name="contact.type">
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
<div ng-repeat="contact in user.contacts">
|
||||
<select ng-model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" name="contact.value" ng:required/>
|
||||
[ <a href="" ng:click="user.contacts.$remove(contact)">X</a> ]
|
||||
<input type="text" ng-model="contact.value" required>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>user={{user}}</pre>
|
||||
<pre>user={{user | json}}</pre>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show debug', function(){
|
||||
it('should show debug', function() {
|
||||
expect(binding('user')).toMatch(/John Smith/);
|
||||
});
|
||||
it('should add contact', function(){
|
||||
it('should add contact', function() {
|
||||
using('.example').element('a:contains(add)').click();
|
||||
using('.example div:last').input('contact.value').enter('you@example.org');
|
||||
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
|
||||
expect(binding('user')).toMatch(/you@example.org/);
|
||||
});
|
||||
|
||||
it('should remove contact', function(){
|
||||
it('should remove contact', function() {
|
||||
using('.example').element('a:contains(X)').click();
|
||||
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
|
||||
});
|
||||
|
||||
it('should validate zip', function(){
|
||||
expect(using('.example').element(':input[name=user.address.zip]').attr('className'))
|
||||
.not().toMatch(/ng-validation-error/);
|
||||
it('should validate zip', function() {
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.zip').enter('abc');
|
||||
expect(using('.example').element(':input[name=user.address.zip]').attr('className'))
|
||||
.toMatch(/ng-validation-error/);
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).toMatch(/ng-invalid/);
|
||||
});
|
||||
|
||||
it('should validate state', function(){
|
||||
expect(using('.example').element(':input[name=user.address.state]').attr('className'))
|
||||
.not().toMatch(/ng-validation-error/);
|
||||
it('should validate state', function() {
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.state').enter('XXX');
|
||||
expect(using('.example').element(':input[name=user.address.state]').attr('className'))
|
||||
.toMatch(/ng-validation-error/);
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.toMatch(/ng-invalid/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -88,14 +102,13 @@ ng:validate="regexp:zip"/><br/><br/>
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The user data model is initialized {@link api/angular.directive.ng:controller controller} and is
|
||||
available in
|
||||
the {@link api/angular.scope scope} with the initial data.
|
||||
* The user data model is initialized {@link api/ng.directive:ngController controller} and is
|
||||
available in the {@link api/ng.$rootScope.Scope scope} with the initial data.
|
||||
* For debugging purposes we have included a debug view of the model to better understand what
|
||||
is going on.
|
||||
* The {@link api/angular.widget.HTML input widgets} simply refer to the model and are auto bound.
|
||||
* The inputs {@link api/angular.validator validate}. (Try leaving them blank or entering non digits
|
||||
in the zip field)
|
||||
* The {@link api/ng.directive:input input directives} simply refer
|
||||
to the model and are data-bound.
|
||||
* The inputs validate. (Try leaving them blank or entering non digits in the zip field)
|
||||
* In your application you can simply read from or write to the model and the form will be updated.
|
||||
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
|
||||
reflected in the view.
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Hello World
|
||||
@description
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" name="name" value="World"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
<script>
|
||||
function HelloCntl($scope) {
|
||||
$scope.name = 'World';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="HelloCntl">
|
||||
Your name: <input type="text" ng-model="name" value="World"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should change the binding when user enters text', function(){
|
||||
it('should change the binding when user enters text', function() {
|
||||
expect(binding('name')).toEqual('World');
|
||||
input('name').enter('angular');
|
||||
expect(binding('name')).toEqual('angular');
|
||||
@@ -22,10 +28,11 @@
|
||||
|
||||
Take a look through the source and note:
|
||||
|
||||
* The script tag that {@link guide/dev_guide.bootstrap bootstraps} the angular environment.
|
||||
* The text {@link api/angular.widget.HTML input widget} which is bound to the greeting name text.
|
||||
* The script tag that {@link guide/bootstrap bootstraps} the angular environment.
|
||||
* The text {@link api/ng.directive:input input form control} which is
|
||||
bound to the greeting name text.
|
||||
* No need for listener registration and event firing on change events.
|
||||
* The implicit presence of the `name` variable which is in the root {@link api/angular.scope scope}.
|
||||
* The implicit presence of the `name` variable which is in the root {@link api/ng.$rootScope.Scope scope}.
|
||||
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
|
||||
* The concept of {@link guide/dev_guide.templates.databinding data binding}, which reflects any
|
||||
changes to the
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook
|
||||
@description
|
||||
@@ -45,7 +44,7 @@ allowing you to send links to specific screens in your app.
|
||||
|
||||
# Services
|
||||
|
||||
{@link api/angular.service Services}: Services are long lived objects in your applications that are
|
||||
{@link api/ng Services}: Services are long lived objects in your applications that are
|
||||
available across controllers. A collection of useful services are pre-bundled with angular but you
|
||||
will likely add your own. Services are initialized using dependency injection, which resolves the
|
||||
order of initialization. This safeguards you from the perils of global state (a common way to
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: MVC
|
||||
@description
|
||||
@@ -7,17 +6,17 @@ MVC allows for a clean an testable separation between the behavior (controller)
|
||||
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
|
||||
view. This makes it very easy for the controller and the view to share the model.
|
||||
|
||||
The model is simply the controller's this. This makes it very easy to test the controller in
|
||||
isolation since one can simply instantiate the controller and test without a view, because there is
|
||||
no connection between the controller and the view.
|
||||
The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
|
||||
This makes it very easy to test the controller in isolation since one can simply instantiate the
|
||||
controller and test without a view, because there is no connection between the controller and the
|
||||
view.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function TicTacToeCntl($location){
|
||||
this.$location = $location;
|
||||
this.cellStyle= {
|
||||
function TicTacToeCntl($scope, $location) {
|
||||
$scope.cellStyle= {
|
||||
'height': '20px',
|
||||
'width': '20px',
|
||||
'border': '1px solid black',
|
||||
@@ -25,30 +24,40 @@ no connection between the controller and the view.
|
||||
'vertical-align': 'middle',
|
||||
'cursor': 'pointer'
|
||||
};
|
||||
this.reset();
|
||||
this.$watch('$location.hashSearch.board', this.readUrl);
|
||||
}
|
||||
TicTacToeCntl.prototype = {
|
||||
dropPiece: function(row, col) {
|
||||
if (!this.winner && !this.board[row][col]) {
|
||||
this.board[row][col] = this.nextMove;
|
||||
this.nextMove = this.nextMove == 'X' ? 'O' : 'X';
|
||||
this.setUrl();
|
||||
}
|
||||
},
|
||||
reset: function(){
|
||||
this.board = [
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.board = [
|
||||
['', '', ''],
|
||||
['', '', ''],
|
||||
['', '', '']
|
||||
];
|
||||
this.nextMove = 'X';
|
||||
this.winner = '';
|
||||
this.setUrl();
|
||||
},
|
||||
grade: function(){
|
||||
var b = this.board;
|
||||
this.winner =
|
||||
$scope.nextMove = 'X';
|
||||
$scope.winner = '';
|
||||
setUrl();
|
||||
};
|
||||
|
||||
$scope.dropPiece = function(row, col) {
|
||||
if (!$scope.winner && !$scope.board[row][col]) {
|
||||
$scope.board[row][col] = $scope.nextMove;
|
||||
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
|
||||
setUrl();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
$scope.$watch(function() { return $location.search().board;}, readUrl);
|
||||
|
||||
function setUrl() {
|
||||
var rows = [];
|
||||
angular.forEach($scope.board, function(row) {
|
||||
rows.push(row.join(','));
|
||||
});
|
||||
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
|
||||
}
|
||||
|
||||
function grade() {
|
||||
var b = $scope.board;
|
||||
$scope.winner =
|
||||
row(0) || row(1) || row(2) ||
|
||||
col(0) || col(1) || col(2) ||
|
||||
diagonal(-1) || diagonal(1);
|
||||
@@ -56,44 +65,36 @@ no connection between the controller and the view.
|
||||
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
|
||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
||||
},
|
||||
setUrl: function(){
|
||||
var rows = [];
|
||||
angular.forEach(this.board, function(row){
|
||||
rows.push(row.join(','));
|
||||
});
|
||||
this.$location.hashSearch.board = rows.join(';') + '/' + this.nextMove;
|
||||
},
|
||||
readUrl: function(value) {
|
||||
}
|
||||
|
||||
function readUrl(value) {
|
||||
if (value) {
|
||||
value = value.split('/');
|
||||
this.nextMove = value[1];
|
||||
$scope.nextMove = value[1];
|
||||
angular.forEach(value[0].split(';'), function(row, col){
|
||||
this.board[col] = row.split(',');
|
||||
}, this);
|
||||
this.grade();
|
||||
} else {
|
||||
this.reset();
|
||||
$scope.board[col] = row.split(',');
|
||||
});
|
||||
grade();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3>Tic-Tac-Toe</h3>
|
||||
<div ng:controller="TicTacToeCntl">
|
||||
<div ng-controller="TicTacToeCntl">
|
||||
Next Player: {{nextMove}}
|
||||
<div class="winner" ng:show="winner">Player {{winner}} has won!</div>
|
||||
<div class="winner" ng-show="winner">Player {{winner}} has won!</div>
|
||||
<table class="board">
|
||||
<tr ng:repeat="row in board" style="height:15px;">
|
||||
<td ng:repeat="cell in row" ng:style="cellStyle"
|
||||
ng:click="dropPiece($parent.$index, $index)">{{cell}}</td>
|
||||
<tr ng-repeat="row in board" style="height:15px;">
|
||||
<td ng-repeat="cell in row" ng-style="cellStyle"
|
||||
ng-click="dropPiece($parent.$index, $index)">{{cell}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng:click="reset()">reset board</button>
|
||||
<button ng-click="reset()">reset board</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should play a game', function(){
|
||||
it('should play a game', function() {
|
||||
piece(1, 1);
|
||||
expect(binding('nextMove')).toEqual('O');
|
||||
piece(3, 1);
|
||||
@@ -124,4 +125,4 @@ board variable.
|
||||
* The view can call any controller function.
|
||||
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
|
||||
hash so the browser's back button will undo game steps. See deep-linking. This example calls {@link
|
||||
api/angular.scope.$watch $watch()} to set up a listener that invokes `readUrl()` when needed.
|
||||
api/ng.$rootScope.Scope#$watch $watch()} to set up a listener that invokes `readUrl()` when needed.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Bootstrap
|
||||
@description
|
||||
|
||||
# Overview
|
||||
|
||||
This page explains the Angular initialization process and how you can manually initialize Angular
|
||||
if necessary.
|
||||
|
||||
|
||||
# Angular `<script>` Tag
|
||||
|
||||
This example shows the recommended path for integrating Angular with what we call automatic
|
||||
initialization.
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng-app>
|
||||
<body>
|
||||
...
|
||||
<script src="angular.js">
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
* Place the `script` tag at the bottom of the page. Placing script tags at the end of the page
|
||||
improves app load time because the HTML loading is not blocked by loading of the `angular.js`
|
||||
script. You can get the latest bits from {@link http://code.angularjs.org}. Please don't link
|
||||
your production code to this URL, as it will expose a security hole on your site. For
|
||||
experimental development linking to our site is fine.
|
||||
* Choose: `angular-[version].js` for a human-readable file, suitable for development and
|
||||
debugging.
|
||||
* Choose: `angular-[version].min.js` for a compressed and obfuscated file, suitable for use in
|
||||
production.
|
||||
* Place `ng-app` to the root of your application, typically on the `<html>` tag if you want
|
||||
angular to auto-bootstrap your application.
|
||||
|
||||
<html ng-app>
|
||||
|
||||
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
|
||||
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
|
||||
`ng:`.)
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
|
||||
|
||||
# Automatic Initialization
|
||||
|
||||
Angular initializes automatically upon `DOMContentLoaded` event, at which point Angular looks for
|
||||
the {@link api/ng.directive:ngApp `ng-app`} directive which
|
||||
designates your application root. If the {@link
|
||||
api/ng.directive:ngApp `ng-app`} directive is found then Angular
|
||||
will:
|
||||
|
||||
* load the {@link guide/module module} associated with the directive.
|
||||
* create the application {@link api/AUTO.$injector injector}
|
||||
* compile the DOM treating the {@link api/ng.directive:ngApp
|
||||
`ng-app`} directive as the root of the compilation. This allows you to tell it to treat only a
|
||||
portion of the DOM as an Angular application.
|
||||
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html ng-app="optionalModuleName">
|
||||
<body>
|
||||
I can add: {{ 1+2 }}.
|
||||
<script src="angular.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
# Manual Initialization
|
||||
|
||||
|
||||
If you need to have more control over the initialization process, you can use a manual
|
||||
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
|
||||
or the need to perform an operation before Angular compiles a page.
|
||||
|
||||
|
||||
Here is an example of manually initializing Angular. The example is equivalent to using the {@link
|
||||
api/ng.directive:ngApp ng-app} directive.
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.bootstrap(document);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
This is the sequence that your code should follow:
|
||||
|
||||
1. After the page and all of the code is loaded, find the root of the HTML template, which is
|
||||
typically the root of the document.
|
||||
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the template into an
|
||||
executable, bi-directionally bound application.
|
||||
@@ -0,0 +1,141 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: HTML Compiler
|
||||
@description
|
||||
|
||||
# Overview
|
||||
|
||||
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
|
||||
extensions {@link api/ng.$compileProvider#directive directives}.
|
||||
|
||||
HTML has a lot of constructs for formatting the HTML for static documents in declarative fashion.
|
||||
For example if something needs to be centered, there is no need to provide instructions to the
|
||||
browser how the window size needs to be divided in half so that center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add `align="center"` attribute to any
|
||||
element to achieve the desired behavior. Such is the power of declarative language.
|
||||
|
||||
But the declarative language is also limited, since it does not allow you to teach the browser new
|
||||
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
|
||||
instead of 1/2. What is needed is a way to teach browser new HTML syntax.
|
||||
|
||||
Angular comes pre-bundled with common directives which are useful for building any app. We also
|
||||
expect that you will create directives that are specific to your app. These extension become a
|
||||
Domain Specific Language for building your application.
|
||||
|
||||
All of this compilation takes place in the web browser; no server side or pre-compilation step is
|
||||
involved.
|
||||
|
||||
|
||||
# Compiler
|
||||
|
||||
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens into two phases.
|
||||
|
||||
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
|
||||
function.
|
||||
|
||||
2. **Link:** combine the directives with a scope and produce a live view. Any changes in the
|
||||
scope model are reflected in the view, and any user interactions with the view are reflected
|
||||
in the scope model. Making the scope model a single source of truth.
|
||||
|
||||
Some directives such {@link api/ng.directive:ngRepeat
|
||||
`ng-repeat`} clone DOM elements once for each item in collection. Having a compile and link phase
|
||||
improves performance since the cloned template only needs to be compiled once, and then linked
|
||||
once for each clone instance.
|
||||
|
||||
|
||||
# Directive
|
||||
|
||||
Directive is a behavior which should be triggered when specific HTML constructs are encountered in
|
||||
compilation process. The directives can be placed in element names, attributes, class names, as
|
||||
well as comments. Here are some equivalent examples of invoking {@link
|
||||
api/ng.directive:ngBind `ng-bind`} directive.
|
||||
|
||||
<pre>
|
||||
<span ng-bind="exp"></span>
|
||||
<span class="ng-bind: exp;"></span>
|
||||
<ng-bind></ng-bind>
|
||||
<!-- directive: ng-bind exp -->
|
||||
</pre>
|
||||
|
||||
Directive is just a function which executes when the compiler encounters it in the DOM. See {@link
|
||||
api/ng.$compileProvider#directive directive API} for in-depth documentation on how
|
||||
to write directives.
|
||||
|
||||
Here is a directive which makes any element draggable. Notice the `draggable` attribute on the
|
||||
`<span>` element.
|
||||
|
||||
<example module="drag">
|
||||
<file name="script.js">
|
||||
angular.module('drag', []).
|
||||
directive('draggable', function($document) {
|
||||
var startX=0, startY=0, x = 0, y = 0;
|
||||
return function(scope, element, attr) {
|
||||
element.css({
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
backgroundColor: 'lightgrey',
|
||||
cursor: 'pointer'
|
||||
});
|
||||
element.bind('mousedown', function(event) {
|
||||
startX = event.screenX - x;
|
||||
startY = event.screenY - y;
|
||||
$document.bind('mousemove', mousemove);
|
||||
$document.bind('mouseup', mouseup);
|
||||
});
|
||||
|
||||
function mousemove(event) {
|
||||
y = event.screenY - startY;
|
||||
x = event.screenX - startX;
|
||||
element.css({
|
||||
top: y + 'px',
|
||||
left: x + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
function mouseup() {
|
||||
$document.unbind('mousemove', mousemove);
|
||||
$document.unbind('mouseup', mouseup);
|
||||
}
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<span draggable>Drag ME</span>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
The presence of `draggable` attribute on any element gives the element new behavior. The beauty of
|
||||
this approach is that we have taught the browser a new trick. We have extended the vocabulary of
|
||||
what the browser understands in a way, which is natural to anyone who is familiar with HTML
|
||||
principles.
|
||||
|
||||
|
||||
# Understanding View
|
||||
|
||||
There are many templating systems out there. Most of them consume a static string template and
|
||||
combine it with data, resulting in a new string. The resulting text is then `innerHTML`ed into
|
||||
an element.
|
||||
|
||||
<img src="img/One_Way_Data_Binding.png">
|
||||
|
||||
This means that any changes to the data need to be re-merged with the template and then
|
||||
`innerHTML`ed into the DOM. Some of the issues are: reading user input and merging it with data,
|
||||
clobbering user input by overwriting it, managing the whole update process, and lack of behavior
|
||||
expressiveness.
|
||||
|
||||
Angular is different. The Angular compiler consumes the DOM with directives, not string templates.
|
||||
The result is a linking function, which when combined with a scope model results in a live view. The
|
||||
view and scope model bindings are transparent. No action from the developer is needed to update
|
||||
the view. And because no `innerHTML` is used there are no issues of clobbering user input.
|
||||
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
|
||||
well.
|
||||
|
||||
<img src="img/Two_Way_Data_Binding.png">
|
||||
|
||||
The Angular approach produces a stable DOM. This means that the DOM element instance bound to a model
|
||||
item instance does not change for the lifetime of the binding. This means that the code can get
|
||||
hold of the elements and register event handlers and know that the reference will not be destroyed
|
||||
by template data merge.
|
||||
@@ -0,0 +1,467 @@
|
||||
@ngdoc overview
|
||||
@name Conceptual Overview
|
||||
@description
|
||||
|
||||
# Overview
|
||||
|
||||
This document gives a quick overview of the main angular components and how they work together.
|
||||
These are:
|
||||
|
||||
* {@link concepts#startup startup} - bring up hello world
|
||||
* {@link concepts#runtime runtime} - overview of angular runtime
|
||||
* {@link concepts#scope scope} - the glue between the view and the controller
|
||||
* {@link concepts#controller controller} - application behavior
|
||||
* {@link concepts#model model} - your application data
|
||||
* {@link concepts#view view} - what the user sees
|
||||
* {@link concepts#directives directives} - extend HTML vocabulary
|
||||
* {@link concepts#filters filters} - format the data in user locale
|
||||
* {@link concepts#injector injector} - assembles your application
|
||||
* {@link concepts#module module} - configures the injector
|
||||
* {@link concepts#angular_namespace `$`} - angular namespace
|
||||
|
||||
<a name="startup"></a>
|
||||
# Startup
|
||||
|
||||
This is how we get the ball rolling (refer to the diagram and example below):
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png">
|
||||
|
||||
1. Browser loads the HTML and parses it into a DOM
|
||||
2. Browser loads `angular.js` script
|
||||
3. Angular waits for `DOMContentLoaded` event
|
||||
4. Angular looks for {@link api/ng.directive:ngApp ng-app}
|
||||
{@link guide/directive directive}, which designates application boundary
|
||||
5. {@link guide/module Module} specified in {@link
|
||||
api/ng.directive:ngApp ng-app} (if any) is used to configure
|
||||
the {@link api/AUTO.$injector $injector}
|
||||
6. {@link api/AUTO.$injector $injector} is used to create the {@link
|
||||
api/ng.$compile $compile} service as well as {@link
|
||||
api/ng.$rootScope $rootScope}
|
||||
7. {@link api/ng.$compile $compile} service is used to compile the DOM and link
|
||||
it with {@link api/ng.$rootScope $rootScope}
|
||||
8. {@link api/ng.directive:ngInit ng-init} {@link
|
||||
guide/directive directive} assigns `World` to the `name` property on the {@link guide/scope
|
||||
scope}
|
||||
9. The `{{name}}` {@link api/ng.$interpolate interpolates} the expression to
|
||||
`Hello World!`
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<p ng-init=" name='World' ">Hello {{name}}!</p>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
<a name="runtime"></a>
|
||||
# Runtime
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png">
|
||||
|
||||
The diagram and the example below describe how Angular interacts with browser's event loop.
|
||||
|
||||
1. Browsers event-loop waits for an event to arrive. Event is a user interactions, timer event,
|
||||
or network event (response from a server).
|
||||
2. The events callback gets executed. This enters the JavaScript context. The callback can
|
||||
modify the DOM structure.
|
||||
3. Once the callback finishes execution, the browser leaves the JavaScript context and
|
||||
re-renders the view based on DOM changes.
|
||||
|
||||
Angular modifies the normal JavaScript flow by providing it's own event processing loop. This
|
||||
splits the JavaScript into classical and Angular execution context. Only operations which are
|
||||
applied in Angular execution context will benefit from angular data-binding, exception handling,
|
||||
property watching, etc... Use $apply() to enter Angular execution context from JavaScript. Keep in
|
||||
mind that in most places (controllers, services) the $apply has already been called for you by the
|
||||
directive which is handling the event. The need to call $apply is reserved only when
|
||||
implementing custom event callbacks, or when working with a third-party library callbacks.
|
||||
|
||||
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
|
||||
api/ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
|
||||
the work you wish to do in Angular execution context.
|
||||
2. Angular executes the `stimulusFn()`, which typically modifies application state.
|
||||
3. Angular enters the {@link api/ng.$rootScope.Scope#$digest $digest} loop. The
|
||||
loop is made up of two smaller loops which process {@link
|
||||
api/ng.$rootScope.Scope#$evalAsync $evalAsync} queue and the {@link
|
||||
api/ng.$rootScope.Scope#$watch $watch} list. The {@link
|
||||
api/ng.$rootScope.Scope#$digest $digest} loop keeps iterating until the model
|
||||
stabilizes, which means that the {@link api/ng.$rootScope.Scope#$evalAsync
|
||||
$evalAsync} queue is empty and the {@link api/ng.$rootScope.Scope#$watch
|
||||
$watch} list does not detect any changes.
|
||||
4. The {@link api/ng.$rootScope.Scope#$evalAsync $evalAsync} queue is used to
|
||||
schedule work which needs to occur outside of current stack frame, but before the browser
|
||||
view render. This is usually done with `setTimeout(0)`, but the `setTimeout(0)` approach
|
||||
suffers from slowness and may cause view flickering since the browser renders the view after
|
||||
each event.
|
||||
5. The {@link api/ng.$rootScope.Scope#$watch $watch} list is a set of expressions
|
||||
which may have changed since last iteration. If a change is detected then the `$watch`
|
||||
function is called which typically updates the DOM with the new value.
|
||||
6. Once Angular {@link api/ng.$rootScope.Scope#$digest $digest} loop finishes
|
||||
the execution leaves the Angular and JavaScript context. This is followed by the browser
|
||||
re-rendering the DOM to reflect any changes.
|
||||
|
||||
|
||||
Here is the explanation of how the `Hello wold` example achieves the data-binding effect when the
|
||||
user enters text into the text field.
|
||||
|
||||
1. During the compilation phase:
|
||||
1. the {@link api/ng.directive:ngModel ng-model} and {@link
|
||||
api/ng.directive:input input} {@link guide/directive
|
||||
directive} set up a `keydown` listener on the `<input>` control.
|
||||
2. the {@link api/ng.$interpolate {{name}} } interpolation
|
||||
sets up a {@link api/ng.$rootScope.Scope#$watch $watch} to be notified of
|
||||
`name` changes.
|
||||
2. During the runtime phase:
|
||||
1. Pressing an '`X`' key causes the browser to emit a `keydown` event on the input control.
|
||||
2. The {@link api/ng.directive:input input} directive
|
||||
captures the change to the input's value and calls {@link
|
||||
api/ng.$rootScope.Scope#$apply $apply}`("name = 'X';")` to update the
|
||||
application model inside the Angular execution context.
|
||||
3. Angular applies the `name = 'X';` to the model.
|
||||
4. The {@link api/ng.$rootScope.Scope#$digest $digest} loop begins
|
||||
5. The {@link api/ng.$rootScope.Scope#$watch $watch} list detects a change
|
||||
on the `name` property and notifies the {@link api/ng.$interpolate
|
||||
{{name}} } interpolation, which in turn updates the DOM.
|
||||
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
|
||||
the JavaScript execution context.
|
||||
7. The browser re-renders the view with update text.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<input ng-model="name">
|
||||
<p>Hello {{name}}!</p>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<a name="scope"></a>
|
||||
#Scope
|
||||
|
||||
The {@link guide/scope scope} is responsible for detecting changes to the model section and
|
||||
provides the execution context for expressions. The scopes are nested in a hierarchical structure
|
||||
which closely follow the DOM structure. (See individual directive documentation to see which
|
||||
directives cause a creation of new scopes.)
|
||||
|
||||
The following example demonstrates how `name` {@link guide/expression expression} will evaluate
|
||||
into different value depending on which scope it is evaluated in. The example is followed by
|
||||
a diagram depicting the scope boundaries.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<div class="show-scope">
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="GreetCtrl">
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
<div ng-controller="ListCtrl">
|
||||
<ol>
|
||||
<li ng-repeat="name in names">{{name}}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function GreetCtrl($scope) {
|
||||
$scope.name = 'World';
|
||||
}
|
||||
|
||||
function ListCtrl($scope) {
|
||||
$scope.names = ['Igor', 'Misko', 'Vojta'];
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.show-scope .doc-example-live.ng-scope,
|
||||
.show-scope .doc-example-live .ng-scope {
|
||||
border: 1px solid red;
|
||||
margin: 3px;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
</div>
|
||||
|
||||
<img class="center" src="img/guide/concepts-scope.png">
|
||||
|
||||
|
||||
<a name="controller"></a>
|
||||
# Controller
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-controller.png">
|
||||
|
||||
Controller is the code behind the view. Its job is to construct the model and publish it to the
|
||||
view along with callback methods. The view is a projection of the scope onto the template (the
|
||||
HTML). The scope is the glue which marshals the model to the view and forwards the events to the
|
||||
controller.
|
||||
|
||||
The separation of the controller and the view is important because:
|
||||
|
||||
* The controller is written in JavaScript. JavaScript is imperative. Imperative is a good fit
|
||||
for specifying application behavior. The controller should not contain any rendering
|
||||
information (DOM references or HTML fragments).
|
||||
* The view template is written in HTML. HTML is declarative. Declarative is a good fit for
|
||||
specifying UI. The View should not contain any behavior.
|
||||
* Since the controller is unaware of the view, there could be many views for the same
|
||||
controller. This is important for re-skinning, device specific views (i.e. mobile vs desktop),
|
||||
and testability.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="MyCtrl">
|
||||
Hello {{name}}!
|
||||
<button ng-click="action()">
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function MyCtrl($scope) {
|
||||
$scope.action = function() {
|
||||
$scope.name = 'OK';
|
||||
}
|
||||
|
||||
$scope.name = 'World';
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
<a name="model"></a>
|
||||
# Model
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png">
|
||||
|
||||
The model is the data which is used merged with the template to produce the view. To be able to
|
||||
render the model into the view, the model has to be referenceable from the scope. Unlike many
|
||||
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
|
||||
to inherit from or special accessor methods for accessing or changing the model. The model can be
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
|
||||
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
|
||||
<a name="view"></a>
|
||||
# View
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
|
||||
|
||||
The view is what the users sees. The view begins its life as a template, it is merged with the
|
||||
model and finally rendered into the browser DOM. Angular takes a very different approach to
|
||||
rendering the view, to most other templating systems.
|
||||
|
||||
* **Others** - Most templating systems begin as an HTML string with special templating markup.
|
||||
Often the template markup breaks the HTML syntax which means that the template can not be
|
||||
edited by an HTML editor. The template string is then parsed by the template engine, and
|
||||
merged with the data. The result of the merge is an HTML string. The HTML string is then
|
||||
written to the browser using the `.innerHTML`, which causes the browser to render the HTML.
|
||||
When the model changes the whole process needs to be repeated. The granularity of the template
|
||||
is the granularity of the DOM updates. The key here is that the templating system manipulates
|
||||
strings.
|
||||
* **Angular** - Angular is different, since its templating system works on DOM objects not on
|
||||
strings. The template is still written in HTML string, but it is HTML (not HTML with
|
||||
template sprinkled in.) The browser parses the HTML into DOM, and the DOM becomes the input to
|
||||
the template engine know as the {@link api/ng.$compile compiler}. The compiler
|
||||
looks for {@link guide/directive directives} which in turn set up {@link
|
||||
api/ng.$rootScope.Scope#$watch watches} on the model. The result is a
|
||||
continuously updating view which does not need template model re-merging. Your model becomes
|
||||
the single source-of-truth for your view.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
<input ng-model="list" ng-list> <br>
|
||||
<input ng-model="list" ng-list> <br>
|
||||
<pre>list={{list}}</pre> <br>
|
||||
<ol>
|
||||
<li ng-repeat="item in list">
|
||||
{{item}}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
<a name="directives"></a>
|
||||
# Directives
|
||||
|
||||
A directive is a behavior or DOM transformation which is triggered by a presence of an attribute,
|
||||
element name, or a class name. A directive allows you to extend the HTML vocabulary in a
|
||||
declarative fashion. Following is an example which enables data-binding for the `contenteditable`
|
||||
in HTML.
|
||||
|
||||
<example module="directive">
|
||||
<file name="script.js">
|
||||
angular.module('directive', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// view -> model
|
||||
elm.bind('blur', function() {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
});
|
||||
|
||||
// model -> view
|
||||
ctrl.render = function(value) {
|
||||
elm.html(value);
|
||||
};
|
||||
|
||||
// load init value from DOM
|
||||
ctrl.$setViewValue(elm.html());
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div contentEditable="true" ng-model="content">Edit Me</div>
|
||||
<pre>model = {{content}}</pre>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
div[contentEditable] {
|
||||
cursor: pointer;
|
||||
background-color: #D0D0D0;
|
||||
margin-bottom: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<a name="filters"></a>
|
||||
# Filters
|
||||
|
||||
{@link api/ng.$filter Filters} perform data transformation roles. Typically
|
||||
they are used in conjunction with the locale to format the data in locale specific output.
|
||||
They are follow the spirit of UNIX filters and follow similar syntax `|` (pipe).
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
Number formatting: {{ 1234567890 | number }} <br>
|
||||
array filtering <input ng-model="predicate">
|
||||
{{ list | filter:predicate | json }}
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
<a name="module"></a>
|
||||
<a name="injector"></a>
|
||||
# Modules and the Injector
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png">
|
||||
|
||||
An {@link api/AUTO.$injector injector} is a service locator. There is a single
|
||||
{@link api/AUTO.$injector injector} per Angular {@link
|
||||
api/ng.directive:ngApp application}. The {@link
|
||||
api/AUTO.$injector injector} provides a way to look up an object instance by its
|
||||
name. The injector keeps on internal cache of all objects so that repeated calls to get the same
|
||||
object name result in the same instance. If the object does not exist, then the {@link
|
||||
api/AUTO.$injector injector} asks the instance factory to create a new instance.
|
||||
|
||||
A {@link api/angular.Module module} is a way to configure the injector's instance factory, known
|
||||
as a {@link api/AUTO.$provide provider}.
|
||||
|
||||
<div class='clear'></div>
|
||||
<pre>
|
||||
// Create a module
|
||||
var myModule = angular.module('myModule', [])
|
||||
|
||||
// Configure the injector
|
||||
myModule.factory('serviceA', function() {
|
||||
return {
|
||||
// instead of {}, put your object creation here
|
||||
};
|
||||
});
|
||||
|
||||
// create an injector and configure it from 'myModule'
|
||||
var $injector = angular.injector('myModule');
|
||||
|
||||
// retrieve an object from the injector by name
|
||||
var serviceA = $injector.get('serviceA');
|
||||
|
||||
// always true because of instance cache
|
||||
$injector.get('serviceA') === $injector.get('serviceA');
|
||||
</pre>
|
||||
|
||||
|
||||
But the real magic of the {@link api/AUTO.$injector injector} is that it can be
|
||||
used to {@link api/AUTO.$injector#invoke call} methods and {@link
|
||||
api/AUTO.$injector#instantiate instantiate} types. This subtle feature is what
|
||||
allows the methods and types to ask for their dependencies rather then to look for them.
|
||||
|
||||
<pre>
|
||||
// You write functions such as this one.
|
||||
function doSomething(serviceA, serviceB) {
|
||||
// do something here.
|
||||
}
|
||||
|
||||
// Angular provides the injector for your application
|
||||
var $injector = ...;
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// the old-school way of getting dependencies.
|
||||
var serviceA = $injector.get('serviceA');
|
||||
var serviceB = $injector.get('serviceB');
|
||||
|
||||
// now call the function
|
||||
doSomething(serviceA, serviceB);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// the cool way of getting dependencies.
|
||||
// the $injector will supply the arguments to the function automatically
|
||||
$injector.invoke(doSomething); // This is how the framework calls your functions
|
||||
</pre>
|
||||
|
||||
Notice that the only thing you needed to write was the function, and list the dependencies in the
|
||||
function arguments. When angular calls the function, it will use the {@link
|
||||
api/AUTO.$injector#invoke call} which will automatically fill the function
|
||||
arguments.
|
||||
|
||||
Examine the `ClockCtrl` bellow, and notice how it list the dependencies in constructor. When the
|
||||
{@link api/ng.directive:ngController ng-controller} instantiates
|
||||
the controller it automatically provides the dependencies. There is no need to create
|
||||
dependencies, look for dependencies, or even get a reference to the injector.
|
||||
|
||||
<example module="timeExampleModule">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ClockCtrl">
|
||||
Current time is: {{ time.now }}
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('timeExampleModule', []).
|
||||
// Declare new object call time,
|
||||
// which will be available for injection
|
||||
factory('time', function($timeout) {
|
||||
var time = {};
|
||||
|
||||
(function tick() {
|
||||
time.now = new Date().toString();
|
||||
$timeout(tick, 1000);
|
||||
})();
|
||||
return time;
|
||||
});
|
||||
|
||||
// Notice that you can simply ask for time
|
||||
// and it will be provided. No need to look for it.
|
||||
function ClockCtrl($scope, time) {
|
||||
$scope.time = time;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
<a name="angular_namespace"></a>
|
||||
# Angular Namespace
|
||||
|
||||
To prevent accidental name collision, Angular prefixes names of objects which could potentially
|
||||
collide with `$`. Please do not use the `$` prefix in your code as it may accidentally collide
|
||||
with Angular code.
|
||||
@@ -1,101 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Initializing Angular: Automatic Initiialization
|
||||
@description
|
||||
|
||||
Angular initializes automatically when you load the angular script into your page, specifying
|
||||
angular's `ng:autobind` attribute with no arguments:
|
||||
|
||||
<script src="angular.js" ng:autobind>
|
||||
|
||||
From a high-level view, this is what happens during angular's automatic initialization process:
|
||||
|
||||
1. The browser loads the page, and then runs the angular script.
|
||||
|
||||
The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The
|
||||
compilation phase is initiated in the page's `onLoad()` handler. Angular doesn't begin processing
|
||||
the page until after the page load is complete.
|
||||
|
||||
2. Angular finds the root of the HTML document and creates the global variable `angular` in the
|
||||
global namespace. Everything that angular subsequently creates is bound to fields in this global
|
||||
object.
|
||||
|
||||
3. Angular walks the DOM looking for angular widgets, directives, and markup (such as `ng:init` or
|
||||
`ng:repeat`). As angular encounters these, it creates child scopes as necessary and attaches them
|
||||
to the DOM, registers listeners on those scopes, associates any controller functions with their
|
||||
data and their part of the view, and ultimately constructs a runnable application. The resulting
|
||||
app features two-way data-binding and a nice separation between data, presentation, and business
|
||||
logic.
|
||||
|
||||
4. For the duration of the application session (while the page is loaded), angular monitors the
|
||||
state of the application, and updates the view and the data model whenever the state of either one
|
||||
changes.
|
||||
|
||||
For details on how the compiler works, see {@link dev_guide.compiler Angular HTML Compiler}.
|
||||
|
||||
|
||||
## Initialization Options
|
||||
|
||||
The reason why `ng:autobind` exists is because angular should not assume that the entire HTML
|
||||
document should be processed just because the `angular.js` script is included. In order to compile
|
||||
only a part of the document, specify the ID of the element you want to use for angular's root
|
||||
element as the value of the `ng:autobind` attribute:
|
||||
|
||||
ng:autobind="angularContent"
|
||||
|
||||
|
||||
## Auto-bootstrap with `#autobind`
|
||||
|
||||
In some rare cases you can't define the `ng:` prefix before the script tag's attribute (for
|
||||
example, in some CMS systems). In those situations it is possible to auto-bootstrap angular by
|
||||
appending `#autobind` to the `<script src=...>` URL, like in this snippet:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript"
|
||||
src="http://code.angularjs.org/angular.js#autobind"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div xmlns:ng="http://angularjs.org">
|
||||
Hello {{'world'}}!
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
As with `ng:autobind`, you can specify an element id that should be exclusively targeted for
|
||||
compilation as the value of the `#autobind`, for example: `#autobind=angularContent`.
|
||||
|
||||
## Filename Restrictions for Auto-bootstrap
|
||||
|
||||
In order for us to find the auto-bootstrap from a script attribute or URL fragment, the value of
|
||||
the `script` `src` attribute that loads the angular script must match one of these naming
|
||||
conventions:
|
||||
|
||||
- `angular.js`
|
||||
- `angular-min.js`
|
||||
- `angular-x.x.x.js`
|
||||
- `angular-x.x.x.min.js`
|
||||
- `angular-x.x.x-xxxxxxxx.js` (dev snapshot)
|
||||
- `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot)
|
||||
- `angular-bootstrap.js` (used for development of angular)
|
||||
|
||||
Optionally, any of the filename formats above can be prepended with a relative or absolute URL that
|
||||
ends with `/`.
|
||||
|
||||
## Global Angular Object
|
||||
|
||||
The angular script creates a single global variable `angular` in the global namespace. All angular
|
||||
APIs are bound to fields of this global object.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.bootstrap Initializing Angular}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
|
||||
|
||||
## Related API
|
||||
|
||||
{@link api/angular.compile Compiler API}
|
||||
@@ -1,48 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Initializing Angular: Manual Initialization
|
||||
@description
|
||||
|
||||
Letting angular handle the initialization process (bootstrapping) is a handy way to start using
|
||||
angular, but advanced users who want more control over the initialization process can choose to use
|
||||
the manual bootstrapping method instead.
|
||||
|
||||
The best way to get started with manual bootstrapping is to look at the what happens when you use
|
||||
{@link api/angular.directive.ng:autobind ng:autobind}, by showing each step of the process
|
||||
explicitly.
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular.js"
|
||||
ng:autobind></script>
|
||||
<script type="text/javascript">
|
||||
(angular.element(document).ready(function() {
|
||||
angular.compile(document)();
|
||||
})(document);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
This is the sequence that your code should follow if you bootstrap angular on your own:
|
||||
|
||||
1. After the page is loaded, find the root of the HTML template, which is typically the root of
|
||||
the document.
|
||||
2. Run angular's {@link dev_guide.compiler Angular HTML compiler}, which converts a template into
|
||||
an executable, bi-directionally bound application.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.bootstrap Initializing Angular}
|
||||
* {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization}
|
||||
* {@link dev_guide.compiler Angular HTML compiler}
|
||||
|
||||
## Related API
|
||||
|
||||
{@link api/angular.compile Compiler API}
|
||||
@@ -1,65 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Initializing Angular
|
||||
@description
|
||||
|
||||
Initializing angular consists of loading the `angular.js` script in your page, and specifying how
|
||||
angular should process and manage the page. To initialize angular you do the following:
|
||||
|
||||
* Specify the angular namespace in the `<html>` page
|
||||
* Choose which flavor of angular script to load (debug or production)
|
||||
* Specify whether or not angular should process and manage the page automatically (`ng:autobind`)
|
||||
|
||||
The simplest way to initialize angular is to load the angular script and tell angular to compile
|
||||
and manage the whole page. You do this as follows:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
<script src="angular.js" ng:autobind>
|
||||
</body>
|
||||
</pre>
|
||||
|
||||
|
||||
## Specifying the Angular Namespace
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
You need to declare the angular namespace declaration in the following cases:
|
||||
|
||||
* For all types of browser if you are using XHTML.
|
||||
* For Internet Explorer older than version 9 (because older versions of IE do not render widgets
|
||||
properly for either HTML or XHTML).
|
||||
|
||||
|
||||
## Creating Your Own Namespaces
|
||||
|
||||
When you are ready to define your own {@link dev_guide.compiler.widgets widgets}, you must create
|
||||
your own namespace in addition to specifying the angular namespace. You use your own namespace to
|
||||
form the fully qualified name for widgets that you create.
|
||||
|
||||
For example, you could map the alias `my` to your domain, and create a widget called `my:widget`.
|
||||
To create your own namespace, simply add another `xmlns` tag to your page, create an alias, and set
|
||||
it to your unique domain:
|
||||
|
||||
<html xmlns:ng="http://angularjs.org" xmlns:my="http://mydomain.com">
|
||||
|
||||
|
||||
## Loading the Angular Bootstrap Script
|
||||
|
||||
The angular bootstrap script comes in two flavors; a debug script, and a production script:
|
||||
|
||||
* angular-[version].js - This is a human-readable file, suitable for development and debugging.
|
||||
* angular-[version].min.js - This is a compressed and obfuscated file, suitable for use in
|
||||
production.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.bootstrap.auto_bootstrap Automatic Initialization}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
|
||||
@@ -1,39 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Directives: Creating Custom Angular Directives
|
||||
@description
|
||||
|
||||
The following code snippet shows how to define a custom directive. You define a new directive by
|
||||
extending the {@link dev_guide.compiler Angular HTML compiler}. The code snippet below is a
|
||||
simplified definition of the built-in {@link api/angular.directive.ng:bind ng:bind} directive:
|
||||
|
||||
<pre>
|
||||
angular.directive('ng:bind', function(expression, compiledElement) {
|
||||
var compiler = this;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value) {
|
||||
linkElement.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
# Additional Compiler Methods for Custom Directives
|
||||
|
||||
The angular compiler exposes methods that you may need to use when writing your own widgets and
|
||||
directives. For example, the `descend()` method lets you control whether the compiler ignores or
|
||||
processes child elements of the element it is compiling. For information on this and other
|
||||
compiler methods, see the {@link api/angular.compile Compiler API doc}.
|
||||
|
||||
|
||||
## Related Docs
|
||||
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.directive Angular Directive API}.
|
||||
@@ -1,47 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Directives
|
||||
@description
|
||||
|
||||
An angular directive is a custom HTML attribute that angular knows how to process. You add them to
|
||||
a template element like any other attribute. Angular directives all have a `ng:` prefix. In the
|
||||
following example, the angular directive (`ng:controller`) is a div tag:
|
||||
|
||||
<div ng:controller>
|
||||
|
||||
You use angular directives to modify DOM element properties. The element you modify can be an
|
||||
existing HTML element type or a custom DOM element type that you created. You can use any number of
|
||||
directives per element.
|
||||
|
||||
You add angular directives to a standard HTML tag as in the following example, in which we have
|
||||
added the {@link api/angular.directive.ng:click ng:click} directive to a button tag:
|
||||
|
||||
<button name="button1" ng:click="foo()">Click This</button>
|
||||
|
||||
In the example above, `name` is the standard HTML attribute, and `ng:click` is the angular
|
||||
directive. The `ng:click` directive lets you implement custom behavior in an associated controller
|
||||
function.
|
||||
|
||||
In the next example, we add the {@link api/angular.directive.ng:bind ng:bind} directive to a
|
||||
`<span>` tag:
|
||||
|
||||
<span ng:bind="1+2"></span>
|
||||
|
||||
The `ng:bind` directive tells angular to set up {@link dev_guide.templates.databinding data
|
||||
binding} between the data model and the view for the specified expression. When the angular {@link
|
||||
dev_guide.compiler compiler} encounters an `ng:bind` directive in a template, it passes the
|
||||
attribute value to the `ng:bind` function, which in turn sets up the data binding. On any change to
|
||||
the expression in the model, the view is updated to display the span text with the changed
|
||||
expression value.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.directives.creating_directives Creating Angular Directives}
|
||||
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
|
||||
|
||||
## Related API:
|
||||
|
||||
* {@link api/angular.directive Directive API}
|
||||
* {@link api/angular.widget Widget API}
|
||||
@@ -1,48 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Comparing Directives and Attribute Widgets
|
||||
@description
|
||||
|
||||
Although directives and {@link dev_guide.compiler.widgets attribute widgets} appear the same in a
|
||||
template (`ng:init` is a directive, `ng:repeat` is an attribute widget), there is a difference in
|
||||
the order in which they are evaluated. The user of existing directives or widgets cannot determine
|
||||
the order of evaluation. The evaluation order is the responsibility of the developer creating
|
||||
custom directives and widgets.
|
||||
|
||||
For example, consider this piece of HTML, which uses the `ng:repeat`, `ng:init`, and `ng:bind`
|
||||
widget and directives:
|
||||
|
||||
<pre>
|
||||
<ul ng:init="people=['mike', 'mary']">
|
||||
<li ng:repeat="person in people"
|
||||
ng:init="a=a+1"
|
||||
ng:bind="person">
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
Notice that the order of execution matters here. Because we want to run the `ng:init="a=a+1` and
|
||||
`ng:bind="person"` once for each `person in people`, we need to execute {@link
|
||||
api/angular.widget.@ng:repeat ng:repeat} to make copies of the `<li>` element before we run the
|
||||
{@link api/angular.directive.ng:init ng:init}, and {@link api/angular.directive.ng:bind ng:bind}
|
||||
for each of the `<li>`copies.
|
||||
|
||||
If you implemented `ng:repeat` as a directive, there would be no guarantee that the attributes
|
||||
`ng:repeat`, `ng:init`, and `ng:bind` would be evaluated in the order they are declared, because
|
||||
the order of element attributes in HTML is not significant to the browser.
|
||||
|
||||
So, when creating a custom HTML attribute, you will have to consider whether a directive or a
|
||||
widget is more appropriate. When the order of execution doesn't matter, directives are the right
|
||||
choice. In a situation where the order matters and one attribute should be processed with a higher
|
||||
priority than others, use a widget for the attribute that must be processed first.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
|
||||
## Related API:
|
||||
|
||||
* {@link api/angular.directive Directive API}
|
||||
* {@link api/angular.widget Widget API}
|
||||
@@ -1,97 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Extending the Angular Compiler
|
||||
@description
|
||||
|
||||
Let's say that we want to create a new DOM element called `<my:greeter/>` that displays a greeting.
|
||||
We want this HTML source:
|
||||
|
||||
<pre>
|
||||
<div ng:init="s='Hello'; n='World'">
|
||||
<my:greeter salutation="s" name="n"></my:greeter>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
to produce this DOM:
|
||||
|
||||
<pre>
|
||||
<div ng:init="s='Hello'; n='World'">
|
||||
<my:greeter salutation="s" name="n"/>
|
||||
<span class="salutation">Hello</span>
|
||||
<span class="name">World</span>!
|
||||
</my:greeter>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
That is, the new `<my:greeter></my:greeter>` tag's `salutation` and `name` attributes should be
|
||||
transformed by the compiler such that two `<span>` tags display the values of the attributes, with
|
||||
CSS classes applied to the output.
|
||||
|
||||
The following code snippet shows how to write a following widget definition that will be processed
|
||||
by the compiler. Note that you have to declare the {@link dev_guide.bootstrap namespace} `my` in
|
||||
the page:
|
||||
|
||||
<pre>
|
||||
angular.widget('my:greeter', function(compileElement){
|
||||
var compiler = this;
|
||||
compileElement.css('display', 'block');
|
||||
var salutationExp = compileElement.attr('salutation');
|
||||
var nameExp = compileElement.attr('name');
|
||||
return function(linkElement){
|
||||
var salutationSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutationSpan);
|
||||
linkElement.append(' ');
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append('!');
|
||||
this.$watch(salutationExp, function(value){
|
||||
salutationSpan.text(value);
|
||||
});
|
||||
this.$watch(nameExp, function(value){
|
||||
nameSpan.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
Note: For more about widgets, see {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
and the {@link api/angular.widget widget API reference page}.
|
||||
|
||||
# Compilation process for `<my:greeter>`
|
||||
|
||||
Here are the steps that the compiler takes in processing the page that contains the widget
|
||||
definition above:
|
||||
|
||||
## Compile Phase
|
||||
|
||||
1. Recursively traverse the DOM depth-first.
|
||||
2. Find the angular.widget definition.
|
||||
3. Find and execute the widget's compileElement function, which includes the following steps:
|
||||
1. Add a style element with attribute display: block; to the template DOM so that the browser
|
||||
knows to treat the element as block element for rendering. (Note: because this style element was
|
||||
added on the template compileElement, this style is automatically applied to any clones of the
|
||||
template (i.e. any repeating elements)).
|
||||
2. Extract the salutation and name HTML attributes as angular expressions.
|
||||
4. Return the aggregate link function, which includes just one link function in this example.
|
||||
|
||||
## Link Phase
|
||||
|
||||
1. Execute the aggregate link function, which includes the following steps:
|
||||
1. Create a <span> element set to the salutation class
|
||||
2. Create a <span> element set to the name class.
|
||||
2. Add the span elements to the linkElement. (Note: be careful not to add them to the
|
||||
compileElement, because that's the template.)
|
||||
3. Set up watches on the expressions. When an expression changes, copy the data to the
|
||||
corresponding spans.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
|
||||
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile angular.compile()}
|
||||
@@ -1,93 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Markup
|
||||
@description
|
||||
|
||||
Markup in angular is a feature that you can use in templates to transform the content of DOM
|
||||
elements prior to the compile phase (in which elements are compiled and link functions are
|
||||
returned. See the {@link dev_guide.compiler compiler docs} for details on how the compiler
|
||||
works.) The ability to make pre-compile changes to DOM elements lets you create shorthand for
|
||||
{@link api/angular.widget widget} and {@link api/angular.directive directive} declarations.
|
||||
|
||||
Angular provides one built-in markup feature: the double curly-braces used to declare binding
|
||||
points (between the model and view) for angular expressions. You can also create your own custom
|
||||
markup.
|
||||
|
||||
# Using Double Curly-brace Markup (`{{ }}`)
|
||||
|
||||
The double curly-brace (`{{ }}`) markup translates an enclosed expression into an {@link
|
||||
api/angular.directive.ng:bind ng:bind} directive:
|
||||
|
||||
<pre>
|
||||
{{expression}}
|
||||
</pre>
|
||||
|
||||
is transformed to:
|
||||
|
||||
<pre>
|
||||
<span ng:bind="expression"></span>
|
||||
</pre>
|
||||
|
||||
Markup is useful for the simple reason that `{{1+2}}` is easier to write and understand than `<span
|
||||
ng:bind="1+2"></span>`. After markup shorthand is expanded into the DOM elements it represents, the
|
||||
expanded elements are then {@link dev_guide.compiler compiled} normally.
|
||||
|
||||
|
||||
# Creating Custom Markup
|
||||
|
||||
Let's say you want to define markup that transforms `---` into a horizontal rule (`<hr/>`):
|
||||
|
||||
<pre>
|
||||
header
|
||||
---
|
||||
footer
|
||||
</pre>
|
||||
|
||||
should translate to:
|
||||
<pre>
|
||||
header
|
||||
<hr/>
|
||||
footer
|
||||
</pre>
|
||||
|
||||
Here is how you could extend the angular compiler to create the "---" markup:
|
||||
|
||||
<pre>
|
||||
angular.markup('---', function(text, textNode, parentElement) {
|
||||
var compiler = this;
|
||||
var index = text.indexOf('---');
|
||||
if (index > -1) {
|
||||
textNode.after(text.substring(index + 3));
|
||||
textNode.after(angular.element('<hr>'));
|
||||
textNode.after(text.substring(0, index));
|
||||
textNode.remove();
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
Unlike the way the compiler processes {@link api/angular.widget widgets} and {@link
|
||||
api/angular.directive directives} (matching the name of the handler function to a DOM element or
|
||||
attribute name), the compiler calls every markup handler for every text node, giving the handler a
|
||||
chance to transform the text. The markup handler needs to find all the matches in the text.
|
||||
|
||||
## Attribute Markup
|
||||
|
||||
Attribute markup extends the angular compiler in a very similar way to markup, except that it
|
||||
allows you to modify the state of attribute text rather then the content of a node.
|
||||
|
||||
<pre>
|
||||
angular.attrMarkup('extraClass', function(attrValue, attrName, element){
|
||||
if (attrName == 'additional-class') {
|
||||
element.addClass(attrValue);
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile Compiler API Reference}
|
||||
@@ -1,27 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler
|
||||
@description
|
||||
|
||||
The core of angular is its HTML compiler. The compiler processes angular directives, widgets, and
|
||||
markup to transform a static HTML page into a dynamic web application.
|
||||
|
||||
The default HTML transformations that the angular compiler provides are useful for building generic
|
||||
apps, but you can also extend the compiler to create a domain-specific language for building
|
||||
specific types of web applications.
|
||||
|
||||
All compilation takes place in the web browser; no server is involved.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
|
||||
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
|
||||
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
|
||||
* {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.markup Understanding Angular Markup}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile Angular Compiler API}
|
||||
@@ -1,18 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Testing a New DOM Element
|
||||
@description
|
||||
|
||||
|
||||
"Testing, testing, come in, over?"
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.understanding_compiler Understanding How the Compiler Works}
|
||||
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile angular.compile()}
|
||||
@@ -1,69 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding How the Compiler Works
|
||||
@description
|
||||
|
||||
Every {@link api/angular.widget widget}, {@link api/angular.directive directive} and {@link
|
||||
dev_guide.compiler.markup markup} is defined with a compile function, which the angular compiler
|
||||
executes on each widget or directive it encounters. The compile function optionally returns a link
|
||||
function. This compilation process happens automatically when the page is loaded when you specify
|
||||
`ng:autobind` in the script tag from which you load the angular script file. (See {@link
|
||||
dev_guide.bootstrap Initializing Angular}.)
|
||||
|
||||
The compile and link functions are related as follows:
|
||||
|
||||
* **compile function** — Registers a listener for the widget, directive, or markup expression. The
|
||||
compiler calls this function exactly once.
|
||||
* **link function** — Sets up the listener registered by the compile function. This function can be
|
||||
called multiple times, once per cloned DOM element. For example, in the case of the {@link
|
||||
api/angular.widget.@ng:repeat repeater widget} used in a list element (`<li ng:repeat="[item in
|
||||
dataset]"`), the link function gets called to set up a listener on each element in the list.
|
||||
|
||||
Note that angular's built-in widgets, directives, and markup have predefined compile and link
|
||||
functions that you don't need to modify. When you create your own widgets, directives, or markup,
|
||||
you must write compile and link functions for them. Refer to the {@link api/angular.compile
|
||||
Compiler API} for details.
|
||||
|
||||
When the angular compiler compiles a page, it proceeds through 3 phases: Compile, Create Root
|
||||
Scope, and Link:
|
||||
|
||||
1. Compile Phase
|
||||
|
||||
1. Recursively traverse the DOM, depth-first.
|
||||
2. Look for a matching compile function of type widget, then markup, then directive.
|
||||
3. If a compile function is found then execute it.
|
||||
4. When the compile function completes, it should return a link function. Aggregate this link
|
||||
function with all link functions returned previously by step 3.
|
||||
5. Repeat steps 3 and 4 for all compile functions found.
|
||||
|
||||
The result of the compilation phase is an aggregate link function, which comprises all of the
|
||||
individual link functions.
|
||||
|
||||
2. Create Root Scope Phase
|
||||
|
||||
* Inject all services into the root scope.
|
||||
|
||||
3. Link Phase
|
||||
|
||||
1. Execute the aggregate link function with the root scope. The aggregate link function calls
|
||||
all of the individual link functions that were generated in the compile phase.
|
||||
2. If there are any clones of the DOM caused by repeating elements, call the link function
|
||||
multiple times, one for each repeating item.
|
||||
|
||||
Note that while the compile function is executed exactly once, the link function can be executed
|
||||
multiple times, for example, once for each iteration in a repeater.
|
||||
|
||||
The angular compiler exposes methods that you will need to make use of when writing your own
|
||||
widgets and directives. For information on these methods, see the {@link api/angular.compile
|
||||
Compiler API doc}.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.extending_compiler Extending the Angular Compiler}
|
||||
* {@link dev_guide.compiler.testing_dom_element Testing a New DOM Element}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile angular.compile()}
|
||||
@@ -1,96 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Widgets: Creating Custom Widgets
|
||||
@description
|
||||
|
||||
When you create your own widgets, you must set up your own namespace for them. (See
|
||||
dev_guide.bootstrap Initializing Angular} for information about namespaces in angular.)
|
||||
|
||||
Let's say we would like to create a new element type in the namespace `my` that can watch an
|
||||
expression and `alert()` the user with each new value:
|
||||
|
||||
<pre>
|
||||
// An element widget
|
||||
<my:watch exp="name"></my:watch>
|
||||
</pre>
|
||||
|
||||
You can implement `my:watch` like this:
|
||||
|
||||
<pre>
|
||||
angular.widget('my:watch', function(compileElement) {
|
||||
var compiler = this;
|
||||
var exp = compileElement.attr('exp');
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(exp, function(value){
|
||||
alert(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
# Creating a Custom Attribute Widget
|
||||
|
||||
Let's implement the same widget as in the example in Defining an Element Widget, but this time as
|
||||
an attribute that can be added to any existing DOM element:
|
||||
|
||||
<pre>
|
||||
// An attribute widget (my:watch) in a div tag
|
||||
<div my:watch="name">text</div>
|
||||
</pre>
|
||||
You can implement `my:watch` attribute like this:
|
||||
<pre>
|
||||
angular.widget('@my:watch', function(expression, compileElement) {
|
||||
var compiler = this;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value) {
|
||||
alert(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
# Live Example of a Custom Element Widget
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.widget('my:time', function(compileElement){
|
||||
compileElement.css('display', 'block');
|
||||
return function(linkElement){
|
||||
function update(){
|
||||
linkElement.text('Current time is: ' + new Date());
|
||||
setTimeout(update, 1000);
|
||||
}
|
||||
update();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<my:time></my:time>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Additional Compiler Methods for Custom Widgets
|
||||
|
||||
The angular compiler exposes methods that you may need to use of when writing your own widgets and
|
||||
directives. For example, the `descend()` method lets you control whether the compiler ignores or
|
||||
processes child elements of the element it is compiling. For information on this and other
|
||||
compiler methods, see the {@link api/angular.compile Compiler API doc}.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.directives Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets Angular Widgets}
|
||||
* {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile Compiler API}
|
||||
@@ -1,36 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Widgets
|
||||
@description
|
||||
|
||||
Widgets are DOM elements that the browser doesn't already understand. Angular provides some
|
||||
built-in widgets (such as {@link api/angular.widget.@ng:repeat ng:repeat}), and you can create your
|
||||
own custom widgets.
|
||||
|
||||
Widgets are intended to manipulate the DOM tree by adding new elements (unlike {@link
|
||||
dev_guide.compiler.directives angular directives}, which are intended to modify only element
|
||||
properties).
|
||||
|
||||
Widgets come in two types:
|
||||
|
||||
* Element Widget — A custom DOM element. An example of a custom element is shown in {@link
|
||||
dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}.
|
||||
|
||||
* Attribute Widget — A custom attribute on an existing DOM element. An attribute widget is similar
|
||||
to an angular directive, with the main difference being that an attribute widget will always be
|
||||
processed before any directives that are specified on the same element. Only one attribute widget
|
||||
is allowed per element. An example of an attribute widget is shown in {@link
|
||||
dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}.
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.compiler.directives Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets.creating_widgets Creating Custom Widgets}
|
||||
* {@link dev_guide.compiler.directives.creating_directives Creating Custom Directives}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile Compiler API}
|
||||
@@ -1,33 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About Dependency Injection (DI)
|
||||
@description
|
||||
|
||||
Dependency Injection (DI) is an object-oriented software design pattern that supports the
|
||||
decoupling and dependency management of application components.
|
||||
|
||||
The idea behind DI is to decouple each component from all of the other components that it depends
|
||||
on to do its particular job. The way this is done in DI is by moving the responsibility for
|
||||
managing dependencies out of each individual component and into a provider component. The provider
|
||||
(or injector) component manages the life cycles and dependencies for all of the other components in
|
||||
an application.
|
||||
|
||||
Angular has a built-in dependency management subsystem that helps to make your applications easier
|
||||
to develop, understand, and test.
|
||||
|
||||
For more information on DI in general, see {@link http://en.wikipedia.org/wiki/Dependency_injection
|
||||
Dependency Injection} at Wikipedia, and {@link http://martinfowler.com/articles/injection.html
|
||||
Inversion of Control} by Martin Fowler, or read about DI in your favorite software design pattern
|
||||
book.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.di.understanding_di Understanding DI in Angular}
|
||||
* {@link dev_guide.services Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Service API}
|
||||
* {@link api/angular.injector Angular Injector API}
|
||||
@@ -1,106 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: DI: Understanding DI in Angular
|
||||
@description
|
||||
|
||||
|
||||
While DI is widely used in statically typed languages such as Java or C++, it has not been widely
|
||||
used in JavaScript. Angular brings the benefits of DI into JavaScript apps.
|
||||
|
||||
In angular, DI is implemented as a subsystem that manages dependencies between services,
|
||||
controllers, widgets, and filters. The most important of these are {@link api/angular.service
|
||||
services}.
|
||||
|
||||
Services are objects that handle common tasks in web applications. Angular provides several{@link
|
||||
api/angular.service built-in services}, and you can create your own custom services.
|
||||
|
||||
The main job of angular's DI subsystem is to provide services to angular components that depend on
|
||||
them. The way the DI subsystem provides services is as follows: all services are registered with
|
||||
angular's {@link api/angular.service service API}, and all components that depend on services
|
||||
define those dependencies as a property (`$inject`). With this information, the DI subsystem
|
||||
manages the creation of service objects and the provision of those objects to the components that
|
||||
need them, at the time they need them. The following illustration steps through the sequence of
|
||||
events:
|
||||
|
||||
<img src="img/guide/di_sequence_final.png">
|
||||
|
||||
In the illustration above, the dependency injection sequence proceeds as follows:
|
||||
|
||||
1. Service factory functions are registered with angular's service factory repository.
|
||||
2. `ng:autobind` triggers angular's bootstrap sequence, during which angular compiles the template,
|
||||
creates the root scope, and creates the dependency injector.
|
||||
3. The `ng:controller` directive implicitly creates a new child scope, augmented by the application
|
||||
of the `PhoneListCtrl` controller function.
|
||||
4. The Injector identifies the `$xhr` service as `PhoneListCtrl` controller's only dependency.
|
||||
5. The Injector checks if the `$xhr` service has already been instantiated, and if not uses the
|
||||
factory function from the service factory repository to construct it.
|
||||
6. DI provides the instance of $xhr service to the PhoneListCtrl controller constructor
|
||||
|
||||
|
||||
## How Scope Relates to DI
|
||||
|
||||
The {@link api/angular.injector injector} is responsible for resolving the service dependencies in
|
||||
the application. It gets created and configured with the creation of a root scope. The injector
|
||||
caches instances of services, with the services cache bound to the root scope.
|
||||
|
||||
Different root scopes have different instances of the injector. While typical angular applications
|
||||
will only have one root scope (and hence the services will act like application singletons), in
|
||||
tests it is important to not share singletons across test invocations for isolation reasons. We
|
||||
achieve the necessary isolation by having each test create its own separate root scope.
|
||||
|
||||
<pre>
|
||||
// create a root scope
|
||||
var rootScope = angular.scope();
|
||||
// access the service locator
|
||||
var myService = rootScope.$service('myService');
|
||||
</pre>
|
||||
|
||||
## Inferring dependencies from the signature of the factory function or constructor
|
||||
|
||||
**EXPERIMENTAL FEATURE**: This is an experimental feature. See the important note at the end of
|
||||
this section for drawbacks.
|
||||
|
||||
We resort to `$inject` and our own annotation because there is no way in JavaScript to get a list
|
||||
of arguments. Or is there? It turns out that calling `.toString()` on a function returns the
|
||||
function declaration along with the argument names as shown below:
|
||||
|
||||
<pre>
|
||||
function myFn(a,b){}
|
||||
expect(myFn.toString()).toEqual('function myFn(a,b){}');
|
||||
</pre>
|
||||
|
||||
This means that angular can infer the function names after all and use that information to generate
|
||||
the `$inject` annotation automatically. Therefore the following two function definitions are
|
||||
equivalent:
|
||||
|
||||
<pre>
|
||||
// given a user defined service
|
||||
angular.service('serviceA', ...);
|
||||
|
||||
// inject '$window', 'serviceA', curry 'name';
|
||||
function fnA($window, serviceA, name){};
|
||||
fnA.$inject = ['$window', 'serviceA'];
|
||||
|
||||
// inject '$window', 'serviceA', curry 'name';
|
||||
function fnB($window, serviceA_, name){};
|
||||
// implies: fnB.$inject = ['$window', 'serviceA'];
|
||||
</pre>
|
||||
|
||||
If angular does not find a `$inject` annotation on the function, then it calls the `.toString()`
|
||||
method and tries to infer what should be injected by using function argument names as dependency
|
||||
identifiers.
|
||||
|
||||
**IMPORTANT**
|
||||
Minifiers/obfuscators change the names of function arguments and will therefore break the `$inject`
|
||||
inference. For this reason, either explicitly declare the `$inject` or do not use
|
||||
minifiers/obfuscators. In the future, we may provide a pre-processor which will scan the source
|
||||
code and insert the `$inject` into the source code so that it can be minified/obfuscated.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.services Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Services API}
|
||||
@@ -1,54 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: DI: Using DI in Controllers
|
||||
@description
|
||||
|
||||
The most common place to use dependency injection in angular applications is in {@link
|
||||
dev_guide.mvc.understanding_controller controllers}. Here is a simple example:
|
||||
|
||||
<pre>
|
||||
function MyController($route){
|
||||
// configure the route service
|
||||
$route.when(...);
|
||||
}
|
||||
MyController.$inject = ['$route'];
|
||||
</pre>
|
||||
|
||||
In this example, the `MyController` constructor function takes one argument, the {@link
|
||||
api/angular.service.$route $route} service. Angular is then responsible for supplying the instance
|
||||
of `$route` to the controller when the constructor is instantiated. There are two ways to cause
|
||||
controller instantiation – by configuring routes with the `$route` service, or by referencing the
|
||||
controller from the HTML template, as follows:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng:controller="MyController">
|
||||
<script src="http://code.angularjs.org/angular.min.js" ng:autobind></script>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
When angular is instantiating your controller, it needs to know what services, if any, should be
|
||||
injected (passed in as arguments) into the controller. Since there is no reflection in JavaScript,
|
||||
we have to supply this information to angular in the form of an additional property on the
|
||||
controller constructor function called `$inject`. Think of it as annotations for JavaScript.
|
||||
|
||||
<pre>
|
||||
MyController.$inject = ['$route'];
|
||||
</pre>
|
||||
|
||||
The information in `$inject` is then used by the {@link api/angular.injector injector} to call the
|
||||
function with the correct arguments.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.di About Dependency Injection}
|
||||
* {@link dev_guide.di.understanding_di Understanding Dependency Injection in Angular}
|
||||
* {@link dev_guide.services Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.injector Angular Injector API}
|
||||
@@ -0,0 +1,178 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: E2E Testing
|
||||
@description
|
||||
|
||||
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
|
||||
verify the correctness of new features, catch bugs and notice regressions.
|
||||
|
||||
To solve this problem, we have built an Angular Scenario Runner which simulates user interactions
|
||||
that will help you verify the health of your Angular application.
|
||||
|
||||
# Overview
|
||||
You will write scenario tests in JavaScript, which describe how your application should behave,
|
||||
given a certain interaction in a specific state. A scenario is comprised of one or more it blocks
|
||||
(you can think of these as the requirements of your application), which in turn are made of
|
||||
**commands** and **expectations**. Commands tell the Runner to do something with the application
|
||||
(such as navigate to a page or click on a button), and expectations tell the Runner to assert
|
||||
something about the state (such as the value of a field or the current URL). If any expectation
|
||||
fails, the runner marks the `it` as "failed" and continues on to the next one. Scenarios may also
|
||||
have **beforeEach** and **afterEach** blocks, which will be run before (or after) each `it` block,
|
||||
regardless of whether they pass or fail.
|
||||
|
||||
<img src="img/guide/scenario_runner.png">
|
||||
|
||||
In addition to the above elements, scenarios may also contain helper functions to avoid duplicating
|
||||
code in the `it` blocks.
|
||||
|
||||
Here is an example of a simple scenario:
|
||||
<pre>
|
||||
describe('Buzz Client', function() {
|
||||
it('should filter results', function() {
|
||||
input('user').enter('jacksparrow');
|
||||
element(':button').click();
|
||||
expect(repeater('ul li').count()).toEqual(10);
|
||||
input('filterText').enter('Bees');
|
||||
expect(repeater('ul li').count()).toEqual(1);
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
|
||||
filter the stream of the user. It starts by entering a value in the 'user' input field, clicking
|
||||
the only button on the page, and then it verifies that there are 10 items listed. It then enters
|
||||
'Bees' in the 'filterText' input field and verifies that the list is reduced to a single item.
|
||||
|
||||
The API section below lists the available commands and expectations for the Runner.
|
||||
|
||||
# API
|
||||
Source: {@link https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js}
|
||||
|
||||
## pause()
|
||||
Pauses the execution of the tests until you call `resume()` in the console (or click the resume
|
||||
link in the Runner UI).
|
||||
|
||||
## sleep(seconds)
|
||||
Pauses the execution of the tests for the specified number of `seconds`.
|
||||
|
||||
## browser().navigateTo(url)
|
||||
Loads the `url` into the test frame.
|
||||
|
||||
## browser().navigateTo(url, fn)
|
||||
Loads the URL returned by `fn` into the testing frame. The given `url` is only used for the test
|
||||
output. Use this when the destination URL is dynamic (that is, the destination is unknown when you
|
||||
write the test).
|
||||
|
||||
## browser().reload()
|
||||
Refreshes the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().href()
|
||||
Returns the window.location.href of the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().path()
|
||||
Returns the window.location.pathname of the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().search()
|
||||
Returns the window.location.search of the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().hash()
|
||||
Returns the window.location.hash (without `#`) of the currently loaded page in the test frame.
|
||||
|
||||
## browser().location().url()
|
||||
Returns the {@link api/ng.$location $location.url()} of the currently loaded page in
|
||||
the test frame.
|
||||
|
||||
## browser().location().path()
|
||||
Returns the {@link api/ng.$location $location.path()} of the currently loaded page in
|
||||
the test frame.
|
||||
|
||||
## browser().location().search()
|
||||
Returns the {@link api/ng.$location $location.search()} of the currently loaded page
|
||||
in the test frame.
|
||||
|
||||
## browser().location().hash()
|
||||
Returns the {@link api/ng.$location $location.hash()} of the currently loaded page in
|
||||
the test frame.
|
||||
|
||||
## expect(future).{matcher}
|
||||
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
|
||||
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
|
||||
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
|
||||
`expect(browser().location().href()).toEqual('http://www.google.com')`
|
||||
|
||||
## expect(future).not().{matcher}
|
||||
Asserts the value of the given `future` satisfies the negation of the `matcher`.
|
||||
|
||||
## using(selector, label)
|
||||
Scopes the next DSL element selection.
|
||||
|
||||
## binding(name)
|
||||
Returns the value of the first binding matching the given `name`.
|
||||
|
||||
## input(name).enter(value)
|
||||
Enters the given `value` in the text field with the given `name`.
|
||||
|
||||
## input(name).check()
|
||||
Checks/unchecks the checkbox with the given `name`.
|
||||
|
||||
## input(name).select(value)
|
||||
Selects the given `value` in the radio button with the given `name`.
|
||||
|
||||
## input(name).val()
|
||||
Returns the current value of an input field with the given `name`.
|
||||
|
||||
## repeater(selector, label).count()
|
||||
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
|
||||
used for test ouput.
|
||||
|
||||
## repeater(selector, label).row(index)
|
||||
Returns an array with the bindings in the row at the given `index` in the repeater matching the
|
||||
given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## repeater(selector, label).column(binding)
|
||||
Returns an array with the values in the column with the given `binding` in the repeater matching
|
||||
the given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## select(name).option(value)
|
||||
Picks the option with the given `value` on the select with the given `name`.
|
||||
|
||||
## select(name).option(value1, value2...)
|
||||
Picks the options with the given `values` on the multi select with the given `name`.
|
||||
|
||||
## element(selector, label).count()
|
||||
Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
|
||||
output.
|
||||
|
||||
## element(selector, label).click()
|
||||
Clicks on the element matching the given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).query(fn)
|
||||
Executes the function `fn(selectedElements, done)`, where selectedElements are the elements that
|
||||
match the given jQuery `selector` and `done` is a function that is called at the end of the `fn`
|
||||
function. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}()
|
||||
Returns the result of calling `method` on the element matching the given jQuery `selector`, where
|
||||
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
|
||||
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
|
||||
`scrollTop`, `offset`. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}(value)
|
||||
Executes the `method` passing in `value` on the element matching the given jQuery `selector`, where
|
||||
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
|
||||
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
|
||||
`scrollTop`, `offset`. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}(key)
|
||||
Returns the result of calling `method` passing in `key` on the element matching the given jQuery
|
||||
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
|
||||
`label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}(key, value)
|
||||
Executes the `method` passing in `key` and `value` on the element matching the given jQuery
|
||||
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
|
||||
`label` is used for test output.
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
angular which makes testing your angular applications easy. So there is no excuse for not do it.
|
||||
@@ -1,221 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Understanding Angular Expressions
|
||||
@description
|
||||
|
||||
Expressions are {@link dev_guide.templates.databinding bindings} that you write in HTML and embed
|
||||
in templates in order to create views in angular. Angular expressions are similar but not
|
||||
equivalent to JavaScript expressions.
|
||||
|
||||
For example, these are all valid expressions in angular:
|
||||
|
||||
* `1+2={{1+2}}`
|
||||
* `3*10|currency`
|
||||
* `Hello {{name}}!`
|
||||
* `Hello {{'World'}}!`
|
||||
|
||||
|
||||
## Angular Expressions vs. JS Expressions
|
||||
|
||||
It might be tempting to think of angular view expressions as JavaScript expressions, but that is
|
||||
not entirely correct. Angular does not use a simple JavaScript eval of the expression text. You can
|
||||
think of angular expressions as JavaScript expressions with these differences:
|
||||
|
||||
* **Attribute Evaluation:** evaluation of all attributes are against the current scope, not to the
|
||||
global window as in JavaScript.
|
||||
* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript.
|
||||
* **No Control Flow Statements:** you cannot do the following from an angular expression:
|
||||
conditionals, loops, or throw.
|
||||
* **Type Augmentation:** the scope expression evaluator augments built-in types.
|
||||
* **Filters:** you can add filters to an expression, for example to convert raw data into a
|
||||
human-readable format.
|
||||
* **The $:** angular reserves this prefix to differentiate its API names from others.
|
||||
|
||||
If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a
|
||||
controller method and call that. If you want to `eval()` an angular expression from JavaScript, use
|
||||
the `Scope:$eval()` method.
|
||||
|
||||
## Example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
1+2={{1+2}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function(){
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
You can try evaluating different expressions here:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng:init="exprs=[]" class="expressions">
|
||||
Expression:
|
||||
<input type='text' name="expr" value="3*10|currency" size="80"/>
|
||||
<button ng:click="exprs.$add(expr)">Evaluate</button>
|
||||
<ul>
|
||||
<li ng:repeat="expr in exprs">
|
||||
[ <a href="" ng:click="exprs.$remove(expr)">X</a> ]
|
||||
<tt>{{expr}}</tt> => <span ng:bind="$parent.$eval(expr)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should allow user expression testing', function(){
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Attribute Evaluation
|
||||
|
||||
Evaluation of all attributes takes place against the current scope. Unlike JavaScript, where names
|
||||
default to global window properties, angular expressions have to use `$window` to refer to the
|
||||
global object. For example, if you want to call `alert()`, which is defined on `window`, an
|
||||
expression must use `$window.alert()`. This is done intentionally to prevent accidental access to
|
||||
the global state (a common source of subtle bugs).
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div class="example2" ng:init="$window = $service('$window')">
|
||||
Name: <input name="name" type="text" value="World"/>
|
||||
<button ng:click="($window.mockWindow || $window).alert('Hello ' + name)">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function(){
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
alert: function(text){ alertText = text; }
|
||||
};
|
||||
done();
|
||||
});
|
||||
element(':button:contains(Greet)').click();
|
||||
expect(this.addFuture('alert text', function(done) {
|
||||
done(null, alertText);
|
||||
})).toBe('Hello World');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
## Forgiving
|
||||
|
||||
Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating `a.b.c` throws
|
||||
an exception if `a` is not an object. While this makes sense for a general purpose language, the
|
||||
expression evaluations are primarily used for data binding, which often look like this:
|
||||
|
||||
{{a.b.c}}
|
||||
|
||||
It makes more sense to show nothing than to throw an exception if `a` is undefined (perhaps we are
|
||||
waiting for the server response, and it will become defined soon). If expression evaluation wasn't
|
||||
forgiving we'd have to write bindings that clutter the code, for example: `{{((a||{}).b||{}).c}}`
|
||||
|
||||
Similarly, invoking a function `a.b.c()` on undefined or null simply returns undefined.
|
||||
|
||||
Assignments work the same way in reverse:
|
||||
|
||||
a.b.c = 10
|
||||
|
||||
...creates the intermediary objects even if a is undefined.
|
||||
|
||||
|
||||
## No Control Flow Statements
|
||||
|
||||
You cannot write a control flow statement in an expression. The reason behind this is core to the
|
||||
angular philosophy that application logic should be in controllers, not in the view. If you need a
|
||||
conditional (including ternary operators), loop, or to throw from a view expression, delegate to a
|
||||
JavaScript method instead.
|
||||
|
||||
|
||||
## Type Augmentation
|
||||
|
||||
Built-in types have methods like `[].push()`, but the richness of these methods is limited.
|
||||
Consider the example below, which allows you to do a simple search over a canned set of contacts.
|
||||
The example would be much more complicated if we did not have the `Array:$filter()`. There is no
|
||||
built-in method on `Array` called {@link api/angular.Array.filter $filter} and angular doesn't add
|
||||
it to `Array.prototype` because that could collide with other JavaScript frameworks.
|
||||
|
||||
For this reason the scope expression evaluator augments the built-in types to make them act like
|
||||
they have extra methods. The actual method for `$filter()` is `angular.Array.filter()`. You can
|
||||
call it from JavaScript.
|
||||
|
||||
Extensions: You can further extend the expression vocabulary by adding new methods to
|
||||
`angular.Array` or `angular.String`, etc.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng:init="friends = [
|
||||
{name:'John', phone:'555-1212'},
|
||||
{name:'Mary', phone:'555-9876'},
|
||||
{name:'Mike', phone:'555-4321'},
|
||||
{name:'Adam', phone:'555-5678'},
|
||||
{name:'Julie', phone:'555-8765'}]"></div>
|
||||
Search: <input name="searchText"/>
|
||||
<table class="example3">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng:repeat="friend in friends.$filter(searchText)">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should filter the list', function(){
|
||||
var tr = using('table.example3').repeater('tr.ng-attr-widget');
|
||||
expect(tr.count()).toBe(5);
|
||||
input('searchText').enter('a');
|
||||
expect(tr.count()).toBe(2);
|
||||
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
## Filters
|
||||
|
||||
When presenting data to the user, you might need to convert the data from its raw format to a
|
||||
user-friendly format. For example, you might have a data object that needs to be formatted
|
||||
according to the locale before displaying it to the user. You can pass expressions through a chain
|
||||
of filters like this:
|
||||
|
||||
name | uppercase
|
||||
|
||||
The expression evaluator simply passes the value of name to angular.filter.uppercase.
|
||||
|
||||
Chain filters using this syntax:
|
||||
|
||||
value | filter1 | filter2
|
||||
|
||||
You can also pass colon-delimited arguments to filters, for example, to display the number 123 with
|
||||
2 decimal points:
|
||||
|
||||
123 | number:2
|
||||
|
||||
# The $
|
||||
|
||||
You might be wondering, what is the significance of the $ prefix? It is simply a prefix that
|
||||
angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating
|
||||
`a.length()` would return undefined because neither a nor angular define such a property.
|
||||
|
||||
Consider that in a future version of angular we might choose to add a length method, in which case
|
||||
the behavior of the expression would change. Worse yet, you the developer could create a length
|
||||
property and then we would have a collision. This problem exists because angular augments existing
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
so that angular developers and developers who use angular can develop in harmony without collisions.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler.markup Understanding Angular Markup}
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.compile Angular Compiler API}
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular
|
||||
@description
|
||||
@@ -13,7 +12,7 @@ The MVC pattern greatly summarized:
|
||||
* Separate applications into distinct presentation, data, and logic components
|
||||
* Encourage loose coupling between these components
|
||||
|
||||
Along with {@link dev_guide.services services} and {@link dev_guide.di dependency injection}, MVC
|
||||
Along with {@link dev_guide.services services} and {@link di dependency injection}, MVC
|
||||
makes angular applications better structured, easier to maintain and more testable.
|
||||
|
||||
The following topics explain how angular incorporates the MVC pattern into the angular way of
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the Controller Component
|
||||
@description
|
||||
|
||||
In angular, a controller is a JavaScript function (type/class) that is used to augment instances of
|
||||
angular {@link dev_guide.scopes Scope}, excluding the root scope. When you or angular create a new
|
||||
child scope object via the {@link api/angular.scope.$new scope.$new} API , there is an
|
||||
In angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular {@link scope Scope}, excluding the root scope. When you or angular create a new
|
||||
child scope object via the {@link api/ng.$rootScope.Scope#$new scope.$new} API , there is an
|
||||
option to pass in a controller as a method argument. This will tell angular to associate the
|
||||
controller with the new scope and to augment its behavior.
|
||||
|
||||
@@ -25,16 +24,12 @@ constructor). Constructors are always applied to an existing scope object.
|
||||
|
||||
You set up the initial state of a scope by creating model properties. For example:
|
||||
|
||||
function GreetingCtrl() {
|
||||
this.greeting = 'Hola!';
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
|
||||
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
|
||||
|
||||
When a controller function is applied to an angular scope object, the `this` of the controller
|
||||
function becomes the scope of the angular scope object, so any assignment to `this` within the
|
||||
controller function happens on the angular scope object.
|
||||
|
||||
# Adding Behavior to a Scope Object
|
||||
|
||||
Behavior on an angular scope object is in the form of scope method properties available to the
|
||||
@@ -42,13 +37,8 @@ template/view. This behavior interacts with and modifies the application model.
|
||||
|
||||
As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any
|
||||
objects (or primitives) assigned to the scope become model properties. Any functions assigned to
|
||||
the scope, along with any prototype methods of the controller type, become functions available in
|
||||
the template/view, and can be invoked via angular expressions and `ng:` event handlers (e.g. {@link
|
||||
api/angular.directive.ng:click ng:click}). These controller methods are always evaluated within the
|
||||
context of the angular scope object that the controller function was applied to (which means that
|
||||
the `this` keyword of any controller method is always bound to the scope that the controller
|
||||
augments). This is how the second task of adding behavior to the scope is accomplished.
|
||||
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and `ng` event handler directives (e.g. {@link api/ng.directive:ngClick ngClick}).
|
||||
|
||||
# Using Controllers Correctly
|
||||
|
||||
@@ -57,7 +47,7 @@ needed for a single view.
|
||||
|
||||
The most common way to keep controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in controllers via dependency injection.
|
||||
This is discussed in the {@link dev_guide.di Dependency Injection} {@link dev_guide.services
|
||||
This is discussed in the {@link di Dependency Injection} {@link dev_guide.services
|
||||
Services} sections of this guide.
|
||||
|
||||
Do not use controllers for:
|
||||
@@ -66,9 +56,9 @@ Do not use controllers for:
|
||||
manipulation—the presentation logic of an application—is well known for being hard to test.
|
||||
Putting any presentation logic into controllers significantly affects testability of the business
|
||||
logic. Angular offers {@link dev_guide.templates.databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in {@link
|
||||
dev_guide.compiler.widgets widgets} and {@link dev_guide.compiler.directives directives}.
|
||||
- Input formatting — Use {@link dev_guide.templates.formatters angular formatters} instead.
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
|
||||
{@link guide/directive directives}.
|
||||
- Input formatting — Use {@link forms angular form controls} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- Run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
@@ -78,9 +68,9 @@ instances).
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
You can associate controllers with scope objects explicitly via the {@link api/angular.scope.$new
|
||||
scope.$new} api or implicitly via the {@link api/angular.directive.ng:controller ng:controller
|
||||
directive} or {@link api/angular.service.$route $route service}.
|
||||
You can associate controllers with scope objects explicitly via the {@link api/ng.$rootScope.Scope#$new
|
||||
scope.$new} api or implicitly via the {@link api/ng.directive:ngController ngController
|
||||
directive} or {@link api/ng.$route $route service}.
|
||||
|
||||
|
||||
## Controller Constructor and Methods Example
|
||||
@@ -100,37 +90,43 @@ string "very". Depending on which button is clicked, the `spice` model is set to
|
||||
## A Spicy Controller Example
|
||||
|
||||
<pre>
|
||||
<body ng:controller="SpicyCtrl">
|
||||
<button ng:click="chiliSpicy()">Chili</button>
|
||||
<button ng:click="jalapenoSpicy()">Jalapeño</button>
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl() {
|
||||
this.spice = 'very';
|
||||
this.chiliSpicy = function() {
|
||||
this.spice = 'chili';
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.chiliSpicy = function() {
|
||||
$scope.spice = 'chili';
|
||||
}
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
}
|
||||
}
|
||||
|
||||
SpicyCtrl.prototype.jalapenoSpicy = function() {
|
||||
this.spice = 'jalapeño';
|
||||
}
|
||||
|
||||
</pre>
|
||||
|
||||
Things to notice in the example above:
|
||||
|
||||
- The `ng:controller` directive is used to (implicitly) create a scope for our template, and the
|
||||
- The `ngController` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` controller.
|
||||
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
- The JavaScript keyword `this` in the `SpicyCtrl` function is bound to the scope that the
|
||||
controller augments.
|
||||
- Assigning a property to `this` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method) or
|
||||
as prototype methods of the controller constructor function (the `jalapenoSpicy` method)
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method)
|
||||
- Both controller methods are available in the template (for the `body` element and and its
|
||||
children).
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) allowed you to use `this` interchangeably with
|
||||
the $scope method, but this is no longer the case. Inside of methods defined on the scope
|
||||
`this` and $scope are interchangeable (angular sets `this` to $scope), but not otherwise
|
||||
inside your controller constructor.
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) added prototype methods into the scope
|
||||
automatically, but this is no longer the case; all methods need to be added manually to
|
||||
the scope.
|
||||
|
||||
|
||||
Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.
|
||||
@@ -138,17 +134,17 @@ previous example.
|
||||
## Controller Method Arguments Example
|
||||
|
||||
<pre>
|
||||
<body ng:controller="SpicyCtrl">
|
||||
<input name="customSpice" value="wasabi">
|
||||
<button ng:click="spicy('chili')">Chili</button>
|
||||
<button ng:click="spicy(customSpice)">Custom spice</button>
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<input ng-model="customSpice" value="wasabi">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl() {
|
||||
this.spice = 'very';
|
||||
this.spicy = function(spice) {
|
||||
this.spice = spice;
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.spicy = function(spice) {
|
||||
$scope.spice = spice;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
@@ -161,33 +157,33 @@ input box) in the second button.
|
||||
|
||||
## Controller Inheritance Example
|
||||
|
||||
Controller inheritance in angular is based on {@link api/angular.scope Scope} inheritance. Let's
|
||||
Controller inheritance in angular is based on {@link api/ng.$rootScope.Scope Scope} inheritance. Let's
|
||||
have a look at an example:
|
||||
|
||||
<pre>
|
||||
<body ng:controller="MainCtrl">
|
||||
<body ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<div ng:controller="ChildCtrl">
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng:controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</body>
|
||||
|
||||
function MainCtrl() {
|
||||
this.timeOfDay = 'morning';
|
||||
this.name = 'Nikki';
|
||||
function MainCtrl($scope) {
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}
|
||||
|
||||
function ChildCtrl() {
|
||||
this.name = 'Mattie';
|
||||
function ChildCtrl($scope) {
|
||||
$scope.name = 'Mattie';
|
||||
}
|
||||
|
||||
function BabyCtrl() {
|
||||
this.timeOfDay = 'evening';
|
||||
this.name = 'Gingerbreak Baby';
|
||||
function BabyCtrl($scope) {
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}
|
||||
</pre>
|
||||
|
||||
Notice how we nested three `ng:controller` directives in our template. This template construct will
|
||||
Notice how we nested three `ngController` directives in our template. This template construct will
|
||||
result in 4 scopes being created for our view:
|
||||
|
||||
- The root scope
|
||||
@@ -207,19 +203,17 @@ are applied to the scope object.
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
The way to test a controller depends upon how complicated the controller is.
|
||||
|
||||
- If your controller doesn't use DI or scope methods — create the controller with the `new`
|
||||
operator and test away. For example:
|
||||
Although there are many ways to test a controller, one of the best conventions, shown below,
|
||||
involves injecting the `$rootScope` and `$controller`
|
||||
|
||||
Controller Function:
|
||||
<pre>
|
||||
function myController() {
|
||||
this.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
function myController($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
|
||||
this.spice = "habanero";
|
||||
$scope.spice = "habanero";
|
||||
}
|
||||
</pre>
|
||||
|
||||
@@ -227,28 +221,52 @@ Controller Test:
|
||||
<pre>
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function(){
|
||||
var ctrl;
|
||||
describe('myController', function() {
|
||||
var scope;
|
||||
|
||||
beforeEach(function() {
|
||||
ctrl = new myController();
|
||||
});
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
var ctrl = $controller(myController, {$scope: scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(ctrl.spices.length).toBe(3);
|
||||
expect(scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(ctrl.spice).toBe('habanero');
|
||||
expect(scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
- If your controller does use DI or scope methods — create a root scope, then create the controller
|
||||
in the root scope with `scope.$new(MyController)`. Test the controller using `$eval`, if necessary.
|
||||
- If you need to test a nested controller that depends on its parent's state — create a root scope,
|
||||
create a parent scope, create a child scope, and test the controller using $eval if necessary.
|
||||
|
||||
If you need to test a nested controller one needs to create the same scope hierarchy
|
||||
in your test as exist in the DOM.
|
||||
|
||||
<pre>
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, babyScope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = childCtrl.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
expect(mainScope.timeOfDay).toBe('morning');
|
||||
expect(mainScope.name).toBe('Nikki');
|
||||
expect(childScope.timeOfDay).toBe('morning');
|
||||
expect(childScope.name).toBe('Mattie');
|
||||
expect(babyScope.timeOfDay).toBe('evening');
|
||||
expect(babyScope.name).toBe('Gingerbreak Baby');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the Model Component
|
||||
@description
|
||||
@@ -8,7 +7,7 @@ either a single object representing one entity (for example, a model called "pho
|
||||
being an array of phones) or the entire data model for the application (all entities).
|
||||
|
||||
In angular, a model is any data that is reachable as a property of an angular {@link
|
||||
dev_guide.scopes Scope} object. The name of the property is the model identifier and the value is
|
||||
scope Scope} object. The name of the property is the model identifier and the value is
|
||||
any JavaScript object (including arrays and primitives).
|
||||
|
||||
The only requirement for a JavaScript object to be a model in angular is that the object must be
|
||||
@@ -21,34 +20,34 @@ following ways:
|
||||
* Make a direct property assignment to the scope object in JavaScript code; this most commonly
|
||||
occurs in controllers:
|
||||
|
||||
function MyCtrl() {
|
||||
function MyCtrl($scope) {
|
||||
// create property 'foo' on the MyCtrl's scope
|
||||
// and assign it an initial value 'bar'
|
||||
this.foo = 'bar';
|
||||
$scope.foo = 'bar';
|
||||
}
|
||||
|
||||
* Use an {@link dev_guide.expressions angular expression} with an assignment operator in templates:
|
||||
* Use an {@link expression angular expression} with an assignment operator in templates:
|
||||
|
||||
<button ng:click="{{foos='ball'}}">Click me</button>
|
||||
<button ng-click="{{foos='ball'}}">Click me</button>
|
||||
|
||||
* Use {@link api/angular.directive.ng:init ng:init directive} in templates (for toy/example apps
|
||||
* Use {@link api/ng.directive:ngInit ngInit directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
<body ng:init=" foo = 'bar' ">
|
||||
<body ng-init=" foo = 'bar' ">
|
||||
|
||||
Angular creates models implicitly (by creating a scope property and assigning it a suitable value)
|
||||
when processing the following template constructs:
|
||||
|
||||
* Form input, select, textarea and other form elements:
|
||||
|
||||
<input name="query" value="fluffy cloud">
|
||||
<input ng-model="query" value="fluffy cloud">
|
||||
|
||||
The code above creates a model called "query" on the current scope with the value set to "fluffy
|
||||
cloud".
|
||||
|
||||
* An iterator declaration in {@link api/angular.widget.@ng:repeat ng:repeater}:
|
||||
* An iterator declaration in {@link api/ng.directive:ngRepeat ngRepeater}:
|
||||
|
||||
<p ng:repeat="phone in phones"></p>
|
||||
<p ng-repeat="phone in phones"></p>
|
||||
|
||||
The code above creates one child scope for each item in the "phones" array and creates a "phone"
|
||||
object (model) on each of these scopes with its value set to the value of "phone" in the array.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the View Component
|
||||
@description
|
||||
@@ -10,8 +9,8 @@ the DOM based on information in the template, controller and model.
|
||||
|
||||
In the angular implementation of MVC, the view has knowledge of both the model and the controller.
|
||||
The view knows about the model where two-way data-binding occurs. The view has knowledge of the
|
||||
controller through angular directives, such as {@link api/angular.directive.ng:controller
|
||||
ng:controller} and {@link api/angular.widget.ng:view ng:view}, and through bindings of this form:
|
||||
controller through angular directives, such as {@link api/ng.directive:ngController
|
||||
ngController} and {@link api/ng.directive:ngView ngView}, and through bindings of this form:
|
||||
`{{someControllerFunction()}}`. In these ways, the view can call functions in an associated
|
||||
controller function.
|
||||
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Overview
|
||||
@description
|
||||
|
||||
|
||||
# What Is Angular?
|
||||
|
||||
The short answer: angular is a new, powerful, client-side technology that makes it much easier for
|
||||
you to create dynamic web sites and complex web apps, all without leaving the comfort of your HTML
|
||||
/ JavaScript home.
|
||||
|
||||
The long answer: it depends on where you're coming from...
|
||||
|
||||
* If you're a web designer, you might perceive angular to be a sweet {@link dev_guide.templates
|
||||
templating} system, that doesn't get in your way and provides you with lots of nice built-ins that
|
||||
make it easier to do what you want to do.
|
||||
|
||||
* If you're a web developer, you might be thrilled that angular functions as an excellent web
|
||||
framework, one that assists you all the way through the development cycle.
|
||||
|
||||
* If you want to go deeper, you can immerse yourself in angular's extensible HTML {@link
|
||||
dev_guide.compiler compiler} that runs in your browser. The angular compiler teaches your browser
|
||||
new tricks.
|
||||
|
||||
Angular is not just a templating system, but you can create fantastic templates with it. Angular is
|
||||
not just a web framework, but it features a very nice framework. Angular is not just an extensible
|
||||
HTML compiler, but the compiler is at the core of Angular. Angular includes all of these
|
||||
components, along with others. Angular is far greater than the sum of its parts. It is a new,
|
||||
better way to develop web applications!
|
||||
|
||||
|
||||
## An Introductory Angular Example
|
||||
|
||||
Let's say that you are a web designer, and you've spent many thous — erm, hundreds of hours
|
||||
designing web sites. But at this point, the thought of manipulating the DOM, writing listeners and
|
||||
input validators, all just to implement a simple form? No. You either don't want to go there in
|
||||
the first place or you've been there and the thrill is gone.
|
||||
|
||||
So look over the following simple example written using angular. Note that it features only the
|
||||
templating aspect of angular, but this should suffice for now to quickly demonstrate how much
|
||||
easier a web developer's life can if they're using angular:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<b>Invoice:</b>
|
||||
<br />
|
||||
<br />
|
||||
<table>
|
||||
<tr><td> </td><td> </td>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input name="qty" value="1" ng:validate="integer:0" ng:required /></td>
|
||||
<td><input name="cost" value="19.95" ng:validate="number" ng:required /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</doc:source>
|
||||
<!--
|
||||
<doc:scenario>
|
||||
it('should show of angular binding', function(){
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</doc:scenario>
|
||||
-->
|
||||
</doc:example>
|
||||
|
||||
Try out the Live Preview above, and then let's walk through the example and describe what's going
|
||||
on.
|
||||
|
||||
In the `<html>` tag, we add an attribute to let the browser know about the angular namespace:
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
This ensures angular runs nicely in all major browsers.
|
||||
|
||||
In the `<script>` tag we do two angular setup tasks:
|
||||
|
||||
1. We load `angular.js`.
|
||||
2. The angular {@link api/angular.directive.ng:autobind ng:autobind} directive tells angular to
|
||||
{@link dev_guide.compiler compile} and manage the whole HTML document.
|
||||
|
||||
`<script src="http://code.angularjs.org/0.9.15/angular-0.9.15.min.js"
|
||||
ng:autobind></script>`
|
||||
|
||||
From the `name` attribute of the `<input>` tags, angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
Quantity: <input name="qty" value="1" ng:validate="integer:0" ng:required/>
|
||||
Cost: <input name="cost" value="199.95" ng:validate="number" ng:required/>
|
||||
|
||||
These input widgets look normal enough, but consider these points:
|
||||
|
||||
* When this page loaded, angular bound the names of the input widgets (`qty` and `cost`) to
|
||||
variables of the same name. Think of those variables as the "Model" component of the
|
||||
Model-View-Controller design pattern.
|
||||
* Note the angular directives, {@link api/angular.widget.@ng:validate ng:validate} and {@link
|
||||
api/angular.widget.@ng:required ng:required}. You may have noticed that when you enter invalid data
|
||||
or leave the the input fields blank, the borders turn red color, and the display value disappears.
|
||||
These `ng:` directives make it easier to implement field validators than coding them in JavaScript,
|
||||
no? Yes.
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
|
||||
Total: {{qty * cost | currency}}
|
||||
|
||||
This notation, `{{ _expression_ }}`, is a bit of built-in angular {@link dev_guide.compiler.markup
|
||||
markup}, a shortcut for displaying data to the user. The expression within curly braces gets
|
||||
transformed by the angular compiler into an angular directive ({@link api/angular.directive.ng:bind
|
||||
ng:bind}). The expression itself can be a combination of both an expression and a {@link
|
||||
dev_guide.templates.filters filter}: `{{ expression | filter }}`. Angular provides filters for
|
||||
formatting display data.
|
||||
|
||||
In the example above, the expression in double-curly braces directs angular to, "Bind the data we
|
||||
got from the input widgets to the display, multiply them together, and format the resulting number
|
||||
into output that looks like money."
|
||||
|
||||
|
||||
# The Angular Philosophy
|
||||
|
||||
Angular is built around the belief that declarative code is better than imperative when it comes to
|
||||
building UIs and wiring software components together, while imperative code is excellent for
|
||||
expressing business logic.
|
||||
|
||||
Not to put too fine a point on it, but if you wanted to add a new label to your application, you
|
||||
could do so by simply adding text to the HTML template, saving the code, and refreshing your
|
||||
browser:
|
||||
|
||||
<pre>
|
||||
<span class="label">Hello</span>
|
||||
</pre>
|
||||
|
||||
Or, as in programmatic systems (like {@link http://code.google.com/webtoolkit/ GWT}), you would
|
||||
have to write the code and then run the code like this:
|
||||
|
||||
<pre>
|
||||
var label = new Label();
|
||||
label.setText('Hello');
|
||||
label.setClass('label');
|
||||
parent.addChild(label);
|
||||
</pre>
|
||||
|
||||
That's one line of markup versus four times as much code.
|
||||
|
||||
|
||||
## More Angular Philosophy
|
||||
|
||||
* It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
|
||||
the testability of the code.
|
||||
* It is a really, _really_ good idea to regard app testing as equal in importance to app writing.
|
||||
Testing difficulty is dramatically affected by the way the code is structured.
|
||||
* It is an excellent idea to decouple the client side of an app from the server side. This allows
|
||||
development work to progress in parallel, and allows for reuse of both sides.
|
||||
* It is very helpful indeed if the framework guides developers through the entire journey of
|
||||
building an app: from designing the UI, through writing the business logic, to testing.
|
||||
* It is always good to make common tasks trivial and difficult tasks possible.
|
||||
|
||||
Now that we're homing in on what angular is, perhaps now would be a good time to list a few things
|
||||
that angular is not:
|
||||
|
||||
* It's not a Library. You don't just call its functions, although it does provide you with some
|
||||
utility APIs.
|
||||
* It's not a DOM Manipulation Library. Angular uses jQuery to manipulate the DOM behind the scenes,
|
||||
rather than give you functions to manipulate the DOM yourself.
|
||||
* It's not a Widget Library. There are lots of existing widget libraries that you can integrate
|
||||
with angular.
|
||||
* It's not "Just Another Templating System". A part of angular is a templating system. The
|
||||
templating subsystem of angular is different from the traditional approach for these reasons:
|
||||
* It Uses HTML/CSS syntax: This makes it easy to read and can be edited with existing HTML/CSS
|
||||
authoring tools.
|
||||
* It Extends HTML vocabulary: Angular allows you to create new HTML tags, which expand into
|
||||
dynamic UI components.
|
||||
* It Executes in the browser: Removes the round trip to the server for many operations and
|
||||
creates instant feedback for users as well as developers.
|
||||
* It Has Bidirectional data binding: The model is the single source of truth. Programmatic
|
||||
changes to the model are automatically reflected in the view. Any changes by the user to the view
|
||||
are automatically reflected in the model.
|
||||
|
||||
|
||||
# Why You Want Angular
|
||||
|
||||
Angular frees you from the following pain:
|
||||
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the
|
||||
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It vastly
|
||||
reduces the amount of JavaScript coding _you_ have to do, and it makes it easier to see what your
|
||||
application does.
|
||||
* **Manipulating HTML DOM programatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it's cumbersome and error-prone. By declaratively describing how the UI should
|
||||
change as your application state changes, you are freed from low level DOM manipulation tasks. Most
|
||||
applications written with angular never have to programatically manipulate the DOM, although you
|
||||
can if you want to.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to an HTML form,
|
||||
allowing users to modify the form, validating the form, displaying validation errors, returning to
|
||||
an internal model, and then back to the server, creates a lot of boilerplate code. Angular
|
||||
eliminates almost all of this boilerplate, leaving code that describes the overall flow of the
|
||||
application rather than all of the implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot of
|
||||
plumbing just to get a basic "Hello World" AJAX app working. With angular you can bootstrap your
|
||||
app easily using services, which are auto-injected into your application in a {@link
|
||||
http://code.google.com/p/google-guice/ Guice}-like dependency-injection style. This allows you to
|
||||
get started developing features quickly. As a bonus, you get full control over the initialization
|
||||
process in automated tests.
|
||||
|
||||
|
||||
# Watch a Presentation About Angular
|
||||
|
||||
Here is an early presentation on angular, but note that substantial development has occurred since
|
||||
the talk was given in July of 2010.
|
||||
|
||||
<object width="480" height="385">
|
||||
<param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"></param>
|
||||
<param name="allowFullScreen" value="true"></param>
|
||||
<param name="allowscriptaccess" value="always"></param>
|
||||
<embed src="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"
|
||||
type="application/x-shockwave-flash" allowscriptaccess="always"
|
||||
allowfullscreen="true" width="480" height="385"></embed>
|
||||
</object>
|
||||
|
||||
{@link
|
||||
|
||||
https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID
|
||||
|
||||
Presentation}
|
||||
|
|
||||
{@link
|
||||
|
||||
https://docs.google.com/document/edit?id=1ZHVhqC0apbzPRQcgnb1Ye-bAUbNJ-IlFMyPBPCZ2cYU&hl=en&authkey=CInnwLYO
|
||||
|
||||
Source}
|
||||
@@ -1,39 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Scopes: Applying Controllers to Scopes
|
||||
@description
|
||||
|
||||
When a controller function is applied to a scope, the scope is augmented with the behavior defined
|
||||
in the controller. The end result is that the scope behaves as if it were the controller:
|
||||
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
scope.$watch('name', function(){
|
||||
this.greeting = this.salutation + ' ' + this.name + '!';
|
||||
});
|
||||
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
scope.name = 'Misko';
|
||||
// scope.$eval() will propagate the change to listeners
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
scope.$eval();
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scopes}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Angular Scopes}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
@@ -1,38 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Scopes
|
||||
@description
|
||||
|
||||
|
||||
An angular scope is a JavaScript type defined by angular. Instances of this type are objects that
|
||||
serve as the context within which all model and controller methods live and get evaluated.
|
||||
|
||||
Angular links scope objects to specific points in a compiled (processed) template. This linkage
|
||||
provides the contexts in which angular creates data-bindings between the model and the view. You
|
||||
can think of angular scope objects as the medium through which the model, view, and controller
|
||||
communicate.
|
||||
|
||||
In addition to providing the context in which data is evaluated, angular scope objects watch for
|
||||
model changes. The scope objects also notify all components interested in any model changes (for
|
||||
example, functions registered through {@link api/angular.scope.$watch $watch}, bindings created by
|
||||
{@link api/angular.directive.ng:bind ng:bind}, or HTML input elements).
|
||||
|
||||
Angular scope objects are responsible for:
|
||||
|
||||
* Gluing the model, controller and view template together.
|
||||
* Providing the mechanism to watch for model changes ({@link api/angular.scope.$watch}).
|
||||
* Notifying interested components when the model changes ({@link api/angular.scope.$eval}).
|
||||
* Providing the context in which all controller functions and angular expressions are evaluated.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Scopes: Understanding Scopes
|
||||
@description
|
||||
|
||||
Angular automatically creates a root scope during initialization, and attaches it to the page's
|
||||
root DOM element (usually `<html>`). The root scope object, along with any of its child scope
|
||||
objects, serves as the infrastructure on which your data model is built. The data model (JavaScript
|
||||
objects, arrays, or primitives) is attached to angular scope properties. Angular binds the property
|
||||
values to the DOM where bindings are specified in the template. Angular attaches any controller
|
||||
functions you have created to their respective scope objects.
|
||||
|
||||
<img src="img/guide/simple_scope_final.png">
|
||||
|
||||
Angular scopes can be nested, so a child scope has a parent scope upstream in the DOM. When you
|
||||
display an angular expression in the view, angular walks the DOM tree looking in the closest
|
||||
attached scope object for the specified data. If it doesn't find the data in the closest attached
|
||||
scope, it looks further up the scope hierarchy until it finds the data.
|
||||
|
||||
A child scope object inherits properties from its parents. For example, in the following snippet of
|
||||
code, observe how the value of `name` changes, based on the HTML element it is displayed in:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<ul ng:init="name='Hank'; names=['Igor', 'Misko', 'Gail', 'Kai']">
|
||||
<li ng:repeat="name in names">
|
||||
Name = {{name}}!
|
||||
</li>
|
||||
</ul>
|
||||
<pre>Name={{name}}</pre>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should override the name property', function() {
|
||||
expect(using('.doc-example-live').repeater('li').row(0)).
|
||||
toEqual(['Igor']);
|
||||
expect(using('.doc-example-live').repeater('li').row(1)).
|
||||
toEqual(['Misko']);
|
||||
|
||||
expect(using('.doc-example-live').repeater('li').row(2)).
|
||||
toEqual(['Gail']);
|
||||
expect(using('.doc-example-live').repeater('li').row(3)).
|
||||
toEqual(['Kai']);
|
||||
expect(using('.doc-example-live').element('pre').text()).
|
||||
toBe('Name=Hank');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
The angular {@link api/angular.widget.@ng:repeat ng:repeat} directive creates a new scope for each
|
||||
element that it repeats (in this example the elements are list items). In the `<ul>` element, we
|
||||
initialized `name` to "Hank", and we created an array called `names` to use as the data source for
|
||||
the list items. In each `<li>` element, `name` is overridden. Outside of the `<li>` repeater, the
|
||||
original value of `name` is displayed.
|
||||
|
||||
The following illustration shows the DOM and angular scopes for the example above:
|
||||
|
||||
<img src="img/guide/dom_scope_final.png">
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
@@ -1,38 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Scopes: Updating Scope Properties
|
||||
@description
|
||||
|
||||
You can update a scope by calling its {@link api/angular.scope.$eval $eval()} method, but usually
|
||||
you do not have to do this explicitly. In most cases, angular intercepts all external events (such
|
||||
as user interactions, XHRs, and timers) and calls the `$eval()` method on the scope object for you
|
||||
at the right time. The only time you might need to call `$eval()` explicitly is when you create
|
||||
your own custom widget or service.
|
||||
|
||||
The reason it is unnecessary to call `$eval()` from within your controller functions when you use
|
||||
built-in angular widgets and services is because a change in the data model triggers a call to the
|
||||
`$eval()` method on the scope object where the data model changed.
|
||||
|
||||
When a user inputs data, angularized widgets copy the data to the appropriate scope and then call
|
||||
the `$eval()` method on the root scope to update the view. It works this way because scopes are
|
||||
inherited, and a child scope `$eval()` overrides its parent's `$eval()` method. Updating the whole
|
||||
page requires a call to `$eval()` on the root scope as `$root.$eval()`. Similarly, when a request
|
||||
to fetch data from a server is made and the response comes back, the data is written into the model
|
||||
and then `$eval()` is called to push updates through to the view and any other dependents.
|
||||
|
||||
A widget that creates scopes (such as {@link api/angular.widget.@ng:repeat ng:repeat}) is
|
||||
responsible for forwarding `$eval()` calls from the parent to those child scopes. That way, calling
|
||||
`$eval()` on the root scope will update the whole page. This creates a spreadsheet-like behavior
|
||||
for your app; the bound views update immediately as the user enters data.
|
||||
|
||||
|
||||
## Related Documents
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
@@ -1,52 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Scopes: Working With Angular Scopes
|
||||
@description
|
||||
|
||||
When you use {@link api/angular.directive.ng:autobind ng:autobind} to bootstrap your application,
|
||||
angular creates the root scope automatically for you. If you need more control over the
|
||||
bootstrapping process, or if you need to create a root scope for a test, you can do so using the
|
||||
{@link api/angular.scope angular.scope()} API.
|
||||
|
||||
Here is a simple code snippet that demonstrates how to create a scope object, assign model
|
||||
properties to it, and register listeners to watch for changes to the model properties:
|
||||
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
// Verify that greeting is undefined
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
// Set up the watcher...
|
||||
scope.$watch('name', function(){
|
||||
// when 'name' changes, set 'greeting'...
|
||||
this.greeting = this.salutation + ' ' + this.name + '!';
|
||||
}
|
||||
);
|
||||
|
||||
// verify that 'greeting' was set...
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
// 'name' changed!
|
||||
scope.name = 'Misko';
|
||||
|
||||
// scope.$eval() will propagate the change to listeners
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
scope.$eval();
|
||||
// verify that '$eval' propagated the change
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
</pre>
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.scopes Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.scope Angular Scope API}
|
||||
@@ -0,0 +1,640 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Using $location
|
||||
@description
|
||||
|
||||
# What does it do?
|
||||
|
||||
The `$location` service parses the URL in the browser address bar (based on the {@link
|
||||
https://developer.mozilla.org/en/window.location window.location}) and makes the URL available to
|
||||
your application. Changes to the URL in the address bar are reflected into $location service and
|
||||
changes to $location are reflected into the browser address bar.
|
||||
|
||||
**The $location service:**
|
||||
|
||||
- Exposes the current URL in the browser address bar, so you can
|
||||
- Watch and observe the URL.
|
||||
- Change the URL.
|
||||
- Synchronizes the URL with the browser when the user
|
||||
- Changes the address bar.
|
||||
- Clicks the back or forward button (or clicks a History link).
|
||||
- Clicks on a link.
|
||||
- Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
|
||||
|
||||
|
||||
## Comparing $location to window.location
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<td class="empty-corner-lt"></td>
|
||||
<td>window.location</td>
|
||||
<td>$location service</td>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="head">purpose</td>
|
||||
<td>allow read/write access to the current browser location</td>
|
||||
<td>same</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">API</td>
|
||||
<td>exposes "raw" object with properties that can be directly modified</td>
|
||||
<td>exposes jQuery-style getters and setters</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">integration with angular application life-cycle</td>
|
||||
<td>none</td>
|
||||
<td>knows about all internal life-cycle phases, integrates with $watch, ...</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">seamless integration with HTML5 API</td>
|
||||
<td>no</td>
|
||||
<td>yes (with a fallback for legacy browsers)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">aware of docroot/context from which the application is loaded</td>
|
||||
<td>no - window.location.path returns "/docroot/actual/path"</td>
|
||||
<td>yes - $location.path() returns "/actual/path"</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## When should I use $location?
|
||||
Any time your application needs to react to a change in the current URL or if you want to change
|
||||
the current URL in the browser.
|
||||
|
||||
## What does it not do?
|
||||
Does not cause a full page reload when the browser URL is changed. To reload the page after
|
||||
changing the URL, use the lower-level API, `$window.location.href`.
|
||||
|
||||
|
||||
# General overview of the API
|
||||
|
||||
The `$location` service can behave differently, depending on the configuration that was provided to
|
||||
it when it was instantiated. The default configuration is suitable for many applications, for
|
||||
others customizing the configuration can enable new features.
|
||||
|
||||
Once the `$location` service is instantiated, you can interact with it via jQuery-style getter and
|
||||
setter methods that allow you to get or change the current URL in the browser.
|
||||
|
||||
## $location service configuration
|
||||
|
||||
To configure the `$location` service, retrieve the
|
||||
{@link api/ng.$locationProvider $locationProvider} and set the parameters as follows:
|
||||
|
||||
|
||||
- **html5Mode(mode)**: {boolean}<br />
|
||||
`true` - see HTML5 mode<br />
|
||||
`false` - see Hashbang mode<br />
|
||||
default: `false`
|
||||
|
||||
- **hashPrefix(prefix)**: {string}<br />
|
||||
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
|
||||
default: `'!'`
|
||||
|
||||
### Example configuration
|
||||
<pre>
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
</pre>
|
||||
|
||||
## Getter and setter methods
|
||||
|
||||
`$location` service provides getter methods for read-only parts of the URL (absUrl, protocol, host,
|
||||
port) and getter / setter methods for url, path, search, hash:
|
||||
<pre>
|
||||
// get the current path
|
||||
$location.path();
|
||||
|
||||
// change the path
|
||||
$location.path('/newValue')
|
||||
</pre>
|
||||
|
||||
All of the setter methods return the same `$location` object to allow chaining. For example, to
|
||||
change multiple segments in one go, chain setters like this:
|
||||
<pre>$location.path('/newValue').search({key: value});</pre>
|
||||
|
||||
There is a special `replace` method which can be used to tell the $location service that the next
|
||||
time the $location service is synced with the browser, the last history record should be replaced
|
||||
instead of creating a new one. This is useful when you want to implement redirection, which would
|
||||
otherwise break the back button (navigating back would retrigger the redirection). To change the
|
||||
current URL without creating a new browser history record you can call:
|
||||
<pre>
|
||||
$location.path('/someNewPath');
|
||||
$location.replace();
|
||||
// or you can chain these as: $location.path('/someNewPath').replace();
|
||||
</pre>
|
||||
|
||||
Note that the setters don't update `window.location` immediately. Instead, `$location` service is
|
||||
aware of the {@link api/ng.$rootScope.Scope scope} life-cycle and coalesces multiple `$location`
|
||||
mutations into one "commit" to the `window.location` object during the scope `$digest` phase. Since
|
||||
multiple changes to the $location's state will be pushed to the browser as a single change, it's
|
||||
enough to call the `replace()` method just once to make the entire "commit" a replace operation
|
||||
rather than addition to the browser history. Once the browser is updated, the $location service
|
||||
resets the flag set by `replace()` method and future mutations will create new history records,
|
||||
unless `replace()` is called again.
|
||||
|
||||
### Setters and character encoding
|
||||
You can pass special characters to `$location` service and it will encode them according to rules
|
||||
specified in {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. When you access the methods:
|
||||
|
||||
- All values that are passed to `$location` setter methods, `path()`, `search()`, `hash()`, are
|
||||
encoded.
|
||||
- Getters (calls to methods without parameters) return decoded values for the following methods
|
||||
`path()`, `search()`, `hash()`.
|
||||
- When you call the `absUrl()` method, the returned value is a full url with its segments encoded.
|
||||
- When you call the `url()` method, the returned value is path, search and hash, in the form
|
||||
`/path?search=a&b=c#hash`. The segments are encoded as well.
|
||||
|
||||
|
||||
# Hashbang and HTML5 Modes
|
||||
|
||||
`$location` service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
|
||||
HTML5 {@link http://www.w3.org/TR/html5/history.html History API}. Applications use the same API in
|
||||
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.
|
||||
|
||||
<img src="img/guide/hashbang_vs_regular_url.jpg">
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<td class="empty-corner-lt"></td>
|
||||
<td>Hashbang mode</td>
|
||||
<td>HTML5 mode</td>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="head">configuration</td>
|
||||
<td>the default</td>
|
||||
<td>{ html5Mode: true }</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">URL format</td>
|
||||
<td>hashbang URLs in all browsers</td>
|
||||
<td>regular URLs in modern browser, hashbang URLs in old browser</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head"><a href=""> link rewriting</td>
|
||||
<td>no</td>
|
||||
<td>yes</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">requires server-side configuration</td>
|
||||
<td>no</td>
|
||||
<td>yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Hashbang mode (default mode)
|
||||
|
||||
In this mode, `$location` uses Hashbang URLs in all browsers.
|
||||
|
||||
### Example
|
||||
|
||||
<pre>
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5mode = false;
|
||||
$locationProvider.hashPrefix = '!';
|
||||
},
|
||||
function($location) {
|
||||
// open http://host.com/base/index.html#!/a
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/a'
|
||||
$location.path() == '/a'
|
||||
|
||||
$location.path('/foo')
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/foo'
|
||||
|
||||
$location.search() == {}
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/foo?a=b&c'
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.absUrl() == 'http://host.com/base/index.html#!/new?x=y'
|
||||
}
|
||||
));
|
||||
</pre>
|
||||
|
||||
### Crawling your app
|
||||
|
||||
To allow indexing of your AJAX application, you have to add special meta tag in the head section of
|
||||
your document:
|
||||
<pre><meta name="fragment" content="!" /></pre>
|
||||
|
||||
This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
|
||||
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
|
||||
see {@link http://code.google.com/web/ajaxcrawling/docs/specification.html Making AJAX Applications
|
||||
Crawlable}.
|
||||
|
||||
## HTML5 mode
|
||||
|
||||
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
|
||||
through the HTML5 history API, which allows for use of regular URL path and search segments,
|
||||
instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the
|
||||
`$location` service will fall back to using the hashbang URLs automatically. This frees you from
|
||||
having to worry about whether the browser displaying your app supports the history API or not; the
|
||||
`$location` service transparently uses the best available option.
|
||||
|
||||
- Opening a regular URL in a legacy browser -> redirects to a hashbang URL
|
||||
- Opening hashbang URL in a modern browser -> rewrites to a regular URL
|
||||
|
||||
### Example
|
||||
|
||||
<pre>
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5mode = true;
|
||||
$locationProvider.hashPrefix = '!';
|
||||
},
|
||||
function($location) {
|
||||
// in browser with HTML5 history support:
|
||||
// open http://host.com/#!/a -> rewrite to http://host.com/a
|
||||
// (replacing the http://host.com/#!/a history record)
|
||||
$location.path() == '/a'
|
||||
|
||||
$location.path('/foo');
|
||||
$location.absUrl() == 'http://host.com/foo'
|
||||
|
||||
$location.search() == {}
|
||||
$location.search({a: 'b', c: true});
|
||||
$location.absUrl() == 'http://host.com/foo?a=b&c'
|
||||
|
||||
$location.path('/new').search('x=y');
|
||||
$location.url() == 'new?x=y'
|
||||
$location.absUrl() == 'http://host.com/new?x=y'
|
||||
|
||||
// in browser without html5 history support:
|
||||
// open http://host.com/new?x=y -> redirect to http://host.com/#!/new?x=y
|
||||
// (again replacing the http://host.com/new?x=y history item)
|
||||
$location.path() == '/new'
|
||||
$location.search() == {x: 'y'}
|
||||
|
||||
$location.path('/foo/bar');
|
||||
$location.path() == '/foo/bar'
|
||||
$location.url() == '/foo/bar?x=y'
|
||||
$location.absUrl() == 'http://host.com/#!/foo/bar?x=y'
|
||||
}
|
||||
));
|
||||
</pre>
|
||||
|
||||
### Fallback for legacy browsers
|
||||
|
||||
For browsers that support the HTML5 history API, `$location` uses the HTML5 history API to write
|
||||
path and search. If the history API is not supported by a browser, `$location` supplies a Hasbang
|
||||
URL. This frees you from having to worry about whether the browser viewing your app supports the
|
||||
history API or not; the `$location` service makes this transparent to you.
|
||||
|
||||
### Html link rewriting
|
||||
|
||||
When you use the history API mode, you will need different links in different browser, but all you
|
||||
have to do is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
|
||||
|
||||
When a user clicks on this link,
|
||||
|
||||
- In a legacy browser, the URL changes to `/index.html#!/some?foo=bar`
|
||||
- In a modern browser, the URL changes to `/some?foo=bar`
|
||||
|
||||
|
||||
In cases like the following, links are not rewritten; instead, the browser will perform a full page
|
||||
reload to the original link.
|
||||
|
||||
- Links that contain `target` element<br>
|
||||
Example: `<a href="/ext/link?a=b" target="_self">link</a>`
|
||||
- Absolute links that go to a different domain<br>
|
||||
Example: `<a href="http://angularjs.org/">link</a>`
|
||||
- Links starting with '/' that lead to a different base path when base is defined<br>
|
||||
Example: `<a href="/not-my-base/link">link</a>`
|
||||
|
||||
|
||||
### Server side
|
||||
|
||||
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
|
||||
to entry point of your application (e.g. index.html)
|
||||
|
||||
### Crawling your app
|
||||
|
||||
If you want your AJAX application to be indexed by web crawlers, you will need to add the following
|
||||
meta tag to the HEAD section of your document:
|
||||
<pre><meta name="fragment" content="!" /></pre>
|
||||
|
||||
This statement causes a crawler to request links with an empty `_escaped_fragment_` parameter so that
|
||||
your server can recognize the crawler and serve it HTML snapshots. For more information about this
|
||||
technique, see {@link http://code.google.com/web/ajaxcrawling/docs/specification.html Making AJAX
|
||||
Applications Crawlable}.
|
||||
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. You must either specify the url base in
|
||||
the head of your main html file (`<base href="/my-base">`) or you must use absolute urls
|
||||
(starting with `/`) everywhere because relative urls will be resolved to absolute urls using the
|
||||
initial absolute url of the document, which is often different from the root of the application.
|
||||
|
||||
Running Angular apps with the History API enabled from document root is strongly encouraged as it
|
||||
takes care of all relative link issues.
|
||||
|
||||
### Sending links among different browsers
|
||||
|
||||
Because of rewriting capability in HTML5 mode, your users will be able to open regular url links in
|
||||
legacy browsers and hashbang links in modern browser:
|
||||
|
||||
- Modern browser will rewrite hashbang URLs to regular URLs.
|
||||
- Older browsers will redirect regular URLs to hashbang URLs.
|
||||
|
||||
### Example
|
||||
|
||||
Here you can see two `$location` instances, both in **Html5 mode**, but on different browsers, so
|
||||
that you can see the differences. These `$location` services are connected to a fake browsers. Each
|
||||
input represents address bar of the browser.
|
||||
|
||||
Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
|
||||
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
|
||||
= on page reload.
|
||||
|
||||
In this examples we use `<base href="/base/index.html" />`
|
||||
<doc:example>
|
||||
<doc:source source="false">
|
||||
|
||||
<div ng-non-bindable class="html5-hashbang-example">
|
||||
<div id="html5-mode" ng-controller="Html5Cntl">
|
||||
<h3>Browser with History API</h3>
|
||||
<div ng-address-bar browser="html5"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
|
||||
<div id="hashbang-mode" ng-controller="HashbangCntl">
|
||||
<h3>Browser without History API</h3>
|
||||
<div ng-address-bar browser="hashbang"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function FakeBrowser(initUrl, baseHref) {
|
||||
this.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
this.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
|
||||
this.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
|
||||
this.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
this.notifyWhenOutstandingRequests = angular.noop;
|
||||
}
|
||||
|
||||
var browsers = {
|
||||
html5: new FakeBrowser('http://www.host.com/base/path?a=b#h', '/base/index.html'),
|
||||
hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
|
||||
function Html5Cntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function HashbangCntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$document', root);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
|
||||
$compileProvider.directive('ngAddressBar', function() {
|
||||
return function(scope, elm, attrs) {
|
||||
var browser = browsers[attrs.browser],
|
||||
input = angular.element('<input type="text">').val(browser.url()),
|
||||
delay;
|
||||
|
||||
input.bind('keypress keyup keydown', function() {
|
||||
if (!delay) {
|
||||
delay = setTimeout(fireUrlChange, 250);
|
||||
}
|
||||
});
|
||||
|
||||
browser.url = function(url) {
|
||||
return input.val(url);
|
||||
};
|
||||
|
||||
elm.append('Address: ').append(input);
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
browser.urlChange(input.val());
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
root.bind('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
initEnv('html5');
|
||||
initEnv('hashbang');
|
||||
</script>
|
||||
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Caveats
|
||||
|
||||
## Page reload navigation
|
||||
|
||||
The `$location` service allows you to change only the URL; it does not allow you to reload the
|
||||
page. When you need to change the URL and reload the page or navigate to a different page, please
|
||||
use a lower level API, {@link api/ng.$window $window.location.href}.
|
||||
|
||||
## Using $location outside of the scope life-cycle
|
||||
|
||||
`$location` knows about Angular's {@link api/ng.$rootScope.Scope scope} life-cycle. When a URL changes in
|
||||
the browser it updates the `$location` and calls `$apply` so that all $watchers / $observers are
|
||||
notified.
|
||||
When you change the `$location` inside the `$digest` phase everything is ok; `$location` will
|
||||
propagate this change into browser and will notify all the $watchers / $observers.
|
||||
When you want to change the `$location` from outside Angular (for example, through a DOM Event or
|
||||
during testing) - you must call `$apply` to propagate the changes.
|
||||
|
||||
## $location.path() and ! or / prefixes
|
||||
|
||||
A path should always begin with forward slash (`/`); the `$location.path()` setter will add the
|
||||
forward slash if it is missing.
|
||||
|
||||
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
|
||||
hashPrefix.
|
||||
|
||||
|
||||
# Testing with the $location service
|
||||
|
||||
When using `$location` service during testing, you are outside of the angular's {@link
|
||||
api/ng.$rootScope.Scope scope} life-cycle. This means it's your responsibility to call `scope.$apply()`.
|
||||
|
||||
<pre>
|
||||
describe('serviceUnderTest', function() {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.factory('serviceUnderTest', function($location){
|
||||
// whatever it does...
|
||||
});
|
||||
});
|
||||
|
||||
it('should...', inject(function($location, $rootScope, serviceUnderTest) {
|
||||
$location.path('/new/path');
|
||||
$rootScope.$apply();
|
||||
|
||||
// test whatever the service should do...
|
||||
|
||||
}));
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
# Migrating from earlier AngularJS releases
|
||||
|
||||
In earlier releases of Angular, `$location` used `hashPath` or `hashSearch` to process path and
|
||||
search methods. With this release, the `$location` service processes path and search methods and
|
||||
then uses the information it obtains to compose hashbang URLs (such as
|
||||
`http://server.com/#!/path?search=a`), when necessary.
|
||||
|
||||
## Changes to your code
|
||||
|
||||
<table>
|
||||
<tr class="head">
|
||||
<td>Navigation inside the app</td>
|
||||
<td>Change to</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.href = value<br />$location.hash = value<br />$location.update(value)<br
|
||||
/>$location.updateHash(value)</td>
|
||||
<td>$location.path(path).search(search)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashPath = path</td>
|
||||
<td>$location.path(path)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashSearch = search</td>
|
||||
<td>$location.search(search)</td>
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<td>Navigation outside the app</td>
|
||||
<td>Use lower level API</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.href = value<br />$location.update(value)</td>
|
||||
<td>$window.location.href = value</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location[protocol | host | port | path | search]</td>
|
||||
<td>$window.location[protocol | host | port | path | search]</td>
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<td>Read access</td>
|
||||
<td>Change to</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashPath</td>
|
||||
<td>$location.path()</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.hashSearch</td>
|
||||
<td>$location.search()</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.href<br />$location.protocol<br />$location.host<br />$location.port<br
|
||||
/>$location.hash</td>
|
||||
<td>$location.absUrl()<br />$location.protocol()<br />$location.host()<br />$location.port()<br
|
||||
/>$location.path() + $location.search()</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>$location.path<br />$location.search</td>
|
||||
<td>$window.location.path<br />$window.location.search</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Two-way binding to $location
|
||||
|
||||
The Angular's compiler currently does not support two-way binding for methods (see {@link
|
||||
https://github.com/angular/angular.js/issues/404 issue}). If you should require two-way binding
|
||||
to the $location object (using {@link api/ng.directive:input.text
|
||||
ngModel} directive on an input field), you will need to specify an extra model property
|
||||
(e.g. `locationPath`) with two watchers which push $location updates in both directions. For
|
||||
example:
|
||||
<pre>
|
||||
<!-- html -->
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</pre>
|
||||
<pre>
|
||||
// js - controller
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
|
||||
$scope.$watch('$location.path()', function(path) {
|
||||
scope.locationPath = path;
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
# Related API
|
||||
|
||||
* {@link api/ng.$location $location API}
|
||||
|
||||
|
||||
|
||||
@@ -1,59 +1,104 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Creating Angular Services
|
||||
@name Developer Guide: Angular Services: Creating Services
|
||||
@description
|
||||
|
||||
While angular offers several useful services, for any nontrivial application you'll find it useful
|
||||
While Angular offers several useful services, for any nontrivial application you'll find it useful
|
||||
to write your own custom services. To do this you begin by registering a service factory function
|
||||
that angular's DI will use to create the service object when it is needed.
|
||||
with a module either via the {@link api/angular.module Module#factory api} or directly
|
||||
via the {@link api/AUTO.$provide $provide} api inside of module config function.
|
||||
|
||||
The `angular.service` method accepts three parameters:
|
||||
|
||||
- `{string} name` - Name of the service.
|
||||
- `{function()} factory` - Factory function (called just once by DI).
|
||||
- `{Object} config` - Configuration object with the following properties:
|
||||
- `$inject` - {Array.<string>} - Array of service ids this service depends on. These services
|
||||
will be passed as arguments into the factory function in the same order specified in the `$inject`
|
||||
array. Defaults to `[]`.
|
||||
- `$eager` - {boolean} - If true, the service factory will be called and the service will be
|
||||
instantiated when angular boots. If false, the service will be lazily instantiated when it is first
|
||||
requested during instantiation of a dependant. Defaults to `false`.
|
||||
|
||||
The `this` of the factory function is bound to the root scope of the angular application.
|
||||
|
||||
All angular services participate in {@link dev_guide.di dependency injection (DI)} by registering
|
||||
themselves with angular's DI system (injector) under a `name` (id) as well as by declaring
|
||||
All Angular services participate in {@link di dependency injection (DI)} by registering
|
||||
themselves with Angular's DI system (injector) under a `name` (id) as well as by declaring
|
||||
dependencies which need to be provided for the factory function of the registered service. The
|
||||
ability to swap dependencies for mocks/stubs/dummies in tests allows for services to be highly
|
||||
testable.
|
||||
|
||||
|
||||
# Registering Services
|
||||
|
||||
To register a service, you must have a module that this service will be part of. Afterwards, you
|
||||
can register the service with the module either via the {@link api/angular.Module Module api} or
|
||||
by using the {@link api/AUTO.$provide $provide} service in the module configuration
|
||||
function.The following pseudo-code shows both approaches:
|
||||
|
||||
Using the angular.Module api:
|
||||
<pre>
|
||||
var myModule = angular.module('myModule', []);
|
||||
myModule.factory('serviceId', function() {
|
||||
var shinyNewServiceInstance;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
});
|
||||
</pre>
|
||||
|
||||
Using the $provide service:
|
||||
<pre>
|
||||
angular.module('myModule', [], function($provide) {
|
||||
$provide.factory('serviceId', function() {
|
||||
var shinyNewServiceInstance;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that you are not registering a service instance, but rather a factory function that will
|
||||
create this instance when called.
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
Services can not only be depended upon, but also have its own dependencies. These can be specified
|
||||
as arguments of the factory function. {@link di Read more} about the DI
|
||||
in Angular and the use of array notation and $inject property to make DI annotation
|
||||
minification-proof.
|
||||
|
||||
Following is an example of a very simple service. This service depends on the `$window` service
|
||||
(which is passed as a parameter to the factory function) and is just a function. The service simply
|
||||
stores all notifications; after the third one, the service displays all of the notifications by
|
||||
window alert.
|
||||
|
||||
<pre>
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$inject: ['$window']});
|
||||
angular.module('myModule', [], function($provide) {
|
||||
$provide.factory('notify', ['$window', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
# Instantiating Angular Services
|
||||
|
||||
All services in Angular are instantiated lazily. This means that a service will be created
|
||||
only when it is needed for instantiation of a service or an application component that depends on it.
|
||||
In other words, Angular won't instantiate lazy services unless they are requested directly or
|
||||
indirectly by the application.
|
||||
|
||||
|
||||
# Services as singletons
|
||||
|
||||
Lastly, it is important to realize that all Angular services are application singletons. This means
|
||||
that there is only one instance of a given service per injector. Since Angular is lethally allergic
|
||||
to global state, it is possible to create multiple injectors, each with its own instance of a
|
||||
given service, but that is rarely needed, except in tests where this property is crucially
|
||||
important.
|
||||
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
* {@link api/ng Angular Service API}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Injecting Services Into Controllers
|
||||
@description
|
||||
@@ -7,12 +6,12 @@ Using services as dependencies for controllers is very similar to using services
|
||||
for another service.
|
||||
|
||||
Since JavaScript is a dynamic language, DI can't figure out which services to inject by static
|
||||
types (like in static typed languages). Therefore, you must specify the service name by using the
|
||||
types (like in static typed languages). Therefore, you can specify the service name by using the
|
||||
`$inject` property, which is an array containing strings with names of services to be injected.
|
||||
The name must match the corresponding service ID registered with angular. The order of the service
|
||||
IDs matters: the order of the services in the array will be used when calling the factory function
|
||||
with injected parameters. The names of parameters in factory function don't matter, but by
|
||||
convention they match the service IDs.
|
||||
convention they match the service IDs, which has added benefits discussed below.
|
||||
|
||||
<pre>
|
||||
function myController($loc, $log) {
|
||||
@@ -29,51 +28,91 @@ this.secondMethod = function() {
|
||||
myController.$inject = ['$location', '$log'];
|
||||
</pre>
|
||||
|
||||
<doc:example>
|
||||
<doc:example module="MyServiceModule">
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$inject: ['$window']});
|
||||
<script>
|
||||
angular.
|
||||
module('MyServiceModule', []).
|
||||
factory('notify', ['$window', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
function myController(notifyService) {
|
||||
this.callNotify = function(msg) {
|
||||
function myController(scope, notifyService) {
|
||||
scope.callNotify = function(msg) {
|
||||
notifyService(msg);
|
||||
};
|
||||
}
|
||||
|
||||
myController.$inject = ['notify'];
|
||||
myController.$inject = ['$scope','notify'];
|
||||
</script>
|
||||
|
||||
<div ng:controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng:init="message='test'" type="text" name="message" />
|
||||
<button ng:click="callNotify(message);">NOTIFY</button>
|
||||
<div ng-controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message" >
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should test service', function(){
|
||||
expect(element(':input[name=message]').val()).toEqual('test');
|
||||
});
|
||||
it('should test service', function() {
|
||||
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
## Implicit Dependency Injection
|
||||
|
||||
A new feature of Angular DI allows it to determine the dependency from the name of the parameter.
|
||||
Let's rewrite the above example to show the use of this implicit dependency injection of
|
||||
`$window`, `$scope`, and our `notify` service:
|
||||
|
||||
<doc:example module="MyServiceModuleDI">
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.
|
||||
module('MyServiceModuleDI', []).
|
||||
factory('notify', function($window) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
$window.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function myController($scope, notify) {
|
||||
$scope.callNotify = function(msg) {
|
||||
notify(msg);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="myController">
|
||||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message">
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
However, if you plan to {@link http://en.wikipedia.org/wiki/Minification_(programming) minify} your
|
||||
code, your variable names will get renamed in which case you will still need to explicitly specify
|
||||
dependencies with the `$inject` property.
|
||||
|
||||
## Related Topics
|
||||
|
||||
{@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
{@link dev_guide.services.creating_services Creating Angular Services}
|
||||
{@link dev_guide.services.registering_services Registering Angular Services}
|
||||
{@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
{@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
{@link api/angular.service Angular Service API}
|
||||
{@link api/ng Angular Service API}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Managing Service Dependencies
|
||||
@description
|
||||
@@ -6,80 +5,110 @@
|
||||
Angular allows services to declare other services as dependencies needed for construction of their
|
||||
instances.
|
||||
|
||||
To declare dependencies, you specify them in the factory function signature and via the `$inject`
|
||||
property, as an array of string identifiers. Optionally the `$inject` property declaration can be
|
||||
To declare dependencies, you specify them in the factory function signature and annotate the
|
||||
function with the inject annotations either using by setting the `$inject` property, as an array of
|
||||
string identifiers or using the array notation. Optionally the `$inject` property declaration can be
|
||||
dropped (see "Inferring `$inject`" but note that that is currently an experimental feature).
|
||||
|
||||
Using the array notation:
|
||||
|
||||
<pre>
|
||||
function myModuleCfgFn($provide) {
|
||||
$provide.factory('myService', ['dep1', 'dep2', function(dep1, dep2) {}]);
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Using the $inject property:
|
||||
|
||||
<pre>
|
||||
function myModuleCfgFn($provide) {
|
||||
var myServiceFactory = function(dep1, dep2) {};
|
||||
myServiceFactory.$inject = ['dep1', 'dep2'];
|
||||
$provide.factory('myService', myServiceFactory);
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Using DI inference (incompatible with minifiers):
|
||||
|
||||
<pre>
|
||||
function myModuleCfgFn($provide) {
|
||||
$provide.factory('myService', function(dep1, dep2) {});
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Here is an example of two services that depend on each other, as well as on other services that are
|
||||
provided by angular's web framework:
|
||||
provided by Angular's web framework:
|
||||
|
||||
<pre>
|
||||
/**
|
||||
* batchLog service allows for messages to be queued in memory and flushed
|
||||
* to the console.log every 50 seconds.
|
||||
*
|
||||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
angular.service('batchLog', function($defer, $log) {
|
||||
var messageQueue = [];
|
||||
* batchLog service allows for messages to be queued in memory and flushed
|
||||
* to the console.log every 50 seconds.
|
||||
*
|
||||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
function batchLogModule($provide){
|
||||
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
|
||||
var messageQueue = [];
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$defer(log, 50000);
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$timeout(log, 50000);
|
||||
}
|
||||
|
||||
// start periodic checking
|
||||
log();
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
}
|
||||
}]);
|
||||
|
||||
/**
|
||||
* routeTemplateMonitor monitors each $route change and logs the current
|
||||
* template via the batchLog service.
|
||||
*/
|
||||
$provide.factory('routeTemplateMonitor',
|
||||
['$route', 'batchLog', '$rootScope',
|
||||
function($route, batchLog, $rootScope) {
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
batchLog($route.current ? $route.current.template : null);
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
// start periodic checking
|
||||
log();
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
}
|
||||
}, {$inject: ['$defer', '$log']});
|
||||
// note how we declared dependency on built-in $defer and $log services above
|
||||
|
||||
/**
|
||||
* routeTemplateMonitor monitors each $route change and logs the current
|
||||
* template via the batchLog service.
|
||||
*/
|
||||
angular.service('routeTemplateMonitor', function($route, batchLog) {
|
||||
$route.onChange(function() {
|
||||
batchLog($route.current ? $route.current.template : null);
|
||||
});
|
||||
}, {$inject: ['$route', 'batchLog'], $eager: true});
|
||||
// get the main service to kick of the application
|
||||
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
||||
</pre>
|
||||
|
||||
Things to notice in this example:
|
||||
|
||||
* The `batchLog` service depends on the built-in {@link api/angular.service.$defer $defer} and
|
||||
{@link api/angular.service.$log $log} services, and allows messages to be logged into the
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$timeout $timeout} and
|
||||
{@link api/ng.$log $log} services, and allows messages to be logged into the
|
||||
`console.log` in batches.
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link api/angular.service.$route
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link api/ng.$route
|
||||
$route} service as well as our custom `batchLog` service.
|
||||
* The `routeTemplateMonitor` service is declared to be eager, so that it is started as soon as the
|
||||
application starts.
|
||||
* To underline the need for the eager instantiation of the `routeTemplateMonitor` service, nothing
|
||||
else in the application depends on this service, and in this particular case the factory function
|
||||
of this service doesn't return anything at all.
|
||||
* Both of our services use the factory function signature as well as the `$inject` property to
|
||||
declare their dependencies. It is important that the order of the string identifiers in the array
|
||||
associated with the `$inject` property is the same as the order of argument names in the signature
|
||||
of the factory function. Unless the dependencies are inferred from the function signature, it is
|
||||
this array with IDs and their order that the injector uses to determine which services and in which
|
||||
order to inject.
|
||||
* Both of our services use the factory function signature and array notation for inject annotations
|
||||
to declare their dependencies. It is important that the order of the string identifiers in the array
|
||||
is the same as the order of argument names in the signature of the factory function. Unless the
|
||||
dependencies are inferred from the function signature, it is this array with IDs and their order
|
||||
that the injector uses to determine which services and in which order to inject.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Services}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
* {@link api/ng Angular Service API}
|
||||
* {@link api/angular.injector Angular Injector API}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services
|
||||
@description
|
||||
|
||||
Services are a feature that angular brings to client-side web apps from the server side, where
|
||||
services have been commonly used for a long time. Services in angular apps are substitutable
|
||||
objects that are wired together using {@link dev_guide.di dependency injection (DI)}. Services are
|
||||
most often used with {@link dev_guide.di dependency injection}, also a key feature of angular apps.
|
||||
objects that are wired together using {@link di dependency injection (DI)}. Services are
|
||||
most often used with {@link di dependency injection}, also a key feature of angular apps.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers}
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
* {@link api/ng Angular Service API}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Registering Angular Services
|
||||
@description
|
||||
|
||||
To register a service, register a factory function that creates the service with angular's
|
||||
Injector. The Injector is exposed as {@link api/angular.scope.$service scope.$service}. The
|
||||
following pseudo-code shows a simple service registration:
|
||||
|
||||
<pre>
|
||||
angular.service('service id', function() {
|
||||
var shinyNewServiceInstance;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that you are not registering a service instance, but rather a factory function that will
|
||||
create this instance when called.
|
||||
|
||||
# Instantiating Angular Services
|
||||
|
||||
A service can be instantiated eagerly or lazily. By default angular instantiates services lazily,
|
||||
which means that a service will be created only when it is needed for instantiation of a service or
|
||||
an application component that depends on it. In other words, angular won't instantiate lazy
|
||||
services unless they are requested directly or indirectly by the application.
|
||||
|
||||
Eager services on the other hand, are instantiated right after the injector itself is created,
|
||||
which happens when the angular {@link dev_guide.bootstrap application initializes}.
|
||||
|
||||
To override the default, you can request that a service is eagerly instantiated as follows:
|
||||
|
||||
<pre>
|
||||
angular.service('service id', function() {
|
||||
var shinyNewServiceInstance;
|
||||
//factory function body that constructs shinyNewServiceInstance
|
||||
return shinyNewServiceInstance;
|
||||
}, {$eager: true});
|
||||
</pre>
|
||||
|
||||
While it is tempting to declare services as eager, only in few cases it is actually useful. If you
|
||||
are unsure whether to make a service eager, it likely doesn't need to be. To be more specific, a
|
||||
service should be declared as eager only if it fits one of these scenarios:
|
||||
|
||||
* Nothing in your application declares this service as its dependency, and this service affects the
|
||||
state or configuration of the application (e.g. a service that configures `$route` or `$resource`
|
||||
services)
|
||||
* A guarantee is needed that the service will be instantiated at application boot time, usually
|
||||
because the service passively observes the application and it is optional for other application
|
||||
components to depend on it. An example of this scenario is a service that monitors and logs
|
||||
application memory usage.
|
||||
|
||||
Lastly, it is important to realize that all angular services are applicaiton singletons. This means
|
||||
that there is only one instance of a given service per injector. Since angular is lethally allergic
|
||||
to the global state, it is possible to create multiple injectors, each with its own instance of a
|
||||
given service, but that is rarely needed, except in tests where this property is crucially
|
||||
important.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
@@ -1,9 +1,8 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Testing Angular Services
|
||||
@description
|
||||
|
||||
Following is a unit test for the service in the example in {@link
|
||||
The following is a unit test for the 'notify' service in the 'Dependencies' example in {@link
|
||||
dev_guide.services.creating_services Creating Angular Services}. The unit test example uses Jasmine
|
||||
spy (mock) instead of a real browser alert.
|
||||
|
||||
@@ -12,7 +11,14 @@ var mock, notify;
|
||||
|
||||
beforeEach(function() {
|
||||
mock = {alert: jasmine.createSpy()};
|
||||
notify = angular.service('notify')(mock);
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('$window', mock);
|
||||
});
|
||||
|
||||
inject(function($injector) {
|
||||
notify = $injector.get('notify');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not alert first two notifications', function() {
|
||||
@@ -48,12 +54,9 @@ it('should clear messages after alert', function() {
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Conrollers}
|
||||
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
|
||||
|
||||
* {@link api/ng Angular Service API}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Understanding Angular Services
|
||||
@description
|
||||
|
||||
Angular services are singletons that carry out specific tasks common to web apps, such as the
|
||||
{@link api/angular.service.$xhr $xhr service} that provides low level access to the browser's
|
||||
{@link api/ng.$http $http service} that provides low level access to the browser's
|
||||
`XMLHttpRequest` object.
|
||||
|
||||
To use an angular service, you identify it as a dependency for the dependent (a controller, or
|
||||
@@ -13,26 +12,25 @@ of the rest. The angular injector subsystem is in charge of service instantiatio
|
||||
dependencies, and provision of dependencies to factory functions as requested.
|
||||
|
||||
Angular injects dependencies using "constructor" injection (the service is passed in via a factory
|
||||
function). Because JavaScript is a dynamically typed language, angular's dependency injection
|
||||
function). Because JavaScript is a dynamically typed language, Angular's dependency injection
|
||||
subsystem cannot use static types to identify service dependencies. For this reason a dependent
|
||||
must explicitly define its dependencies by using the `$inject` property. For example:
|
||||
|
||||
myController.$inject = ['$location'];
|
||||
|
||||
The angular web framework provides a set of services for common operations. Like other core angular
|
||||
variables and identifiers, the built-in services always start with `$` (such as `$xhr` mentioned
|
||||
variables and identifiers, the built-in services always start with `$` (such as `$http` mentioned
|
||||
above). You can also create your own custom services.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.di About Angular Dependency Injection}
|
||||
* {@link di About Angular Dependency Injection}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.service Angular Service API}
|
||||
* {@link api/ng Angular Service API}
|
||||
* {@link api/angular.injector Injector API}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Working With CSS in Angular
|
||||
@description
|
||||
|
||||
|
||||
Angular sets these CSS classes. It is up to your application to provide useful styling.
|
||||
|
||||
# CSS classes used by angular
|
||||
|
||||
* `ng-invalid`, `ng-valid`
|
||||
- **Usage:** angular applies this class to an input widget element if that element's input does
|
||||
not pass validation. (see {@link api/ng.directive:input input} directive).
|
||||
|
||||
* `ng-pristine`, `ng-dirty`
|
||||
- **Usage:** angular {@link api/ng.directive:input input} directive applies `ng-pristine` class
|
||||
to a new input widget element which did not have user interaction. Once the user interacts with
|
||||
the input widget the class is changed to `ng-dirty`.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link forms Angular Forms}
|
||||
@@ -1,52 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Working With CSS in Angular
|
||||
@description
|
||||
|
||||
|
||||
Angular includes built-in CSS classes, which in turn have predefined CSS styles.
|
||||
|
||||
# Built-in CSS classes
|
||||
|
||||
* `ng-exception`
|
||||
|
||||
**Usage:** angular applies this class to a DOM element if that element contains an Expression that
|
||||
threw an exception when evaluated.
|
||||
|
||||
**Styling:** The built-in styling of the ng-exception class displays an error message surrounded
|
||||
by a solid red border, for example:
|
||||
|
||||
<div class="ng-exception">Error message</div>
|
||||
|
||||
You can try to evaluate malformed expressions in {@link dev_guide.expressions expressions} to see
|
||||
the `ng-exception` class' styling.
|
||||
|
||||
* `ng-validation-error`
|
||||
|
||||
**Usage:** angular applies this class to an input widget element if that element's input does not
|
||||
pass validation. Note that you set the validation criteria on the input widget element using the
|
||||
Ng:validate or Ng:required directives.
|
||||
|
||||
**Styling:** The built-in styling of the ng-validation-error class turns the border of the input
|
||||
box red and includes a hovering UI element that includes more details of the validation error. You
|
||||
can see an example in {@link api/angular.widget.@ng:validate ng:validate example}.
|
||||
|
||||
## Overriding Styles for Angular CSS Classes
|
||||
|
||||
To override the styles for angular's built-in CSS classes, you can do any of the following:
|
||||
|
||||
* Download the source code, edit angular.css, and host the source on your own server.
|
||||
* Create a local CSS file, overriding any styles that you'd like, and link to it from your HTML file
|
||||
as you normally would:
|
||||
|
||||
<pre>
|
||||
<link href="yourfile.css" rel="stylesheet" type="text/css">
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link dev_guide.templates.css Working With CSS in Angular}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Data Binding in Angular
|
||||
@description
|
||||
@@ -25,7 +24,7 @@ because first the template (which is the uncompiled HTML along with any addition
|
||||
directives) is compiled on the browser, and second, the compilation step produces a live view. We
|
||||
say live because any changes to the view are immediately reflected in the model, and any changes in
|
||||
the model are propagated to the view. This makes the model always the single-source-of-truth for
|
||||
the application state, greatly simplifying the programing model for the developer. You can think of
|
||||
the application state, greatly simplifying the programming model for the developer. You can think of
|
||||
the view as simply an instant projection of your model.
|
||||
|
||||
Because the view is just a projection of the model, the controller is completely separated from the
|
||||
@@ -35,5 +34,5 @@ isolation without the view and the related DOM/browser dependency.
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.scopes Angular Scopes}
|
||||
* {@link scope Angular Scopes}
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
|
||||
@@ -1,54 +1,51 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Filters: Creating Angular Filters
|
||||
@description
|
||||
|
||||
Writing your own filter is very easy: just define a JavaScript function on the `angular.filter`
|
||||
object.
|
||||
The framework passes in the input value as the first argument to your function. Any filter
|
||||
arguments are passed in as additional function arguments.
|
||||
|
||||
You can use these variables in the function:
|
||||
|
||||
* `this` — The current scope.
|
||||
* `this.$element` — The DOM element containing the binding. The `$element` variable allows the
|
||||
filter to manipulate the DOM.
|
||||
Writing your own filter is very easy: just register a new filter (injectable) factory function with
|
||||
your module. This factory function should return a new filter function which takes the input value
|
||||
as the first argument. Any filter arguments are passed in as additional arguments to the filter
|
||||
function.
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case and assigns color.
|
||||
|
||||
<doc:example>
|
||||
<doc:example module="MyReverseModule">
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
angular.filter('reverse', function(input, uppercase, color) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
// conditional based on optional argument
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
// DOM manipulation using $element
|
||||
if (color) {
|
||||
this.$element.css('color', color);
|
||||
}
|
||||
return out;
|
||||
});
|
||||
<script>
|
||||
angular.module('MyReverseModule', []).
|
||||
filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
// conditional based on optional argument
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
function Ctrl($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}
|
||||
</script>
|
||||
|
||||
<input name="text" type="text" value="hello" /><br>
|
||||
No filter: {{text}}<br>
|
||||
Reverse: {{text|reverse}}<br>
|
||||
Reverse + uppercase: {{text|reverse:true}}<br>
|
||||
Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
|
||||
<div ng-controller="Ctrl">
|
||||
<input ng-model="greeting" type="greeting"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should reverse text', function(){
|
||||
expect(binding('text|reverse')).toEqual('olleh');
|
||||
input('text').enter('ABC');
|
||||
expect(binding('text|reverse')).toEqual('CBA');
|
||||
});
|
||||
it('should reverse greeting', function() {
|
||||
expect(binding('greeting|reverse')).toEqual('olleh');
|
||||
input('greeting').enter('ABC');
|
||||
expect(binding('greeting|reverse')).toEqual('CBA');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
@@ -56,8 +53,8 @@ expect(binding('text|reverse')).toEqual('CBA');
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link compiler Angular HTML Compiler}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.filter Angular Filter API}
|
||||
* {@link api/ng.$filter Angular Filter API}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
@@ -12,10 +11,8 @@ displaying it to the user. You can pass expressions through a chain of filters l
|
||||
|
||||
name | uppercase
|
||||
|
||||
The expression evaluator simply passes the value of name to `angular.filter.uppercase()`.
|
||||
|
||||
In addition to formatting data, filters can also modify the DOM. This allows filters to handle
|
||||
tasks such as conditionally applying CSS styles to filtered output.
|
||||
The expression evaluator simply passes the value of name to
|
||||
{@link api/ng.filter:uppercase uppercase filter}.
|
||||
|
||||
|
||||
## Related Topics
|
||||
@@ -25,4 +22,4 @@ tasks such as conditionally applying CSS styles to filtered output.
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.filter Angular Filter API}
|
||||
* {@link api/ng.$filter Angular Filter API}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Filters: Using Angular Filters
|
||||
@description
|
||||
|
||||
Filters can be part of any {@link api/angular.scope} evaluation but are typically used to format
|
||||
Filters can be part of any {@link api/ng.$rootScope.Scope} evaluation but are typically used to format
|
||||
expressions in bindings in your templates:
|
||||
|
||||
{{ expression | filter }}
|
||||
@@ -38,4 +37,4 @@ argument that specifies how many digits to display to the right of the decimal p
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.filter Angular Filter API}
|
||||
* {@link api/ng.$filter Angular Filter API}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Angular Formatters: Creating Angular Formatters
|
||||
@description
|
||||
|
||||
To create your own formatter, you can simply register a pair of JavaScript functions with
|
||||
`angular.formatter`. One of your functions is used to parse text from the input widget into the
|
||||
data storage format; the other function is used to format stored data into user-readable text.
|
||||
|
||||
The following example demonstrates a "reverse" formatter. Data is stored in uppercase and in
|
||||
reverse, but it is displayed in lower case and non-reversed. When a user edits the data model via
|
||||
the input widget, the input is automatically parsed into the internal data storage format, and when
|
||||
the data changes in the model, it is automatically formatted to the user-readable form for display
|
||||
in the view.
|
||||
|
||||
<pre>
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Angular Formatters
|
||||
@description
|
||||
|
||||
In angular, formatters are responsible for translating user-readable text entered in an {@link
|
||||
api/angular.widget.HTML input widget} to a JavaScript object in the data model that the application
|
||||
can manipulate.
|
||||
|
||||
You can use formatters in a template, and also in JavaScript. Angular provides built-in
|
||||
formatters, and of course you can create your own formatters.
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.formatters.using_formatters Using Angular Formatters}
|
||||
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.formatter Angular Formatter API}
|
||||
@@ -1,9 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Angular Formatters: Using Angular Formatters
|
||||
@description
|
||||
|
||||
The following snippet shows how to use a formatter in a template. The formatter below is
|
||||
`ng:format="reverse"`, added as an attribute to an `<input>` tag.
|
||||
|
||||
<pre>
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Understanding Angular Templates
|
||||
@description
|
||||
@@ -11,34 +10,30 @@ the dynamic view DOM.
|
||||
|
||||
These are the types of angular elements and element attributes you can use in a template:
|
||||
|
||||
* {@link dev_guide.compiler.directives Directive} — An attribute that augments an existing DOM
|
||||
element.
|
||||
* {@link dev_guide.compiler.widgets Widget} — A custom DOM element. An example of a built-in widget
|
||||
is {@link api/angular.widget.@ng:repeat ng:repeat}.
|
||||
* {@link dev_guide.compiler.markup Markup} — Shorthand for a widget or a directive. The double
|
||||
* {@link guide/directive Directive} — An attribute or element that
|
||||
augments an existing DOM element or represents a reusable DOM component - a widget.
|
||||
* {@link api/ng.$interpolate Markup} — The double
|
||||
curly brace notation `{{ }}` to bind expressions to elements is built-in angular markup.
|
||||
* {@link dev_guide.templates.filters Filter} — Formats your data for display to the user.
|
||||
* {@link dev_guide.templates.validators Validator} — Lets you validate user input.
|
||||
* {@link dev_guide.templates.formatters Formatter} — Lets you format the input object into a user
|
||||
readable view.
|
||||
* {@link forms Form controls} — Lets you validate user input.
|
||||
|
||||
Note: In addition to declaring the elements above in templates, you can also access these elements
|
||||
in JavaScript code.
|
||||
|
||||
The following code snippet shows a simple angular template made up of standard HTML tags along with
|
||||
angular {@link dev_guide.compiler.directives directives}, {@link dev_guide.compiler.markup markup},
|
||||
and {@link dev_guide.expressions expressions}:
|
||||
angular {@link guide/directive directives} and curly-brace bindings
|
||||
with {@link expression expressions}:
|
||||
|
||||
<pre>
|
||||
<html>
|
||||
<!-- Body tag augmented with ng:controller directive -->
|
||||
<body ng:controller="MyController">
|
||||
<input name="foo" value="bar">
|
||||
<!-- Button tag with ng:click directive, and
|
||||
<html ng-app>
|
||||
<!-- Body tag augmented with ngController directive -->
|
||||
<body ng-controller="MyController">
|
||||
<input ng-model="foo" value="bar">
|
||||
<!-- Button tag with ng-click directive, and
|
||||
string expression 'buttonText'
|
||||
wrapped in "{{ }}" markup -->
|
||||
<button ng:click="changeFoo()">{{buttonText}}</button>
|
||||
<script src="angular.js" ng:autobind>
|
||||
<button ng-click="changeFoo()">{{buttonText}}</button>
|
||||
<script src="angular.js">
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
@@ -46,8 +41,8 @@ and {@link dev_guide.expressions expressions}:
|
||||
In a simple single-page app, the template consists of HTML, CSS, and angular directives contained
|
||||
in just one HTML file (usually `index.html`). In a more complex app, you can display multiple views
|
||||
within one main page using "partials", which are segments of template located in separate HTML
|
||||
files. You "include" the partials in the main page using the {@link api/angular.service.$route
|
||||
$route} service in conjunction with the {@link api/angular.widget.ng:view ng:view} directive. An
|
||||
files. You "include" the partials in the main page using the {@link api/ng.$route
|
||||
$route} service in conjunction with the {@link api/ng.directive:ngView ngView} directive. An
|
||||
example of this technique is shown in the {@link tutorial/ angular tutorial}, in steps seven and
|
||||
eight.
|
||||
|
||||
@@ -55,8 +50,7 @@ eight.
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
* {@link dev_guide.templates.validators Angular Validators}
|
||||
* {@link forms Angular Forms}
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Validators: Creating Angular Validators
|
||||
@description
|
||||
|
||||
|
||||
To create a custom validator, you simply add your validator code as a method onto the
|
||||
`angular.validator` object and provide input(s) for the validator function. Each input provided is
|
||||
treated as an argument to the validator function. Any additional inputs should be separated by
|
||||
commas.
|
||||
|
||||
The following bit of pseudo-code shows how to set up a custom validator:
|
||||
|
||||
<pre>
|
||||
angular.validator('your_validator', function(input [,additional params]) {
|
||||
[your validation code];
|
||||
if ( [validation succeeds] ) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // No error message specified
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true,
|
||||
there was a problem with that input". If you prefer to provide more information when a validator
|
||||
detects a problem with input, you can specify an error message in the validator that angular will
|
||||
display when the user hovers over the input widget.
|
||||
|
||||
To specify an error message, replace "`return true;`" with an error string, for example:
|
||||
|
||||
return "Must be a value between 1 and 5!";
|
||||
|
||||
Following is a sample UPS Tracking Number validator:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
In this sample validator, we specify a regular expression against which to test the user's input.
|
||||
Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it
|
||||
returns the specified error message ("true").
|
||||
|
||||
Note: you can also access the current angular scope and DOM element objects in your validator
|
||||
functions as follows:
|
||||
|
||||
* `this` === The current angular scope.
|
||||
* `this.$element` === The DOM element that contains the binding. This allows the filter to
|
||||
manipulate the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.validator API Validator Reference}
|
||||
@@ -1,131 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Understanding Angular Validators
|
||||
@description
|
||||
|
||||
Angular validators are attributes that test the validity of different types of user input. Angular
|
||||
provides a set of built-in input validators:
|
||||
|
||||
* {@link api/angular.validator.phone phone number}
|
||||
* {@link api/angular.validator.number number}
|
||||
* {@link api/angular.validator.integer integer}
|
||||
* {@link api/angular.validator.date date}
|
||||
* {@link api/angular.validator.email email address}
|
||||
* {@link api/angular.validator.json JSON}
|
||||
* {@link api/angular.validator.regexp regular expressions}
|
||||
* {@link api/angular.validator.url URLs}
|
||||
* {@link api/angular.validator.asynchronous asynchronous}
|
||||
|
||||
You can also create your own custom validators.
|
||||
|
||||
# Using Angular Validators
|
||||
|
||||
You can use angular validators in HTML template bindings, and in JavaScript:
|
||||
|
||||
* Validators in HTML Template Bindings
|
||||
|
||||
<pre>
|
||||
<input ng:validator="validator_type:parameters" [...]>
|
||||
</pre>
|
||||
|
||||
* Validators in JavaScript
|
||||
|
||||
<pre>
|
||||
angular.validator.[validator_type](parameters)
|
||||
</pre>
|
||||
|
||||
The following example shows how to use the built-in angular integer validator:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Change me: <input type="text" name="number" ng:validate="integer" value="123">
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate the default number string', function() {
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
it('should not validate "foo"', function() {
|
||||
input('number').enter('foo');
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# Creating an Angular Validator
|
||||
|
||||
To create a custom validator, you simply add your validator code as a method onto the
|
||||
`angular.validator` object and provide input(s) for the validator function. Each input provided is
|
||||
treated as an argument to the validator function. Any additional inputs should be separated by
|
||||
commas.
|
||||
|
||||
The following bit of pseudo-code shows how to set up a custom validator:
|
||||
|
||||
<pre>
|
||||
angular.validator('your_validator', function(input [,additional params]) {
|
||||
[your validation code];
|
||||
if ( [validation succeeds] ) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // No error message specified
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true,
|
||||
there was a problem with that input". If you prefer to provide more information when a validator
|
||||
detects a problem with input, you can specify an error message in the validator that angular will
|
||||
display when the user hovers over the input widget.
|
||||
|
||||
To specify an error message, replace "`return true;`" with an error string, for example:
|
||||
|
||||
return "Must be a value between 1 and 5!";
|
||||
|
||||
Following is a sample UPS Tracking Number validator:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
In this sample validator, we specify a regular expression against which to test the user's input.
|
||||
Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it
|
||||
returns the specified error message ("true").
|
||||
|
||||
Note: you can also access the current angular scope and DOM element objects in your validator
|
||||
functions as follows:
|
||||
|
||||
* `this` === The current angular scope.
|
||||
* `this.$element` === The DOM element that contains the binding. This allows the filter to
|
||||
manipulate the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.validator Validator API}
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Unit Testing
|
||||
@description
|
||||
@@ -17,7 +16,7 @@ this may seem obvious it usually is very difficult to be able to call an individ
|
||||
typical project. The reason is that the developers often time mix concerns, and they end up with a
|
||||
piece of code which does everything. It reads the data from XHR, it sorts it and then it
|
||||
manipulates the DOM. With angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR (which you can mock out) and we crated abstraction which
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstraction which
|
||||
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
|
||||
it is easy to write a sort function which sorts some data, so that your test can create a data set,
|
||||
apply the function, and assert that the resulting model is in the correct order. The test does not
|
||||
@@ -43,11 +42,11 @@ on a constructor permanently binds the call site to the type. For example lets s
|
||||
trying to instantiate an `XHR` so that we can get some data from the server.
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
xhr.onreadystatechange = function(){...}
|
||||
xhr.onreadystatechange = function() {...}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
@@ -61,7 +60,7 @@ patching, that is a bad idea for many reasons, which is outside the scope of thi
|
||||
The class above is hard to test since we have to resort to monkey patching:
|
||||
<pre>
|
||||
var oldXHR = XHR;
|
||||
XHR = function MockXHR(){};
|
||||
XHR = function MockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that MockXHR got called with the right arguments
|
||||
@@ -73,8 +72,8 @@ XHR = oldXHR; // if you forget this bad things will happen
|
||||
Another way to approach the problem is look for the service in a well known location.
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
global.xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
@@ -93,8 +92,8 @@ State & Singletons}
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldXHR = glabal.xhr;
|
||||
glabal.xhr = function mockXHR(){};
|
||||
var oldXHR = global.xhr;
|
||||
global.xhr = function mockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
@@ -110,7 +109,7 @@ having the tests replace the services as needed.
|
||||
<pre>
|
||||
function MyClass() {
|
||||
var serviceRegistry = ????;
|
||||
this.doWork = function(){
|
||||
this.doWork = function() {
|
||||
var xhr = serviceRegistry.get('xhr');
|
||||
xhr({
|
||||
method:'...',
|
||||
@@ -127,12 +126,12 @@ there is only one global variable to be reset).
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldServiceLocator = glabal.serviceLocator;
|
||||
glabal.serviceLocator.set('xhr', function mockXHR(){});
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
global.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
global.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -141,7 +140,7 @@ Lastly the dependency can be passed in.
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function(){
|
||||
this.doWork = function() {
|
||||
xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
@@ -165,7 +164,7 @@ myClass.doWork();
|
||||
|
||||
Notice that no global variables were harmed in the writing of this test.
|
||||
|
||||
Angular comes with {@link dev_guide.di dependency-injection} built in which makes the right thing
|
||||
Angular comes with {@link di dependency-injection} built in which makes the right thing
|
||||
easy to do, but you still need to do it if you wish to take advantage of the testability story.
|
||||
|
||||
## Controllers
|
||||
@@ -174,13 +173,13 @@ for your application is mixed in with DOM manipulation, it will be hard to test
|
||||
below:
|
||||
|
||||
<pre>
|
||||
function PasswordController(){
|
||||
function PasswordController() {
|
||||
// get references to DOM elements
|
||||
var msg = $('.ex1 span');
|
||||
var input = $('.ex1 input');
|
||||
var strength;
|
||||
|
||||
this.grade = function(){
|
||||
this.grade = function() {
|
||||
msg.removeClass(strength);
|
||||
var pwd = input.val();
|
||||
password.text(pwd);
|
||||
@@ -219,16 +218,16 @@ In angular the controllers are strictly separated from the DOM manipulation logi
|
||||
a much easier testability story as can be seen in this example:
|
||||
|
||||
<pre>
|
||||
function PasswordCntrl(){
|
||||
this.password = '';
|
||||
this.grade = function(){
|
||||
var size = this.password.length;
|
||||
function PasswordCntrl($scope) {
|
||||
$scope.password = '';
|
||||
$scope.grade = function() {
|
||||
var size = $scope.password.length;
|
||||
if (size > 8) {
|
||||
this.strength = 'strong';
|
||||
$scope.strength = 'strong';
|
||||
} else if (size > 3) {
|
||||
this.strength = 'medium';
|
||||
$scope.strength = 'medium';
|
||||
} else {
|
||||
this.strength = 'weak';
|
||||
$scope.strength = 'weak';
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -248,16 +247,18 @@ that such a test tells a story, rather then asserting random bits which don't se
|
||||
|
||||
|
||||
## Filters
|
||||
{@link api/angular.filter Filters} are functions which transform the data into user readable
|
||||
{@link api/ng.$filter Filters} are functions which transform the data into user readable
|
||||
format. They are important because they remove the formatting responsibility from the application
|
||||
logic, further simplifying the application logic.
|
||||
|
||||
<pre>
|
||||
angular.filter('length', function(text){
|
||||
return (''+(text||'')).length;
|
||||
myModule.filter('length', function() {
|
||||
return function(text){
|
||||
return (''+(text||'')).length;
|
||||
}
|
||||
});
|
||||
|
||||
var length = angular.filter('length');
|
||||
var length = $filter('length');
|
||||
expect(length(null)).toEqual(0);
|
||||
expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Dependency Injection
|
||||
@description
|
||||
|
||||
# Dependency Injection
|
||||
|
||||
Dependency Injection (DI) is a software design pattern that deals with how code gets hold of its
|
||||
dependencies.
|
||||
|
||||
For in-depth discussion about DI, see {@link http://en.wikipedia.org/wiki/Dependency_injection
|
||||
Dependency Injection} at Wikipedia, {@link http://martinfowler.com/articles/injection.html
|
||||
Inversion of Control} by Martin Fowler, or read about DI in your favorite software design pattern
|
||||
book.
|
||||
|
||||
## DI in a nutshell
|
||||
|
||||
There are only three ways how an object or a function can get a hold of its dependencies:
|
||||
|
||||
1. The dependency can be created, typically using the `new` operator.
|
||||
|
||||
2. The dependency can be looked up by referring to a global variable.
|
||||
|
||||
3. The dependency can be passed in to where it is needed.
|
||||
|
||||
|
||||
The first two option of creating or looking up dependencies are not optimal, because they hard
|
||||
code the dependency, making it difficult, if not impossible, to modify the dependencies.
|
||||
This is especially problematic in tests, where it is often desirable to provide mock dependencies
|
||||
for test isolation.
|
||||
|
||||
The third option is the most viable, since it removes the responsibility of locating the
|
||||
dependency from the component. The dependency is simply handed to the component.
|
||||
|
||||
<pre>
|
||||
function SomeClass(greeter) {
|
||||
this.greeter = greeter
|
||||
}
|
||||
|
||||
SomeClass.prototype.doSomething = function(name) {
|
||||
this.greeter.greet(name);
|
||||
}
|
||||
</pre>
|
||||
|
||||
In the above example the `SomeClass` is not concerned with locating the `greeter` dependency, it
|
||||
is simply handed the `greeter` at runtime.
|
||||
|
||||
This is desirable, but it puts the responsibility of getting hold of the dependency onto the
|
||||
code responsible for the construction of `SomeClass`.
|
||||
|
||||
To manage the responsibility of dependency creation, each angular application has an {@link
|
||||
api/angular.injector injector}. The injector is a service locator that is responsible for
|
||||
construction and lookup of dependencies.
|
||||
|
||||
|
||||
Here is an example of using the injector service.
|
||||
<pre>
|
||||
// Provide the wiring information in a module
|
||||
angular.module('myModule', []).
|
||||
|
||||
// Teach the injector how to build a 'greeter'
|
||||
// Notice that greeter itself is dependent on '$window'
|
||||
factory('greeter', function($window) {
|
||||
// This is a factory function, and is responsible for
|
||||
// creating the 'greet' service.
|
||||
return {
|
||||
greet: function(text) {
|
||||
$window.alert(text);
|
||||
}
|
||||
};
|
||||
}).
|
||||
|
||||
// New injector is created from the module.
|
||||
// (This is usually done automatically by angular bootstrap)
|
||||
var injector = angular.injector('myModule');
|
||||
|
||||
// Request any dependency from the injector
|
||||
var greeter = injector.get('greeter');
|
||||
</pre>
|
||||
|
||||
Asking for dependencies solves the issue of hard coding, but it also means that the injector needs
|
||||
to be passed throughout the application. Passing the injector breaks the {@link
|
||||
http://en.wikipedia.org/wiki/Law_of_Demeter Law of Demeter}. To remedy this, we turn the
|
||||
dependency lookup responsibility to the injector by declaring the dependencies as in this example:
|
||||
|
||||
<pre>
|
||||
<!-- Given this HTML -->
|
||||
<div ng-controller="MyController">
|
||||
<button ng-click="sayHello()">Hello</button>
|
||||
</div>
|
||||
</pre>
|
||||
<pre>
|
||||
// And this controller definition
|
||||
function MyController($scope, greeter) {
|
||||
$scope.sayHello = function() {
|
||||
greeter('Hello World');
|
||||
};
|
||||
}
|
||||
|
||||
// The 'ng-controller' directive does this behind the scenes
|
||||
injector.instantiate(MyController);
|
||||
</pre>
|
||||
|
||||
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
|
||||
dependencies of the `MyController` without the controller ever knowing about the injector. This is
|
||||
the best outcome. The application code simply ask for the dependencies it needs, without having to
|
||||
deal with the injector. This setup does not break the Law of Demeter.
|
||||
|
||||
# Dependency Annotation
|
||||
|
||||
How does the injector know what service needs to be injected?
|
||||
|
||||
The application developer needs to provide annotation information, that the injector uses in order
|
||||
to resolve the dependencies. Throughout Angular certain API functions are invoked using the
|
||||
injector, as per the API documentation. The injector needs to know what services to inject into
|
||||
the function. Below are three equivalent ways of annotating your code with service name
|
||||
information. These can be used interchangeably as you see fit and are equivalent.
|
||||
|
||||
# Inferring Dependencies
|
||||
|
||||
The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
||||
are the names of the dependencies.
|
||||
|
||||
<pre>
|
||||
function MyController($scope, greeter) {
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
|
||||
Given a function the injector can infer the names of the service to inject by examining the
|
||||
function declaration and extracting the parameter names. In the above example `$scope`, and
|
||||
`greeter` are two services which need to be injected into the function.
|
||||
|
||||
While straightforward, this method will not work with JavaScript minifiers/obfuscators as they
|
||||
rename the method parameter names. This makes this way of annotating only useful for {@link
|
||||
http://www.pretotyping.org/ pretotyping}, and demo applications.
|
||||
|
||||
# `$inject` Annotation
|
||||
|
||||
To allow the minifers to rename the function parameters and still be able to inject right services
|
||||
the function needs to be annotate with the `$inject` property. The `$inject` property is an array
|
||||
of service names to inject.
|
||||
|
||||
<pre>
|
||||
var MyController = function(renamed$scope, renamedGreeter) {
|
||||
...
|
||||
}
|
||||
MyController.$inject = ['$scope', 'greeter'];
|
||||
</pre>
|
||||
|
||||
Care must be taken that the `$inject` annotation is kept in sync with the actual arguments in the
|
||||
function declaration.
|
||||
|
||||
This method of annotation is useful for controller declarations since it assigns the annotation
|
||||
information with the function.
|
||||
|
||||
# Inline Annotation
|
||||
|
||||
Sometimes using the `$inject` annotation style is not convenient such as when annotating
|
||||
directives.
|
||||
|
||||
For example:
|
||||
<pre>
|
||||
someModule.factory('greeter', function($window) {
|
||||
...;
|
||||
});
|
||||
</pre>
|
||||
|
||||
Results in code bloat due to the need of temporary variable:
|
||||
<pre>
|
||||
var greeterFactory = function(renamed$window) {
|
||||
...;
|
||||
};
|
||||
|
||||
greeterFactory.$inject = ['$window'];
|
||||
|
||||
someModule.factory('greeter', greeterFactory);
|
||||
</pre>
|
||||
|
||||
For this reason the third annotation style is provided as well.
|
||||
<pre>
|
||||
someModule.factory('greeter', ['$window', function(renamed$window) {
|
||||
...;
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
||||
where injection is supported.
|
||||
|
||||
|
||||
# Where can I use DI?
|
||||
|
||||
DI is pervasive throughout Angular. It is typically used in controllers and factory methods.
|
||||
|
||||
## DI in controllers
|
||||
|
||||
Controllers are classes which are responsible for application behavior. Recommended way of
|
||||
declaring controllers is:
|
||||
|
||||
<pre>
|
||||
var MyController = function(dep1, dep2) {
|
||||
...
|
||||
}
|
||||
MyController.$inject = ['dep1', 'dep2'];
|
||||
|
||||
MyController.prototype.aMethod = function() {
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
## Factory methods
|
||||
|
||||
Factory methods are responsible for creating most objects in Angular. Examples are directives,
|
||||
services, and filters. The factory methods are register with the module, and the recommended way
|
||||
of declaring factories is:
|
||||
|
||||
<pre>
|
||||
angualar.module('myModule', []).
|
||||
config(['depProvider', function(depProvider){
|
||||
...
|
||||
}]).
|
||||
factory('serviceId', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
directive('directiveName', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
filter('filterName', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
run(['depService', function(depService) {
|
||||
...
|
||||
}]);
|
||||
</pre>
|
||||
@@ -0,0 +1,690 @@
|
||||
@ngdoc overview
|
||||
@name Directives
|
||||
@description
|
||||
|
||||
Directives are a way to teach HTML new tricks. During DOM compilation directives are matched
|
||||
against the HTML and executed. This allows directives to register behavior, or transform the DOM.
|
||||
|
||||
Angular comes with a built in set of directives which are useful for building web applications but
|
||||
can be extended such that HTML can be turned into a declarative domain specific language (DSL).
|
||||
|
||||
# Invoking directives from HTML
|
||||
|
||||
Directives have camel cased names such as `ngBind`. The directive can be invoked by translating
|
||||
the camel case name into snake case with these special characters `:`, `-`, or `_`. Optionally the
|
||||
directive can be prefixed with `x-`, or `data-` to make it HTML validator compliant. Here is a
|
||||
list of some of the possible directive names: `ng:bind`, `ng-bind`, `ng_bind`, `x-ng-bind` and
|
||||
`data-ng-bind`.
|
||||
|
||||
The directives can be placed in element names, attributes, class names, as well as comments. Here
|
||||
are some equivalent examples of invoking `myDir`. (However, most directives are restricted to
|
||||
attribute only.)
|
||||
|
||||
<pre>
|
||||
<span my-dir="exp"></span>
|
||||
<span class="my-dir: exp;"></span>
|
||||
<my-dir></my-dir>
|
||||
<!-- directive: my-dir exp -->
|
||||
</pre>
|
||||
|
||||
Directives can be invoked in many different ways, but are equivalent in the end result as shown in
|
||||
the following example.
|
||||
|
||||
<doc:example>
|
||||
<doc:source >
|
||||
<script>
|
||||
function Ctrl1($scope) {
|
||||
$scope.name = 'angular';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show off bindings', function() {
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text()).toBe('angular');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# String interpolation
|
||||
|
||||
During the compilation process the {@link api/ng.$compile compiler} matches text and
|
||||
attributes using the {@link api/ng.$interpolate $interpolate} service to see if they
|
||||
contain embedded expressions. These expressions are registered as {@link
|
||||
api/ng.$rootScope.Scope#$watch watches} and will update as part of normal {@link
|
||||
api/ng.$rootScope.Scope#$digest digest} cycle. An example of interpolation is shown
|
||||
here:
|
||||
|
||||
<pre>
|
||||
<img src="img/{{username}}.jpg">Hello {{username}}!</img>
|
||||
</pre>
|
||||
|
||||
# Compilation process, and directive matching
|
||||
|
||||
Compilation of HTML happens in three phases:
|
||||
|
||||
1. First the HTML is parsed into DOM using the standard browser API. This is important to
|
||||
realize because the templates must be parsable HTML. This is in contrast to most templating
|
||||
systems that operate on strings, rather than on DOM elements.
|
||||
|
||||
2. The compilation of the DOM is performed by the call to the {@link api/ng.$compile
|
||||
$compile()} method. The method traverses the DOM and matches the directives. If a match is found
|
||||
it is added to the list of directives associated with the given DOM element. Once all directives
|
||||
for a given DOM element have been identified they are sorted by priority and their `compile()`
|
||||
functions are executed. The directive compile function has a chance to modify the DOM structure
|
||||
and is responsible for producing a `link()` function explained next. The {@link
|
||||
api/ng.$compile $compile()} method returns a combined linking function, which is a
|
||||
collection of all of the linking functions returned from the individual directive compile
|
||||
functions.
|
||||
|
||||
3. Link the template with scope by calling the linking function returned from the previous step.
|
||||
This in turn will call the linking function of the individual directives allowing them to
|
||||
register any listeners on the elements and set up any {@link
|
||||
api/ng.$rootScope.Scope#$watch watches} with the {@link
|
||||
api/ng.$rootScope.Scope scope}. The result of this is a live binding between the
|
||||
scope and the DOM. A change in the scope is reflected in the DOM.
|
||||
|
||||
<pre>
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
|
||||
var html = '<div ng-bind='exp'></div>';
|
||||
|
||||
// Step 1: parse HTML into DOM element
|
||||
var template = angular.element(html);
|
||||
|
||||
// Step 2: compile the template
|
||||
var linkFn = $compile(template);
|
||||
|
||||
// Step 3: link the compiled template with the scope.
|
||||
linkFn(scope);
|
||||
</pre>
|
||||
|
||||
## Reasons behind the compile/link separation
|
||||
|
||||
At this point you may wonder why the compile process is broken down to a compile and link phase.
|
||||
To understand this, let's look at a real world example with repeater:
|
||||
|
||||
<pre>
|
||||
Hello {{user}}, you have these actions:
|
||||
<ul>
|
||||
<li ng-repeat="action in user.actions">
|
||||
{{action.description}}
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
The short answer is that compile and link separation is needed any time a change in model causes
|
||||
a change in DOM structure such as in repeaters.
|
||||
|
||||
When the above example is compiled, the compiler visits every node and looks for directives. The
|
||||
`{{user}}` is an example of an {@link api/ng.$interpolate interpolation} directive. {@link
|
||||
api/ng.directive:ngRepeat ngRepeat} is another directive. But {@link
|
||||
api/ng.directive:ngRepeat ngRepeat} has a dilemma. It needs to be
|
||||
able to quickly stamp out new `li`s for every `action` in `user.actions`. This means that it needs
|
||||
to save a clean copy of the `li` element for cloning purposes and as new `action`s are inserted,
|
||||
the template `li` element needs to be cloned and inserted into `ul`. But cloning the `li` element
|
||||
is not enough. It also needs to compile the `li` so that its directives such as
|
||||
`{{action.descriptions}}` evaluate against the right {@link api/ng.$rootScope.Scope
|
||||
scope}. A naive method would be to simply insert a copy of the `li` element and then compile it.
|
||||
But compiling on every `li` element clone would be slow, since the compilation requires that we
|
||||
traverse the DOM tree and look for directives and execute them. If we put the compilation inside a
|
||||
repeater which needs to unroll 100 items we would quickly run into performance problems.
|
||||
|
||||
The solution is to break the compilation process into two phases; the compile phase where all of
|
||||
the directives are identified and sorted by priority, and a linking phase where any work which
|
||||
links a specific instance of the {@link api/ng.$rootScope.Scope scope} and the specific
|
||||
instance of an `li` is performed.
|
||||
|
||||
{@link api/ng.directive:ngRepeat ngRepeat} works by preventing the
|
||||
compilation process form descending into the `li` element. Instead the {@link
|
||||
api/ng.directive:ngRepeat ngRepeat} directive compiles `li`
|
||||
separately. The result of of the `li` element compilation is a linking function which contains all
|
||||
of the directives contained in the `li` element, ready to be attached to a specific clone of the `li`
|
||||
element. At runtime the {@link api/ng.directive:ngRepeat ngRepeat}
|
||||
watches the expression and as items are added to the array it clones the `li` element, creates a
|
||||
new {@link api/ng.$rootScope.Scope scope} for the cloned `li` element and calls the
|
||||
link function on the cloned `li`.
|
||||
|
||||
Summary:
|
||||
|
||||
* *compile function* - The compile function is relatively rare in directives, since most
|
||||
directives are concerned with working with a specific DOM element instance rather than
|
||||
transforming the template DOM element. Any operation which can be shared among the instance of
|
||||
directives should be moved to the compile function for performance reasons.
|
||||
|
||||
* *link function* - It is rare for the directive not to have a link function. A link function
|
||||
allows the directive to register listeners to the specific cloned DOM element instance as well
|
||||
as to copy content into the DOM from the scope.
|
||||
|
||||
|
||||
# Writing directives (short version)
|
||||
|
||||
In this example we will build a directive that displays the current time.
|
||||
|
||||
<doc:example module="time">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl2($scope) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
}
|
||||
|
||||
angular.module('time', [])
|
||||
// Register the 'myCurrentTime' directive factory method.
|
||||
// We inject $timeout and dateFilter service since the factory method is DI.
|
||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||||
// return the directive link function. (compile function not needed)
|
||||
return function(scope, element, attrs) {
|
||||
var format, // date format
|
||||
timeoutId; // timeoutId, so that we can cancel the time updates
|
||||
|
||||
// used to update the UI
|
||||
function updateTime() {
|
||||
element.text(dateFilter(new Date(), format));
|
||||
}
|
||||
|
||||
// watch the expression, and update the UI on change.
|
||||
scope.$watch(attrs.myCurrentTime, function(value) {
|
||||
format = value;
|
||||
updateTime();
|
||||
});
|
||||
|
||||
// schedule update in one second
|
||||
function updateLater() {
|
||||
// save the timeoutId for canceling
|
||||
timeoutId = $timeout(function() {
|
||||
updateTime(); // update DOM
|
||||
updateLater(); // schedule another update
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
|
||||
updateLater(); // kick off the UI update process.
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div ng-controller="Ctrl2">
|
||||
Date format: <input ng-model="format"> <hr/>
|
||||
Current time is: <span my-current-time="format"></span>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Writing directives (long version)
|
||||
|
||||
An example skeleton of the directive is shown here, for the complete list see below.
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
|
||||
myModule.directive('directiveName', function factory(injectables) {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
template: '<div></div>',
|
||||
templateUrl: 'directive.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'A',
|
||||
scope: false,
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
|
||||
post: function postLink(scope, iElement, iAttrs, controller) { ... }
|
||||
}
|
||||
},
|
||||
link: function postLink(scope, iElement, iAttrs) { ... }
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
</pre>
|
||||
|
||||
In most cases you will not need such fine control and so the above can be simplified. All of the
|
||||
different parts of this skeleton are explained in following sections. In this section we are
|
||||
interested only isomers of this skeleton.
|
||||
|
||||
The first step in simplyfing the code is to rely on the default values. Therefore the above can be
|
||||
simplified as:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
|
||||
myModule.directive('directiveName', function factory(injectables) {
|
||||
var directiveDefinitionObject = {
|
||||
compile: function compile(tElement, tAttrs) {
|
||||
return function postLink(scope, iElement, iAttrs) { ... }
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
</pre>
|
||||
|
||||
Most directives concern themselves only with instances, not with template transformations, allowing
|
||||
further simplification:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
|
||||
myModule.directive('directiveName', function factory(injectables) {
|
||||
return function postLink(scope, iElement, iAttrs) { ... }
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Factory method
|
||||
|
||||
The factory method is responsible for creating the directive. It is invoked only once, when the
|
||||
{@link api/ng.$compile compiler} matches the directive for the first time. You can
|
||||
perform any initialization work here. The method is invoked using the {@link
|
||||
api/AUTO.$injector#invoke $injector.invoke} which
|
||||
makes it injectable following all of the rules of injection annotation.
|
||||
|
||||
## Directive Definition Object
|
||||
|
||||
The directive definition object provides instructions to the {@link api/ng.$compile
|
||||
compiler}. The attributes are:
|
||||
|
||||
* `name` - Name of the current scope. Optional defaults to the name at registration.
|
||||
|
||||
* `priority` - When there are multiple directives defined on a single DOM element, sometimes it
|
||||
is necessary to specify the order in which the directives are applied. The `priority` is used
|
||||
to sort the directives before their `compile` functions get called. Higher `priority` goes
|
||||
first. The order of directives within the same priority is undefined.
|
||||
|
||||
* `terminal` - If set to true then the current `priority` will be the last set of directives
|
||||
which will execute (any directives at the current priority will still execute
|
||||
as the order of execution on same `priority` is undefined).
|
||||
|
||||
* `scope` - If set to:
|
||||
|
||||
* `true` - then a new scope will be created for this directive. If multiple directives on the
|
||||
same element request new scope, only one new scope is created. The new scope rule does not
|
||||
apply for the root of the template since the root of the template always gets a new scope.
|
||||
|
||||
* `{}` (object hash) - then a new 'isolate' scope is created. The 'isolate' scope differs from
|
||||
normal scope in that it does not prototypically inherit from the parent scope. This is useful
|
||||
when creating reusable components, which should not accidentally read or modify data in the
|
||||
parent scope. <br/>
|
||||
The 'isolate' scope takes an object hash which defines a set of local scope properties
|
||||
derived from the parent scope. These local properties are useful for aliasing values for
|
||||
templates. Locals definition is a hash of local scope property to its source:
|
||||
|
||||
* `@` or `@attr` - bind a local scope property to the DOM attribute. The result is always a
|
||||
string since DOM attributes are strings. If no `attr` name is specified then the local name
|
||||
and attribute name are same. Given `<widget my-attr="hello {{name}}">` and widget definition
|
||||
of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
|
||||
the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
|
||||
`localName` property on the widget scope. The `name` is read from the parent scope (not
|
||||
component scope).
|
||||
|
||||
* `=` or `=expression` - set up bi-directional binding between a local scope property and the
|
||||
parent scope property. If no `attr` name is specified then the local name and attribute
|
||||
name are same. Given `<widget my-attr="parentModel">` and widget definition of
|
||||
`scope: { localModel:'=myAttr' }`, then widget scope property `localName` will reflect the
|
||||
value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
|
||||
in `localModel` and any changes in `localModel` will reflect in `parentModel`.
|
||||
|
||||
* `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
|
||||
If no `attr` name is specified then the local name and attribute name are same.
|
||||
Given `<widget my-attr="count = count + value">` and widget definition of
|
||||
`scope: { localFn:'increment()' }`, then isolate scope property `localFn` will point to
|
||||
a function wrapper for the `increment()` expression. Often it's desirable to pass data from
|
||||
the isolate scope via an expression and to the parent scope, this can be done by passing a
|
||||
map of local variable names and values into the expression wrapper fn. For example, if the
|
||||
expression is `increment(amount)` then we can specify the amount value by calling the
|
||||
`localFn` as `localFn({amount: 22})`.
|
||||
|
||||
* `controller` - Controller constructor function. The controller is instantiated before the
|
||||
pre-linking phase and it is shared with other directives if they request it by name (see
|
||||
`require` attribute). This allows the directives to communicate with each other and augment
|
||||
each other behavior. The controller is injectable with the following locals:
|
||||
|
||||
* `$scope` - Current scope associated with the element
|
||||
* `$element` - Current element
|
||||
* `$attrs` - Current attributes obeject for the element
|
||||
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
`function(cloneLinkingFn)`.
|
||||
|
||||
* `require` - Require another controller be passed into current directive linking function. The
|
||||
`require` takes a name of the directive controller to pass in. If no such controller can be
|
||||
found an error is raised. The name can be prefixed with:
|
||||
|
||||
* `?` - Don't raise an error. This makes the require dependency optional.
|
||||
* `^` - Look for the controller on parent elements as well.
|
||||
|
||||
|
||||
* `restrict` - String of subset of `EACM` which restricts the directive to a specific directive
|
||||
declaration style. If omitted directives are allowed on attributes only.
|
||||
|
||||
* `E` - Element name: `<my-directive></my-directive>`
|
||||
* `A` - Attribute: `<div my-directive="exp"></div>`
|
||||
* `C` - Class: `<div class="my-directive: exp;"></div>`
|
||||
* `M` - Comment: `<!-- directive: my-directive exp -->`
|
||||
|
||||
* `template` - replace the current element with the contents of the HTML. The replacement process
|
||||
migrates all of the attributes / classes from the old element to the new one. See Creating
|
||||
Widgets section below for more information.
|
||||
|
||||
* `templateUrl` - Same as `template` but the template is loaded from the specified URL. Because
|
||||
the template loading is asynchronous the compilation/linking is suspended until the template
|
||||
is loaded.
|
||||
|
||||
* `replace` - if set to `true` then the template will replace the current element, rather than
|
||||
append the template to the element.
|
||||
|
||||
* `transclude` - compile the content of the element and make it available to the directive.
|
||||
Typically used with {@link api/ng.directive:ngTransclude
|
||||
ngTransclude}. The advantage of transclusion is that the linking function receives a
|
||||
transclusion function which is pre-bound to the correct scope. In a typical setup the widget
|
||||
creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
|
||||
scope. This makes it possible for the widget to have private state, and the transclusion to
|
||||
be bound to the parent (pre-`isolate`) scope.
|
||||
|
||||
* `true` - transclude the content of the directive.
|
||||
* `'element'` - transclude the whole element including any directives defined at lower priority.
|
||||
|
||||
|
||||
* `compile`: This is the compile function described in the section below.
|
||||
|
||||
* `link`: This is the link function described in the section below. This property is used only
|
||||
if the `compile` property is not defined.
|
||||
|
||||
## Compile function
|
||||
|
||||
<pre>
|
||||
function compile(tElement, tAttrs, transclude) { ... }
|
||||
</pre>
|
||||
|
||||
The compile function deals with transforming the template DOM. Since most directives do not do
|
||||
template transformation, it is not used often. Examples that require compile functions are
|
||||
directives that transform template DOM, such as {@link
|
||||
api/ng.directive:ngRepeat ngRepeat}, or load the contents
|
||||
asynchronously, such as {@link api/ng.directive:ngView ngView}. The
|
||||
compile function takes the following arguments.
|
||||
|
||||
* `tElement` - template element - The element where the directive has been declared. It is
|
||||
safe to do template transformation on the element and child elements only.
|
||||
|
||||
* `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
|
||||
between all directive compile functions. See {@link
|
||||
guide/directive#Attributes Attributes}.
|
||||
|
||||
* `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`.
|
||||
|
||||
NOTE: The template instance and the link instance may not be the same objects if the template has
|
||||
been cloned. For this reason it is not safe in the compile function to do anything other than DOM
|
||||
transformation that applies to all DOM clones. Specifically, DOM listener registration should be
|
||||
done in a linking function rather than in a compile function.
|
||||
|
||||
A compile function can have a return value which can be either a function or an object.
|
||||
|
||||
* returning a function - is equivalent to registering the linking function via the `link` property
|
||||
of the config object when the compile function is empty.
|
||||
|
||||
* returning an object with function(s) registered via `pre` and `post` properties - allows you to
|
||||
control when a linking function should be called during the linking phase. See info about
|
||||
pre-linking and post-linking functions below.
|
||||
|
||||
|
||||
## Linking function
|
||||
|
||||
<pre>
|
||||
function link(scope, iElement, iAttrs, controller) { ... }
|
||||
</pre>
|
||||
|
||||
The link function is responsible for registering DOM listeners as well as updating the DOM. It is
|
||||
executed after the template has been cloned. This is where most of the directive logic will be
|
||||
put.
|
||||
|
||||
* `scope` - {@link api/ng.$rootScope.Scope Scope} - The scope to be used by the
|
||||
directive for registering {@link api/ng.$rootScope.Scope#$watch watches}.
|
||||
|
||||
* `iElement` - instance element - The element where the directive is to be used. It is safe to
|
||||
manipulate the children of the element only in `postLink` function since the children have
|
||||
already been linked.
|
||||
|
||||
* `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
|
||||
between all directive linking functions. See {@link
|
||||
guide/directive#Attributes Attributes}.
|
||||
|
||||
* `controller` - a controller instance - A controller instance if at least one directive on the
|
||||
element defines a controller. The controller is shared among all the directives, which allows
|
||||
the directives to use the controllers as a communication channel.
|
||||
|
||||
|
||||
|
||||
### Pre-linking function
|
||||
|
||||
Executed before the child elements are linked. Not safe to do DOM transformation since the
|
||||
compiler linking function will fail to locate the correct elements for linking.
|
||||
|
||||
### Post-linking function
|
||||
|
||||
Executed after the child elements are linked. Safe to do DOM transformation in here.
|
||||
|
||||
<a name="Attributes"></a>
|
||||
## Attributes
|
||||
|
||||
The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
|
||||
link() or compile() functions - is a way of accessing:
|
||||
|
||||
* *normalized attribute names:* Since a directive such as 'ngBind' can be expressed in many ways
|
||||
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
|
||||
the attributes.
|
||||
|
||||
* *directive inter-communication:* All directives share the same instance of the attributes
|
||||
object which allows the directives to use the attributes object as inter directive
|
||||
communication.
|
||||
|
||||
* *supports interpolation:* Interpolation attributes are assigned to the attribute object
|
||||
allowing other directives to read the interpolated value.
|
||||
|
||||
* *observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
|
||||
that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
|
||||
the only way to easily get the actual value because during the linking phase the interpolation
|
||||
hasn't been evaluated yet and so the value is at this time set to `undefined`.
|
||||
|
||||
<pre>
|
||||
function linkingFn(scope, elm, attrs, ctrl) {
|
||||
// get the attribute value
|
||||
console.log(attrs.ngModel);
|
||||
|
||||
// change the attribute
|
||||
attrs.$set('ngModel', 'new value');
|
||||
|
||||
// observe changes to interpolated attribute
|
||||
attrs.$observe('ngModel', function(value) {
|
||||
console.log('ngModel has changed value to ' + value);
|
||||
});
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
# Understanding Transclusion and Scopes
|
||||
|
||||
It is often desirable to have reusable components. Below is a pseudo code showing how a simplified
|
||||
dialog component may work.
|
||||
|
||||
<pre>
|
||||
<div>
|
||||
<button ng-click="show=true">show</button>
|
||||
<dialog title="Hello {{username}}."
|
||||
visible="show"
|
||||
on-cancel="show = false"
|
||||
on-ok="show = false; doSomething()">
|
||||
Body goes here: {{username}} is {{title}}.
|
||||
</dialog>
|
||||
</pre>
|
||||
|
||||
Clicking on the "show" button will open the dialog. The dialog will have a title, which is
|
||||
data bound to `username`, and it will also have a body which we would like to transclude
|
||||
into the dialog.
|
||||
|
||||
Here is an example of what the template definition for the `dialog` widget may look like.
|
||||
|
||||
<pre>
|
||||
<div ng-show="show()">
|
||||
<h3>{{title}}</h3>
|
||||
<div class="body" ng-transclude></div>
|
||||
<div class="footer">
|
||||
<button ng-click="onOk()">Save changes</button>
|
||||
<button ng-click="onCancel()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
This will not render properly, unless we do some scope magic.
|
||||
|
||||
The first issue we have to solve is that the dialog box template expect `title` to be defined, but
|
||||
the place of instantiation would like to bind to `username`. Furthermore the buttons expect `onOk`
|
||||
as well as `onCancel` functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the `locals` to create local variables which the template
|
||||
expects as follows:
|
||||
|
||||
<pre>
|
||||
scope: {
|
||||
title: 'bind', // set up title to accept data-binding
|
||||
onOk: 'expression', // create a delegate onOk function
|
||||
onCancel: 'expression', // create a delegate onCancel function
|
||||
show: 'accessor' // create a getter/setter function for visibility.
|
||||
}
|
||||
</pre>
|
||||
|
||||
Creating local properties on widget scope creates two problems:
|
||||
|
||||
1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog
|
||||
template will bind to parent scope property. This is unpredictable and undesirable.
|
||||
|
||||
2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the
|
||||
properties which the transclusion needs for data-binding. In our example the `title`
|
||||
property of the widget clobbers the `title` property of the transclusion.
|
||||
|
||||
|
||||
To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
|
||||
isolated scope does not prototypically inherit from the child scope, and therefore we don't have
|
||||
to worry about accidentally clobbering any properties.
|
||||
|
||||
However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
|
||||
isolated scope then it will not be able to bind to anything. For this reason the transcluded scope
|
||||
is a child of the original scope, before the widget created an isolated scope for its local
|
||||
variables. This makes the transcluded and widget isolated scope siblings.
|
||||
|
||||
This may seem as unexpected complexity, but it gives the widget user and developer the least
|
||||
surprise.
|
||||
|
||||
Therefore the final directive definition looks something like this:
|
||||
|
||||
<pre>
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: 'bind', // set up title to accept data-binding
|
||||
onOk: 'expression', // create a delegate onOk function
|
||||
onCancel: 'expression', // create a delegate onCancel function
|
||||
show: 'accessor' // create a getter/setter function for visibility.
|
||||
}
|
||||
</pre>
|
||||
|
||||
# Creating Components
|
||||
|
||||
It is often desirable to replace a single directive with a more complex DOM structure. This
|
||||
allows the directives to become a short hand for reusable components from which applications
|
||||
can be built.
|
||||
|
||||
Following is an example of building a reusable widget.
|
||||
|
||||
|
||||
<doc:example module="zippyModule">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl3($scope) {
|
||||
$scope.title = 'Lorem Ipsum';
|
||||
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
|
||||
}
|
||||
|
||||
angular.module('zippyModule', [])
|
||||
.directive('zippy', function(){
|
||||
return {
|
||||
restrict: 'C',
|
||||
// This HTML will replace the zippy directive.
|
||||
replace: true,
|
||||
transclude: true,
|
||||
scope: { title:'@zippyTitle' },
|
||||
template: '<div>' +
|
||||
'<div class="title">{{title}}</div>' +
|
||||
'<div class="body" ng-transclude></div>' +
|
||||
'</div>',
|
||||
// The linking function will add behavior to the template
|
||||
link: function(scope, element, attrs) {
|
||||
// Title element
|
||||
var title = angular.element(element.children()[0]),
|
||||
// Opened / closed state
|
||||
opened = true;
|
||||
|
||||
// Clicking on title should open/close the zippy
|
||||
title.bind('click', toggle);
|
||||
|
||||
// Toggle the closed/opened state
|
||||
function toggle() {
|
||||
opened = !opened;
|
||||
element.removeClass(opened ? 'closed' : 'opened');
|
||||
element.addClass(opened ? 'opened' : 'closed');
|
||||
}
|
||||
|
||||
// initialize the zippy
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.zippy {
|
||||
border: 1px solid black;
|
||||
display: inline-block;
|
||||
width: 250px;
|
||||
}
|
||||
.zippy.opened > .title:before { content: '▼ '; }
|
||||
.zippy.opened > .body { display: block; }
|
||||
.zippy.closed > .title:before { content: '► '; }
|
||||
.zippy.closed > .body { display: none; }
|
||||
.zippy > .title {
|
||||
background-color: black;
|
||||
color: white;
|
||||
padding: .1em .3em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.zippy > .body {
|
||||
padding: .1em .3em;
|
||||
}
|
||||
</style>
|
||||
<div ng-controller="Ctrl3">
|
||||
Title: <input ng-model="title"> <br>
|
||||
Text: <textarea ng-model="text"></textarea>
|
||||
<hr>
|
||||
<div class="zippy" zippy-title="Details: {{title}}...">{{text}}</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should bind and open / close', function() {
|
||||
input('title').enter('TITLE');
|
||||
input('text').enter('TEXT');
|
||||
expect(element('.title').text()).toEqual('Details: TITLE...');
|
||||
expect(binding('text')).toEqual('TEXT');
|
||||
|
||||
expect(element('.zippy').prop('className')).toMatch(/closed/);
|
||||
element('.zippy > .title').click();
|
||||
expect(element('.zippy').prop('className')).toMatch(/opened/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -0,0 +1,187 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Expressions
|
||||
@description
|
||||
|
||||
Expressions are JavaScript-like code snippets that are usually placed in bindings such as `{{
|
||||
expression }}`. Expressions are processed by {@link api/ng.$parse $parse}
|
||||
service.
|
||||
|
||||
For example, these are all valid expressions in angular:
|
||||
|
||||
* `1+2`
|
||||
* `3*10 | currency`
|
||||
* `user.name`
|
||||
|
||||
|
||||
## Angular Expressions vs. JS Expressions
|
||||
|
||||
It might be tempting to think of angular view expressions as JavaScript expressions, but that is
|
||||
not entirely correct, since angular does not use a JavaScript `eval()` to evaluate expressions.
|
||||
You can think of angular expressions as JavaScript expressions with following differences
|
||||
differences:
|
||||
|
||||
* **Attribute Evaluation:** evaluation of all properties are against the scope, doing the
|
||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||
`window`.
|
||||
|
||||
* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript,
|
||||
where such evaluations generate `NullPointerExceptions`.
|
||||
|
||||
* **No Control Flow Statements:** you cannot do any of the following in angular expression:
|
||||
conditionals, loops, or throw.
|
||||
|
||||
* **Filters:** you can pass result of expression evaluations through filter chains. For example
|
||||
to convert date object into a local specific human-readable format.
|
||||
|
||||
If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a
|
||||
controller method and call the method. If you want to `eval()` an angular expression from
|
||||
JavaScript, use the {@link api/ng.$rootScope.Scope#$eval `$eval()`} method.
|
||||
|
||||
## Example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
1+2={{1+2}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function() {
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
You can try evaluating different expressions here:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Cntl2($scope) {
|
||||
var exprs = $scope.exprs = [];
|
||||
$scope.expr = '3*10|currency';
|
||||
$scope.addExp = function(expr) {
|
||||
exprs.push(expr);
|
||||
};
|
||||
|
||||
$scope.removeExp = function(index) {
|
||||
exprs.splice(index, 1);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Cntl2" class="expressions">
|
||||
Expression:
|
||||
<input type='text' ng-model="expr" size="80"/>
|
||||
<button ng-click="addExp(expr)">Evaluate</button>
|
||||
<ul>
|
||||
<li ng-repeat="expr in exprs">
|
||||
[ <a href="" ng-click="removeExp($index)">X</a> ]
|
||||
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should allow user expression testing', function() {
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Property Evaluation
|
||||
|
||||
Evaluation of all properties takes place against a scope. Unlike JavaScript, where names default
|
||||
to global window properties, angular expressions have to use {@link api/ng.$window
|
||||
`$window`} to refer to the global `window` object. For example, if you want to call `alert()`, which is
|
||||
defined on `window`, in an expression must use `$window.alert()`. This is done intentionally to
|
||||
prevent accidental access to the global state (a common source of subtle bugs).
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Cntl1($window, $scope){
|
||||
$scope.name = 'World';
|
||||
|
||||
$scope.greet = function() {
|
||||
($window.mockWindow || $window).alert('Hello ' + $scope.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="example2" ng-controller="Cntl1">
|
||||
Name: <input ng-model="name" type="text"/>
|
||||
<button ng-click="greet()">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function() {
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
alert: function(text){ alertText = text; }
|
||||
};
|
||||
done();
|
||||
});
|
||||
element(':button:contains(Greet)').click();
|
||||
expect(this.addFuture('alert text', function(done) {
|
||||
done(null, alertText);
|
||||
})).toBe('Hello World');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
## Forgiving
|
||||
|
||||
Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating `a.b.c` throws
|
||||
an exception if `a` is not an object. While this makes sense for a general purpose language, the
|
||||
expression evaluations are primarily used for data binding, which often look like this:
|
||||
|
||||
{{a.b.c}}
|
||||
|
||||
It makes more sense to show nothing than to throw an exception if `a` is undefined (perhaps we are
|
||||
waiting for the server response, and it will become defined soon). If expression evaluation wasn't
|
||||
forgiving we'd have to write bindings that clutter the code, for example: `{{((a||{}).b||{}).c}}`
|
||||
|
||||
Similarly, invoking a function `a.b.c()` on undefined or null simply returns undefined.
|
||||
|
||||
|
||||
## No Control Flow Statements
|
||||
|
||||
You cannot write a control flow statement in an expression. The reason behind this is core to the
|
||||
angular philosophy that application logic should be in controllers, not in the view. If you need a
|
||||
conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.
|
||||
|
||||
|
||||
## Filters
|
||||
|
||||
When presenting data to the user, you might need to convert the data from its raw format to a
|
||||
user-friendly format. For example, you might have a data object that needs to be formatted
|
||||
according to the locale before displaying it to the user. You can pass expressions through a chain
|
||||
of filters like this:
|
||||
|
||||
name | uppercase
|
||||
|
||||
The expression evaluator simply passes the value of name to {@link
|
||||
api/ng.filter:uppercase `uppercase`} filter.
|
||||
|
||||
Chain filters using this syntax:
|
||||
|
||||
value | filter1 | filter2
|
||||
|
||||
You can also pass colon-delimited arguments to filters, for example, to display the number 123
|
||||
with 2 decimal points:
|
||||
|
||||
123 | number:2
|
||||
|
||||
# The $
|
||||
|
||||
You might be wondering, what is the significance of the $ prefix? It is simply a prefix that
|
||||
angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating
|
||||
`a.length()` would return undefined because neither a nor angular define such a property.
|
||||
|
||||
Consider that in a future version of angular we might choose to add a length method, in which case
|
||||
the behavior of the expression would change. Worse yet, you the developer could create a length
|
||||
property and then we would have a collision. This problem exists because angular augments existing
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
so that angular developers and developers who use angular can develop in harmony without collisions.
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
@ngdoc overview
|
||||
@name Forms
|
||||
@description
|
||||
|
||||
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
|
||||
Form is a collection of controls for the purpose of grouping related controls together.
|
||||
|
||||
Form and controls provide validation services, so that the user can be notified of invalid input.
|
||||
This provides a better user experience, because the user gets instant feedback on how to correct the error.
|
||||
Keep in mind that while client-side validation plays an important role in providing good user experience, it can easily be circumvented and thus can not be trusted.
|
||||
Server-side validation is still necessary for a secure application.
|
||||
|
||||
|
||||
# Simple form
|
||||
The key directive in understanding two-way data-binding is {@link api/ng.directive:ngModel ngModel}.
|
||||
The `ngModel` directive provides the two-way data-binding by synchronizing the model to the view, as well as view to the model.
|
||||
In addition it provides {@link api/ng.directive:ngModel.NgModelController API} for other directives to augment its behavior.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="simple-form">
|
||||
Name: <input type="text" ng-model="user.name" /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
<pre>form = {{user | json}}</pre>
|
||||
<pre>master = {{master | json}}</pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
Note that `novalidate` is used to disable browser's native form validation.
|
||||
|
||||
|
||||
|
||||
# Using CSS classes
|
||||
|
||||
To allow styling of form as well as controls, `ngModel` add these CSS classes:
|
||||
|
||||
- `ng-valid`
|
||||
- `ng-invalid`
|
||||
- `ng-pristine`
|
||||
- `ng-dirty`
|
||||
|
||||
Following example uses the CSS to display validity of each form control.
|
||||
In the example both `user.name` and `user.email` are required, but are rendered with red background only when they are dirty.
|
||||
This ensures that the user is not distracted with an error until after interacting with the control, and failing to satisfy its validity.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="css-form">
|
||||
Name:
|
||||
<input type="text" ng-model="user.name" required /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" required /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
.css-form input.ng-invalid.ng-dirty {
|
||||
background-color: #FA787E;
|
||||
}
|
||||
|
||||
.css-form input.ng-valid.ng-dirty {
|
||||
background-color: #78FA89;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
||||
# Binding to form and control state
|
||||
|
||||
A form is in instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
Similarly control is an instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
The control instance can similarly be published into the form instance using the `name` attribute.
|
||||
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.
|
||||
|
||||
This allows us to extend the above example with these features:
|
||||
|
||||
- RESET button is enabled only if form has some changes
|
||||
- SAVE button is enabled only if form has some changes and is valid
|
||||
- custom error messages for `user.email` and `user.agree`
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-controller="Controller">
|
||||
<form name="form" class="css-form" novalidate>
|
||||
Name:
|
||||
<input type="text" ng-model="user.name" name="uName" required /><br />
|
||||
E-mail:
|
||||
<input type="email" ng-model="user.email" name="uEmail" required/><br />
|
||||
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
|
||||
<span ng-show="form.uEmail.$error.required">Tell us your email.</span>
|
||||
<span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
|
||||
</div>
|
||||
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
|
||||
<input type="checkbox" ng-model="user.agree" name="userAgree" required />
|
||||
I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
|
||||
required /><br />
|
||||
<div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>
|
||||
|
||||
<button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
|
||||
<button ng-click="update(user)"
|
||||
ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.isUnchanged = function(user) {
|
||||
return angular.equals(user, $scope.master);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
||||
# Custom Validation
|
||||
|
||||
Angular provides basic implementation for most common html5 {@link api/ng.directive:input input}
|
||||
types: ({@link api/ng.directive:input.text text}, {@link api/ng.directive:input.number number}, {@link api/ng.directive:input.url url}, {@link api/ng.directive:input.email email}, {@link api/ng.directive:input.radio radio}, {@link api/ng.directive:input.checkbox checkbox}), as well as some directives for validation (`required`, `pattern`, `minlength`, `maxlength`, `min`, `max`).
|
||||
|
||||
Defining your own validator can be done by defining your own directive which adds a custom validation function to the `ngModel` {@link api/ng.directive:ngModel.NgModelController controller}.
|
||||
To get a hold of the controller the directive specifies a dependency as shown in the example below.
|
||||
The validation can occur in two places:
|
||||
|
||||
* **Model to View update** -
|
||||
Whenever the bound model changes, all functions in {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters} array are pipe-lined, so that each of these functions has an opportunity to format the value and change validity state of the form control through {@link api/ng.directive:ngModel.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
|
||||
* **View to Model update** -
|
||||
In a similar way, whenever a user interacts with a control it calls {@link api/ng.directive:ngModel.NgModelController#$setViewValue NgModelController#$setViewValue}.
|
||||
This in turn pipelines all functions in {@link api/ng.directive:ngModel.NgModelController#$parsers NgModelController#$parsers} array, so that each of these functions has an opportunity to convert the value and change validity state of the form control through {@link api/ng.directive:ngModel.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
|
||||
In the following example we create two directives.
|
||||
|
||||
* The first one is `integer` and it validates whether the input is a valid integer.
|
||||
For example `1.23` is an invalid value, since it contains a fraction.
|
||||
Note, that we unshift the array instead of pushing.
|
||||
This is because we want to be first parser and consume the control string value, as we need to execute the validation function before a conversion to number occurs.
|
||||
|
||||
* The second directive is a `smart-float`.
|
||||
It parses both `1.2` and `1,2` into a valid float number `1.2`.
|
||||
Note that, we can't use input type `number` here as HTML5 browsers would not allow the user to type what it would consider an invalid number such as `1,2`.
|
||||
|
||||
|
||||
<doc:example module="form-example1">
|
||||
<doc:source>
|
||||
<div ng-controller="Controller">
|
||||
<form name="form" class="css-form" novalidate>
|
||||
<div>
|
||||
Size (integer 0 - 10):
|
||||
<input type="number" ng-model="size" name="size"
|
||||
min="0" max="10" integer />{{size}}<br />
|
||||
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
|
||||
<span ng-show="form.size.$error.min || form.size.$error.max">
|
||||
The value must be in range 0 to 10!</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Length (float):
|
||||
<input type="text" ng-model="length" name="length" smart-float />
|
||||
{{length}}<br />
|
||||
<span ng-show="form.length.$error.float">
|
||||
This is not a valid float number!</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var app = angular.module('form-example1', []);
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
app.directive('integer', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (INTEGER_REGEXP.test(viewValue)) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('integer', true);
|
||||
return viewValue;
|
||||
} else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('integer', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
|
||||
app.directive('smartFloat', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (FLOAT_REGEXP.test(viewValue)) {
|
||||
ctrl.$setValidity('float', true);
|
||||
return parseFloat(viewValue.replace(',', '.'));
|
||||
} else {
|
||||
ctrl.$setValidity('float', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Implementing custom form control (using `ngModel`)
|
||||
Angular implements all of the basic HTML form controls ({@link api/ng.directive:input input}, {@link api/ng.directive:select select}, {@link api/ng.directive:textarea textarea}), which should be sufficient for most cases.
|
||||
However, if you need more flexibility, you can write your own form control as a directive.
|
||||
|
||||
In order for custom control to work with `ngModel` and to achieve two-way data-binding it needs to:
|
||||
|
||||
- implement `render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- call `$setViewValue` method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.
|
||||
|
||||
See {@link guide/directive $compileProvider.directive} for more info.
|
||||
|
||||
The following example shows how to add two-way data-binding to contentEditable elements.
|
||||
|
||||
<doc:example module="form-example2">
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.module('form-example2', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// view -> model
|
||||
elm.bind('blur', function() {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
});
|
||||
|
||||
// model -> view
|
||||
ctrl.$render = function(value) {
|
||||
elm.html(value);
|
||||
};
|
||||
|
||||
// load init value from DOM
|
||||
ctrl.$setViewValue(elm.html());
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
|
||||
<pre>model = {{content}}</pre>
|
||||
|
||||
<style type="text/css">
|
||||
div[contentEditable] {
|
||||
cursor: pointer;
|
||||
background-color: #D0D0D0;
|
||||
}
|
||||
</style>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
@@ -0,0 +1,124 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: i18n and l10n
|
||||
@description
|
||||
|
||||
# I18n and L10n in AngularJS
|
||||
|
||||
**What is i18n and l10n?**
|
||||
|
||||
Internationalization, abbreviated i18n, is the process of developing products in such a way that
|
||||
they can be localized for languages and cultures easily. Localization, abbreviated l10n, is the
|
||||
process of adapting applications and text to enable their usability in a particular cultural or
|
||||
linguistic market. For application developers, internationalizing an application means abstracting
|
||||
all of the strings and other locale-specific bits (such as date or currency formats) out of the
|
||||
application. Localizing an application means providing translations and localized formats for the
|
||||
abstracted bits.
|
||||
|
||||
**What level of support for i18n/l10n is currently in Angular?**
|
||||
|
||||
Currently, Angular supports i18n/l10n for {@link
|
||||
http://docs.angularjs.org/#!/api/ng.filter:date datetime}, {@link
|
||||
http://docs.angularjs.org/#!/api/ng.filter:number number} and {@link
|
||||
http://docs.angularjs.org/#!/api/ng.filter:currency currency} filters.
|
||||
|
||||
Additionally, Angular supports localizable pluralization support provided by the {@link
|
||||
api/ng.directive:ngPluralize ngPluralize directive}.
|
||||
|
||||
All localizable Angular components depend on locale-specific rule sets managed by the {@link
|
||||
api/ng.$locale $locale service}.
|
||||
|
||||
For readers who want to jump straight into examples, we have a few web pages that showcase how to
|
||||
use Angular filters with various locale rule sets. You can find these examples either on {@link
|
||||
https://github.com/angular/angular.js/tree/master/i18n/e2e Github} or in the i18n/e2e folder of
|
||||
Angular development package.
|
||||
|
||||
**What is a locale id?**
|
||||
|
||||
A locale is a specific geographical, political, or cultural region. The most commonly used locale
|
||||
ID consists of two parts: language code and country code. For example, en-US, en-AU, zh-CN are all
|
||||
valid locale IDs that have both language codes and country codes. Because specifying a country code
|
||||
in locale ID is optional, locale IDs such as en, zh, and sk are also valid. See the {@link
|
||||
http://userguide.icu-project.org/locale ICU } website for more information about using locale IDs.
|
||||
|
||||
**Supported locales in Angular**
|
||||
Angular separates number and datetime format rule sets into different files, each file for a
|
||||
particular locale. You can find a list of currently supported locales {@link
|
||||
https://github.com/angular/angular.js/tree/master/i18n/locale here}
|
||||
# Providing locale rules to Angular
|
||||
|
||||
There are two approaches to providing locale rules to Angular:
|
||||
|
||||
**1. Pre-bundled rule sets**
|
||||
|
||||
You can pre-bundle the desired locale file with Angular by concatenating the content of the
|
||||
locale-specific file to the end of `angular.js` or `angular.min.js` file.
|
||||
|
||||
For example on *nix, to create a an angular.js file that contains localization rules for german
|
||||
locale, you can do the following:
|
||||
|
||||
`cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js`
|
||||
|
||||
When the application containing `angular_de-ge.js` script instead of the generic angular.js script
|
||||
starts, Angular is automatically pre-configured with localization rules for the german locale.
|
||||
|
||||
**2. Including locale js script in index.html page**
|
||||
|
||||
You can also include the locale specific js file in the index.html page. For example, if one client
|
||||
requires German locale, you would serve index_de-ge.html which will look something like this:
|
||||
|
||||
<pre>
|
||||
<html ng-app>
|
||||
<head>
|
||||
….
|
||||
<script src="angular.js"></script>
|
||||
<script src="i18n/angular-locale_de-ge.js"></script>
|
||||
….
|
||||
</head>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
**Comparison of the two approaches**
|
||||
Both approaches described above requires you to prepare different index.html pages or js files for
|
||||
each locale that your app may be localized into. You also need to configure your server to serve
|
||||
the correct file that correspond to the desired locale.
|
||||
|
||||
However, the second approach (Including locale js script in index.html page) is likely to be slower
|
||||
because an extra script needs to be loaded.
|
||||
|
||||
|
||||
# "Gotchas"
|
||||
|
||||
**Currency symbol "gotcha"**
|
||||
|
||||
Angular's {@link http://docs.angularjs.org/#!/api/ng.filter:currency currency filter} allows
|
||||
you to use the default currency symbol from the {@link api/ng.$locale locale service},
|
||||
or you can provide the filter with a custom currency symbol. If your app will be used only in one
|
||||
locale, it is fine to rely on the default currency symbol. However, if you anticipate that viewers
|
||||
in other locales might use your app, you should provide your own currency symbol to make sure the
|
||||
actual value is understood.
|
||||
|
||||
For example, if you want to display account balance of 1000 dollars with the following binding
|
||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
|
||||
In this case, you need to override the default currency symbol by providing the {@link
|
||||
http://docs.angularjs.org/#!/api/ng.filter:currency currency filter} with a currency symbol as
|
||||
a parameter when you configure the filter, for example, {{ 1000 | currency:"USD$"}}. This way,
|
||||
Angular will always show a balance of 'USD$1000' and disregard any locale changes.
|
||||
|
||||
**Translation length "gotcha"**
|
||||
|
||||
Keep in mind that translated strings/datetime formats can vary greatly in length. For example,
|
||||
`June 3, 1977` will be translated to Spanish as `3 de junio de 1977`. There are bound to be other
|
||||
more extreme cases. Hence, when internationalizing your apps, you need to apply CSS rules
|
||||
accordingly and do thorough testing to make sure UI components do not overlap.
|
||||
|
||||
|
||||
**Timezones**
|
||||
|
||||
Keep in mind that Angular datetime filter uses the time zone settings of the browser. So the same
|
||||
application will show different time information depending on the time zone settings of the
|
||||
computer that the application is running on. Neither Javascript nor Angular currently supports
|
||||
displaying the date with a timezone specified by the developer.
|
||||
@@ -0,0 +1,166 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Internet Explorer Compatibility
|
||||
@description
|
||||
|
||||
# Overview
|
||||
|
||||
This document describes the Internet Explorer (IE) idiosyncrasies when dealing with custom HTML
|
||||
attributes and tags. Read this document if you are planning on deploying your angular application
|
||||
on IE v8.0 or earlier.
|
||||
|
||||
# Short Version
|
||||
|
||||
To make your angular application work on IE please make sure that:
|
||||
|
||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
|
||||
2. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
`<div ng-view>` instead), or
|
||||
|
||||
3. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
The **important** parts are:
|
||||
|
||||
* `xmlns:ng` - *namespace* - you need one namespace for each custom tag you are planning on
|
||||
using.
|
||||
|
||||
* `document.createElement(yourTagName)` - *creation of custom tag names* - Since this is an
|
||||
issue only for older version of IE you need to load it conditionally. For each tag which does
|
||||
not have namespace and which is not defined in HTML you need to pre-declare it to make IE
|
||||
happy.
|
||||
|
||||
|
||||
# Long Version
|
||||
|
||||
IE has issues with element tag names which are not standard HTML tag names. These fall into two
|
||||
categories, and each category has its own fix.
|
||||
|
||||
* If the tag name starts with `my:` prefix than it is considered an XML namespace and must
|
||||
have corresponding namespace declaration on `<html xmlns:my="ignored">`
|
||||
|
||||
* If the tag has no `:` but it is not a standard HTML tag, then it must be pre-created using
|
||||
`document.createElement('my-tag')`
|
||||
|
||||
* If you are planning on styling the custom tag with CSS selectors, then it must be
|
||||
pre-created using `document.createElement('my-tag')` regardless of XML namespace.
|
||||
|
||||
|
||||
## The Good News
|
||||
|
||||
The good news is that these restrictions only apply to element tag names, and not to element
|
||||
attribute names. So this requires no special handling in IE: `<div my-tag your:tag></div>`.
|
||||
|
||||
|
||||
## What happens if I fail to do this?
|
||||
|
||||
Suppose you have HTML with unknown tag `mytag` (this could also be `my:tag` or `my-tag` with same
|
||||
result):
|
||||
|
||||
<pre>
|
||||
<html>
|
||||
<body>
|
||||
<mytag>some text</mytag>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
It should parse into the following DOM:
|
||||
|
||||
<pre>
|
||||
#document
|
||||
+- HTML
|
||||
+- BODY
|
||||
+- mytag
|
||||
+- #text: some text
|
||||
</pre>
|
||||
|
||||
The expected behavior is that the `BODY` element has a child element `mytag`, which in turn has
|
||||
the text `some text`.
|
||||
|
||||
But this is not what IE does (if the above fixes are not included):
|
||||
|
||||
<pre>
|
||||
#document
|
||||
+- HTML
|
||||
+- BODY
|
||||
+- mytag
|
||||
+- #text: some text
|
||||
+- /mytag
|
||||
</pre>
|
||||
|
||||
In IE, the behavior is that the `BODY` element has three children:
|
||||
|
||||
1. A self closing `mytag`. Example of self closing tag is `<br/>`. The trailing `/` is optional,
|
||||
but the `<br>` tag is not allowed to have any children, and browsers consider `<br>some
|
||||
text</br>` as three siblings not a `<br>` with `some text` as child.
|
||||
|
||||
2. A text node with `some text`. This should have been a child of `mytag` above, not a sibling.
|
||||
|
||||
3. A corrupt self closing `/mytag`. This is corrupt since element names are not allowed to have
|
||||
the `/` character. Furthermore this closing element should not be part of the DOM since it is
|
||||
only used to delineate the structure of the DOM.
|
||||
|
||||
|
||||
## CSS Styling of Custom Tag Names
|
||||
|
||||
To make CSS selectors work with custom elements, the custom element name must be pre-created with
|
||||
`document.createElement('my-tag')` regardless of XML namespace.
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="needed for ng: namespace">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
// needed to make ng-include parse properly
|
||||
document.createElement('ng-include');
|
||||
|
||||
// needed to enable CSS reference
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
ng\\:view {
|
||||
display: block;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
ng-include {
|
||||
display: block;
|
||||
border: 1px solid blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ng:view></ng:view>
|
||||
<ng-include></ng-include>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide
|
||||
@description
|
||||
|
||||
Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
|
||||
Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
|
||||
to develop web apps, you've come to the right place.
|
||||
|
||||
If you are completely or relatively unfamiliar with angular, you may want to check out one or both
|
||||
@@ -11,51 +10,3 @@ of the following documents before returning here to the Developer Guide:
|
||||
|
||||
* {@link misc/started Getting Started}
|
||||
* {@link tutorial/index Angular Tutorial}
|
||||
|
||||
<hr>
|
||||
|
||||
## {@link dev_guide.overview Overview of Angular}
|
||||
|
||||
## {@link dev_guide.bootstrap Initializing Angular}
|
||||
|
||||
* {@link dev_guide.bootstrap.auto_bootstrap Understanding Automatic Initialization}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Understanding Manual Initialization}
|
||||
|
||||
## {@link dev_guide.mvc About MVC in Angular}
|
||||
|
||||
* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
|
||||
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
|
||||
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
|
||||
|
||||
## {@link dev_guide.scopes Angular Scope Objects}
|
||||
|
||||
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects}
|
||||
* {@link dev_guide.scopes.working_scopes Working With Angular Scopes}
|
||||
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
|
||||
* {@link dev_guide.scopes.updating_scopes Updating Scope Properties}
|
||||
|
||||
## {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
* {@link dev_guide.compiler.directives Understanding Angular Directives}
|
||||
* {@link dev_guide.compiler.widgets Understanding Angular Widgets}
|
||||
* {@link dev_guide.compiler.directives_widgets Comparing Directives and Widgets}
|
||||
* {@link dev_guide.compiler.markup Understanding Angular Markup}
|
||||
|
||||
## {@link dev_guide.templates Angular Templates}
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Understanding Angular Formatters}
|
||||
* {@link dev_guide.templates.validators Understanding Angular Validators}
|
||||
|
||||
## {@link dev_guide.services Angular Services}
|
||||
|
||||
* {@link dev_guide.services.understanding_services Understanding Angular Services}
|
||||
* {@link dev_guide.services.creating_services Creating Angular Services}
|
||||
* {@link dev_guide.services.registering_services Registering Angular Services}
|
||||
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
|
||||
* {@link dev_guide.services.testing_services Testing Angular Services}
|
||||
|
||||
## {@link dev_guide.di About Dependency Injection}
|
||||
|
||||
* {@link dev_guide.di.understanding_di Understanding DI in Angular}
|
||||
* {@link dev_guide.di.using_di_controllers Using DI in Controllers}
|
||||
|
||||
+11
-12
@@ -1,19 +1,18 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Introduction
|
||||
@description
|
||||
|
||||
Angular is pure client-side technology, written entirely in JavaScript. It works with the
|
||||
long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of web
|
||||
apps easier and faster than ever before.
|
||||
long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of
|
||||
web apps easier and faster than ever before.
|
||||
|
||||
One important way that angular simplifies web development is by increasing the level of abstraction
|
||||
between the developer and most low-level web app development tasks. Angular automatically takes
|
||||
care of many of these tasks, including:
|
||||
|
||||
* DOM Manipulation
|
||||
* Setting Up Listeners and Notifiers
|
||||
* Input Validation
|
||||
* DOM Manipulation
|
||||
* Setting Up Listeners and Notifiers
|
||||
* Input Validation
|
||||
|
||||
Because angular handles much of the work involved in these tasks, developers can concentrate more
|
||||
on application logic and less on repetitive, error-prone, lower-level coding.
|
||||
@@ -21,12 +20,12 @@ on application logic and less on repetitive, error-prone, lower-level coding.
|
||||
At the same time that angular simplifies the development of web apps, it brings relatively
|
||||
sophisticated techniques to the client-side, including:
|
||||
|
||||
* Separation of data, application logic, and presentation components
|
||||
* Data Binding between data and presentation components
|
||||
* Services (common web app operations, implemented as substitutable objects)
|
||||
* Dependency Injection (used primarily for wiring together services)
|
||||
* An extensible HTML compiler (written entirely in JavaScript)
|
||||
* Ease of Testing
|
||||
* Separation of data, application logic, and presentation components
|
||||
* Data Binding between data and presentation components
|
||||
* Services (common web app operations, implemented as substitutable objects)
|
||||
* Dependency Injection (used primarily for wiring together services)
|
||||
* An extensible HTML compiler (written entirely in JavaScript)
|
||||
* Ease of Testing
|
||||
|
||||
These techniques have been for the most part absent from the client-side for far too long.
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Modules
|
||||
@description
|
||||
|
||||
# What is a Module?
|
||||
|
||||
Most applications have a main method which instantiates, wires, and bootstraps the application.
|
||||
Angular apps don't have a main method, instead modules serve the purpose of declaratively
|
||||
specifying how an application should be bootstrapped. There are several advantages to this
|
||||
approach:
|
||||
|
||||
* The process is more declarative which is easier to understand
|
||||
* In unit-testing there is no need to load all modules, which may aid in writing unit-tests.
|
||||
* Additional modules can be loaded in scenario tests, which can override some of the
|
||||
configuration and help end-to-end test the application
|
||||
* Third party code can be packaged as reusable modules.
|
||||
* The modules can be loaded in any/parallel order (due to delayed nature of module execution).
|
||||
|
||||
|
||||
# The Basics
|
||||
|
||||
Ok, I'm in a hurry. How do I get a Hello World module working?
|
||||
|
||||
Important things to notice:
|
||||
|
||||
* {@link api/angular.Module Module} API
|
||||
* Notice the reference to the `myApp` module in the `<html ng-app="myApp">`, it is what
|
||||
bootstraps the app using your module.
|
||||
|
||||
<doc:example module='myApp'>
|
||||
<doc:source>
|
||||
<script>
|
||||
// declare a module
|
||||
var simpleAppModule = angular.module('myApp', []);
|
||||
|
||||
// configure the module.
|
||||
// in this example we will create a greeting filter
|
||||
myAppModule.filter('greet', function() {
|
||||
return function(name) {
|
||||
return 'Hello, ' + name + '!';
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
<div>
|
||||
{{ 'World' | greet }}
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
||||
# Recommended Setup
|
||||
|
||||
While the example above is simple, it will not scale to large applications. Instead we recommend
|
||||
that you break your application to multiple modules like this:
|
||||
|
||||
* A service module, for service declaration
|
||||
* A directive module, for directive declaration
|
||||
* A filter module, for filter declaration
|
||||
* And an application level module which depends on the above modules, and which has
|
||||
initialization code.
|
||||
|
||||
The reason for this breakup is that in your tests, it is often necessary to ignore the
|
||||
initialization code, which tends to be difficult to test. By putting it into a separate module it
|
||||
can be easily ignored in tests. The tests can also be more focused by only loading the modules
|
||||
that are relevant to tests.
|
||||
|
||||
The above is only a suggestion, so feel free to tailor it to your needs.
|
||||
|
||||
<doc:example module='xmpl'>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.module('xmpl.service', []).
|
||||
value('greeter', {
|
||||
salutation: 'Hello',
|
||||
localize: function(localization) {
|
||||
this.salutation = localization.salutation;
|
||||
},
|
||||
greet: function(name) {
|
||||
return this.salutation + ' ' + name + '!';
|
||||
}
|
||||
}).
|
||||
value('user', {
|
||||
load: function(name) {
|
||||
this.name = name;
|
||||
}
|
||||
});
|
||||
|
||||
angular.module('xmpl.directive', []);
|
||||
|
||||
angular.module('xmpl.filter', []);
|
||||
|
||||
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']).
|
||||
run(function(greeter, user) {
|
||||
// This is effectively part of the main method initialization code
|
||||
greeter.localize({
|
||||
salutation: 'Bonjour'
|
||||
});
|
||||
user.load('World');
|
||||
})
|
||||
|
||||
|
||||
// A Controller for your app
|
||||
var XmplController = function($scope, greeter, user) {
|
||||
$scope.greeting = greeter.greet(user.name);
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="XmplController">
|
||||
{{ greeting }}!
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
||||
# Module Loading & Dependencies
|
||||
|
||||
A module is a collection of configuration and run blocks which get applied to the application
|
||||
during the bootstrap process. In its simplest form the module consist of collection of two kinds
|
||||
of blocks:
|
||||
|
||||
1. **Configuration blocks** - get executed during the provider registrations and configuration
|
||||
phase. Only providers and constants can be injected into configuration blocks. This is to
|
||||
prevent accidental instantiation of services before they have been fully configured.
|
||||
2. **Run blocks** - get executed after the injector is created and are used to kickstart the
|
||||
application. Only instances and constants can be injected into run blocks. This is to prevent
|
||||
further system configuration during application run time.
|
||||
|
||||
<pre>
|
||||
angular.module('myModule', []).
|
||||
config(function(injectables) { // provider-injector
|
||||
// This is an example of config block.
|
||||
// You can have as many of these as you want.
|
||||
// You can only inject Providers (not instances)
|
||||
// into the config blocks.
|
||||
}).
|
||||
run(function(injectables) { // instance-injector
|
||||
// This is an example of a run block.
|
||||
// You can have as many of these as you want.
|
||||
// You can only inject instances (not Providers)
|
||||
// into the run blocks
|
||||
});
|
||||
</pre>
|
||||
|
||||
## Configuration Blocks
|
||||
|
||||
There are some convenience methods on the module which are equivalent to the config block. For
|
||||
example:
|
||||
|
||||
<pre>
|
||||
angular.module('myModule', []).
|
||||
value('a', 123).
|
||||
factory('a', function() { return 123; }).
|
||||
directive('directiveName', ...).
|
||||
filter('filterName', ...);
|
||||
|
||||
// is same as
|
||||
|
||||
angular.module('myModule', []).
|
||||
config(function($provide, $compileProvider, $filterProvider) {
|
||||
$provide.value('a', 123)
|
||||
$provide.factory('a', function() { return 123; })
|
||||
$compileProvider.directive('directiveName', ...).
|
||||
$filterProvider.register('filterName', ...);
|
||||
});
|
||||
</pre>
|
||||
|
||||
The configuration blocks get applied in the order in which they are registered. The only exception
|
||||
to it are constant definitions, which are placed at the beginning of all configuration blocks.
|
||||
|
||||
## Run Blocks
|
||||
|
||||
Run blocks are the closest thing in Angular to the main method. A run block is the code which
|
||||
needs to run to kickstart the application. It is executed after all of the service have been
|
||||
configured and the injector has been created. Run blocks typically contain code which is hard
|
||||
to unit-test, and for this reason should be declared in isolated modules, so that they can be
|
||||
ignored in the unit-tests.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Modules can list other modules as their dependencies. Depending on a module implies that required
|
||||
module needs to be loaded before the requiring module is loaded. In other words the configuration
|
||||
blocks of the required modules execute before the configuration blocks or the requiring module.
|
||||
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
|
||||
modules require it.
|
||||
|
||||
## Asynchronous Loading
|
||||
|
||||
Modules are a way of managing $injector configuration, and have nothing to do with loading of
|
||||
scripts into a VM. There are existing projects which deal with script loading, which may be used
|
||||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||||
|
||||
|
||||
# Unit Testing
|
||||
|
||||
In its simplest form a unit-test is a way of instantiating a subset of the application in test and
|
||||
then applying a stimulus to it. It is important to realize that each module can only be loaded
|
||||
once per injector. Typically an app has only one injector. But in tests, each test has its own
|
||||
injector, which means that the modules are loaded multiple times per VM. Properly structured
|
||||
modules can help with unit testing, as in this example:
|
||||
|
||||
In all of these examples we are going to assume this module definition:
|
||||
<pre>
|
||||
angular.module('greetMod', []).
|
||||
|
||||
factory('alert', function($window) {
|
||||
return function(text) {
|
||||
$window.alert(text);
|
||||
}
|
||||
}).
|
||||
|
||||
value('salutation', 'Hello').
|
||||
|
||||
factory('greet', function(alert, salutation) {
|
||||
return function(name) {
|
||||
alert(salutation + ' ' + name + '!');
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
Let's write some tests:
|
||||
<pre>
|
||||
describe('myApp', function() {
|
||||
// load the application relevant modules then load a special
|
||||
// test module which overrides the $window with mock version,
|
||||
// so that calling window.alert() will not block the test
|
||||
// runner with a real alert box. This is an example of overriding
|
||||
// configuration information in tests.
|
||||
beforeEach(module('greetMod', function($provide) {
|
||||
$provide.value('$window', {
|
||||
alert: jasmine.createSpy('alert')
|
||||
});
|
||||
}));
|
||||
|
||||
// The inject() will create the injector and inject the greet and
|
||||
// $window into the tests. The test need not concern itself with
|
||||
// wiring of the application, only with testing it.
|
||||
it('should alert on $window', inject(function(greet, $window) {
|
||||
greet('World');
|
||||
expect($window.alert).toHaveBeenCalledWith('Hello World!');
|
||||
}));
|
||||
|
||||
// this is another way of overriding configuration in the
|
||||
// tests using an inline module and inject methods.
|
||||
it('should alert using the alert service', function() {
|
||||
var alertSpy = jasmine.createSpy('alert');
|
||||
module(function($provide) {
|
||||
$provide.value('alert', alertSpy);
|
||||
});
|
||||
inject(function(greet) {
|
||||
greet('World');
|
||||
expect(alertSpy).toHaveBeenCalledWith('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
@@ -0,0 +1,207 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Overview
|
||||
@description
|
||||
|
||||
|
||||
# What Is Angular?
|
||||
|
||||
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
|
||||
language and lets you extend HTML's syntax to express your application's components clearly and
|
||||
succinctly. Out of the box, it eliminates much of the code you currently write through data
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser making it an
|
||||
ideal partner with any server technology.
|
||||
|
||||
Angular is what HTML would have been had it been designed for applications. HTML is a great
|
||||
declarative language for static documents. It does not contain much in the way of creating
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do, so
|
||||
that I trick the browser in to doing what I want.*
|
||||
|
||||
Impedance mismatch between dynamic applications and static documents are often solved as:
|
||||
|
||||
* **library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
in charge and it calls into the library when it sees fit. E.g., `jQuery`.
|
||||
* **frameworks** - a particular implementation of a web application, where your code fills in
|
||||
the details. The framework is in charge and it calls into your code when it needs something
|
||||
app specific. E.g., `knockout`, `sproutcore`, etc.
|
||||
|
||||
|
||||
Angular takes another approach. It attempts to minimize the impedance mismatch between document
|
||||
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
|
||||
browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
* Data binding as in `{{}}`.
|
||||
* DOM control structures for repeating/hiding DOM fragments.
|
||||
* Support for forms and form validation.
|
||||
* Attaching code-behind to DOM elements.
|
||||
* Grouping of HTML into reusable components.
|
||||
|
||||
|
||||
|
||||
## End-to-end solution
|
||||
|
||||
Angular tries to be an end-to-end solution, when building a web application. This means it is
|
||||
not a single piece in an overall puzzle of building a web application, but an end-to-end solution.
|
||||
This makes Angular opinionated about how a CRUD application should be built. But while it is
|
||||
opinionated, it also tries to make sure that its opinion is just a starting point, which you can
|
||||
easily change. Angular comes with the following out-of-the-box:
|
||||
|
||||
* Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
|
||||
directives, form validation, routing, deep-linking, reusable components, dependency injection.
|
||||
* Testability story: unit-testing, end-to-end testing, mocks, test harnesses.
|
||||
* Seed application with directory layout and test scripts as a starting point.
|
||||
|
||||
|
||||
## Angular Sweet Spot
|
||||
|
||||
Angular simplifies application development by presenting a higher level of abstraction to the
|
||||
developer. Like any abstraction, it comes at a cost of flexibility. In other words not every app
|
||||
is a good fit for Angular. Angular was built for the CRUD application in mind. Luckily CRUD
|
||||
applications represent at least 90% of the web applications. But to understand what Angular is
|
||||
good at one also has to understand when an app is not a good fit for Angular.
|
||||
|
||||
Games, and GUI editors are examples of very intensive and tricky DOM manipulation. These kinds of
|
||||
apps are different from CRUD apps, and as a result are not a good fit for Angular. In these cases
|
||||
using something closer to bare metal such as `jQuery` may be a better fit.
|
||||
|
||||
|
||||
# An Introductory Angular Example
|
||||
|
||||
Below is a typical CRUD application which contains a form. The form values are validated, and
|
||||
are used to compute the total, which is formatted to a particular locale. These are some common
|
||||
concepts which the application developer may face:
|
||||
|
||||
* attaching data-model to the UI.
|
||||
* writing, reading and validating user input.
|
||||
* computing new values based on the model.
|
||||
* formatting output in a user specific locale.
|
||||
|
||||
<example>
|
||||
<file name="script.js">
|
||||
function InvoiceCntl($scope) {
|
||||
$scope.qty = 1;
|
||||
$scope.cost = 19.95;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceCntl">
|
||||
<b>Invoice:</b>
|
||||
<br>
|
||||
<br>
|
||||
<table>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input type="integer" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-model="cost" required ></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should show of angular binding', function() {
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Try out the Live Preview above, and then let's walk through the example and describe what's going
|
||||
on.
|
||||
|
||||
In the `<html>` tag, we specify that it is an angular
|
||||
application with the `ng-app` directive. The `ng-app` will cause Angular to {@link
|
||||
bootstrap auto initialize} your application.
|
||||
|
||||
<html ng-app>
|
||||
|
||||
We load Angular using the `<script>` tag:
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/?.?.?/angular.min.js"></script>
|
||||
|
||||
From the `ng-model` attribute of the `<input>` tags, angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
Quantity: <input type="integer" min="0" ng-model="qty" required >
|
||||
Cost: <input type="number" ng-model="cost" required >
|
||||
|
||||
These input widgets look normal enough, but consider these points:
|
||||
|
||||
* When this page loaded, angular bound the names of the input widgets (`qty` and `cost`) to
|
||||
variables of the same name. Think of those variables as the "Model" component of the
|
||||
Model-View-Controller design pattern.
|
||||
* Note that the HTML widget {@link api/ng.directive:input input}
|
||||
has special powers. The input invalidates itself by turning red when you enter invalid data or
|
||||
leave the the input fields blank. These new widget behaviors make it easier to implement field
|
||||
validation common in CRUD applications.
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
|
||||
Total: {{qty * cost | currency}}
|
||||
|
||||
This notation, `{{ _expression_ }}`, is Angular markup for data-binding. The expression itself can
|
||||
be a combination of both an expression and a {@link dev_guide.templates.filters filter}: `{{
|
||||
expression | filter }}`. Angular provides filters for formatting display data.
|
||||
|
||||
In the example above, the expression in double-curly braces directs Angular to "bind the data we
|
||||
got from the input widgets to the display, multiply them together, and format the resulting number
|
||||
into output that looks like money."
|
||||
|
||||
Notice that we achieved this application behavior not by calling Angular methods, nor by
|
||||
implementing application specific behavior as a framework. We achieved the behavior because the
|
||||
browser behaved more in line with what is needed for a dynamic web application rather then what is
|
||||
needed for a static document. Angular has lowered the impedance mismatch to the point where no
|
||||
library/framework calls are needed.
|
||||
|
||||
|
||||
# The Zen of Angular
|
||||
|
||||
Angular is built around the belief that declarative code is better than imperative when it comes
|
||||
to building UIs and wiring software components together, while imperative code is excellent for
|
||||
expressing business logic.
|
||||
|
||||
|
||||
* It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves
|
||||
the testability of the code.
|
||||
* It is a really, _really_ good idea to regard app testing as equal in importance to app
|
||||
writing. Testing difficulty is dramatically affected by the way the code is structured.
|
||||
* It is an excellent idea to decouple the client side of an app from the server side. This
|
||||
allows development work to progress in parallel, and allows for reuse of both sides.
|
||||
* It is very helpful indeed if the framework guides developers through the entire journey of
|
||||
building an app: from designing the UI, through writing the business logic, to testing.
|
||||
* It is always good to make common tasks trivial and difficult tasks possible.
|
||||
|
||||
|
||||
|
||||
Angular frees you from the following pain:
|
||||
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the
|
||||
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It
|
||||
vastly reduces the amount of JavaScript coding _you_ have to do, and it makes it easier to see
|
||||
what your application does.
|
||||
* **Manipulating HTML DOM programmatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it's cumbersome and error-prone. By declaratively describing how the UI
|
||||
should change as your application state changes, you are freed from low level DOM manipulation
|
||||
tasks. Most applications written with Angular never have to programmatically manipulate the
|
||||
DOM, although you can if you want to.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to an HTML
|
||||
form, allowing users to modify the form, validating the form, displaying validation errors,
|
||||
returning to an internal model, and then back to the server, creates a lot of boilerplate
|
||||
code. Angular eliminates almost all of this boilerplate, leaving code that describes the
|
||||
overall flow of the application rather than all of the implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot
|
||||
of plumbing just to get a basic "Hello World" AJAX app working. With angular you can bootstrap
|
||||
your app easily using services, which are auto-injected into your application in a {@link
|
||||
http://code.google.com/p/google-guice/ Guice}-like dependency-injection style. This allows you
|
||||
to get started developing features quickly. As a bonus, you get full control over the
|
||||
initialization process in automated tests.
|
||||
|
||||
|
||||
# Watch a Presentation About Angular
|
||||
|
||||
Here is a presentation on Angular from May 2012.
|
||||
|
||||
<iframe width="560" height="315" src="http://www.youtube.com/embed/bfrn5VNpwsg" frameborder="0" allowfullscreen></iframe>
|
||||
@@ -0,0 +1,331 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Scopes
|
||||
@description
|
||||
|
||||
# What are Scopes?
|
||||
|
||||
{@link api/ng.$rootScope.Scope scope} is an object that refers to the application
|
||||
model. It is an execution context for {@link expression expressions}. Scopes are
|
||||
arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can
|
||||
watch {@link guide/expression expressions} and propagate events.
|
||||
|
||||
## Scope characteristics
|
||||
|
||||
- Scopes provide APIs ({@link api/ng.$rootScope.Scope#$watch $watch}) to observe
|
||||
model mutations.
|
||||
|
||||
- Scopes provide APIs ({@link api/ng.$rootScope.Scope#$apply $apply}) to
|
||||
propagate any model changes through the system into the view from outside of the "Angular
|
||||
realm" (controllers, services, Angular event handlers).
|
||||
|
||||
- Scopes can be nested to isolate application components while providing access to shared model
|
||||
properties. A scope (prototypically) inherits properties from its parent scope.
|
||||
|
||||
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
|
||||
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
|
||||
scope which defines the `username` property.
|
||||
|
||||
## Scope as Data-Model
|
||||
|
||||
Scope is the glue between application controller and the view. During the template {@link compiler
|
||||
linking} phase the {@link api/ng.$compileProvider#directive directives} set up
|
||||
{@link api/ng.$rootScope.Scope#$watch `$watch`} expressions on the scope. The
|
||||
`$watch` allows the directives to be notified of property changes, which allows the directive to
|
||||
render the updated value to the DOM.
|
||||
|
||||
Both controllers and directives have reference to the scope, but not to each other. This
|
||||
arrangement isolates the controller from the directive as well as from DOM. This is an important
|
||||
point since it makes the controllers view agnostic, which greatly improves the testing story of
|
||||
the applications.
|
||||
|
||||
<example>
|
||||
<file name="script.js">
|
||||
function MyController($scope) {
|
||||
$scope.username = 'World';
|
||||
|
||||
$scope.sayHello = function() {
|
||||
$scope.greeting = 'Hello ' + $scope.username + '!';
|
||||
};
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="MyController">
|
||||
Your name:
|
||||
<input type="text" ng-model="username">
|
||||
<button ng-click='sayHello()'>greet</button>
|
||||
<hr>
|
||||
{{greeting}}
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
In the above example notice that the `MyController` assigns `World` to the `username` property of
|
||||
the scope. The scope then notifies the `input` of the assignment, which then renders the input
|
||||
with username pre-filled. This demonstrates how a controller can write data into the scope.
|
||||
|
||||
Similarly the controller can assign behavior to scope as seen by the `sayHello` method, which is
|
||||
invoked when the user clicks on the 'greet' button. The `sayHello` method can read the `username`
|
||||
property and create a `greeting` property. This demonstrates that the properties on scope update
|
||||
automatically when they are bound to HTML input widgets.
|
||||
|
||||
Logically the rendering of `{{greeting}}` involves:
|
||||
|
||||
* retrieval of the scope associated with DOM node where `{{greeting}}` is defined in template.
|
||||
In this example this is the same scope as the scope which was passed into `MyController`. (We
|
||||
will discuss scope hierarchies later.)
|
||||
|
||||
* Evaluate the `greeting` {@link guide/expression expression} against the scope retrieved above,
|
||||
and assign the result to the text of the enclosing DOM element.
|
||||
|
||||
|
||||
You can think of the scope and its properties as the data which is used to render the view. The
|
||||
scope is the single source-of-truth for all things view related.
|
||||
|
||||
From testability, the separation of the controller and the view is desirable, because it allows us
|
||||
to test the behavior without being distracted by the rendering details.
|
||||
|
||||
<pre>
|
||||
it('should say hello', function() {
|
||||
var scopeMock = {};
|
||||
var cntl = new MyController(scopeMock);
|
||||
|
||||
// Assert that username is pre-filled
|
||||
expect(scopeMock.username).toEqual('World');
|
||||
|
||||
// Assert that we read new username and greet
|
||||
scopeMock.username = 'angular';
|
||||
scopeMock.sayHello();
|
||||
expect(scopeMock.greeting).toEqual('Hello angular!');
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Scope Hierarchies
|
||||
|
||||
Each Angular application has exactly one {@link api/ng.$rootScope root scope}, but
|
||||
may have several child scopes.
|
||||
|
||||
The application can have multiple scopes, because some {@link guide/directive directives} create
|
||||
new child scopes (refer to directive documentation to see which directives create new scopes).
|
||||
When new scopes are created, they are added as children of their parent scope. This creates a tree
|
||||
structure which parallels the DOM where they're attached
|
||||
|
||||
When Angular evaluates `{{username}}`, it first looks at the scope associated with the given
|
||||
element for the `username` property. If no such property is found, it searches the parent scope
|
||||
and so on until the root scope is reached. In JavaScript this behavior is known as prototypical
|
||||
inheritance, and child scopes prototypically inherit from their parents.
|
||||
|
||||
This example illustrates scopes in application, and prototypical inheritance of properties.
|
||||
|
||||
<example>
|
||||
<file name="style.css">
|
||||
/* remove .doc-example-live in jsfiddle */
|
||||
.doc-example-live .ng-scope {
|
||||
border: 1px dashed red;
|
||||
}
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function EmployeeController($scope) {
|
||||
$scope.department = 'Engineering';
|
||||
$scope.employee = {
|
||||
name: 'Joe the Manager',
|
||||
reports: [
|
||||
{name: 'John Smith'},
|
||||
{name: 'Mary Run'}
|
||||
]
|
||||
};
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="EmployeeController">
|
||||
Manager: {{employee.name}} [ {{department}} ]<br>
|
||||
Reports:
|
||||
<ul>
|
||||
<li ng-repeat="employee in employee.reports">
|
||||
{{employee.name}} [ {{department}} ]
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
{{greeting}}
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Notice that the Angular automatically places `ng-scope` class on elements where scopes are
|
||||
attached. The `<style>` definition in this example highlights in red the new scope locations. The
|
||||
child scopes are necessary because the repeater evaluates `{{employee.name}}` expression, but
|
||||
depending on which scope the expression is evaluated it produces different result. Similarly the
|
||||
evaluation of `{{department}}` prototypically inherits from root scope, as it is the only place
|
||||
where the `department` property is defined.
|
||||
|
||||
|
||||
## Retrieving Scopes from the DOM.
|
||||
|
||||
Scopes are attached to the DOM as `$scope` data property, and can be retrieved for debugging
|
||||
purposes. (It is unlikely that one would need to retrieve scopes in this way inside the
|
||||
application.) The location where the root scope is attached to the DOM is defined by the location
|
||||
of {@link api/ng.directive:ngApp `ng-app`} directive. Typically
|
||||
`ng-app` is placed an the `<html>` element, but it can be placed on other elements as well, if,
|
||||
for example, only a portion of the view needs to be controlled by angular.
|
||||
|
||||
To examine the scope in the debugger:
|
||||
|
||||
1. right click on the element of interest in your browser and select 'inspect element'. You
|
||||
should see the browser debugger with the element you clicked on highlighted.
|
||||
|
||||
2. The debugger allows you to access the currently selected element in the console as `$0`
|
||||
variable.
|
||||
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()`
|
||||
|
||||
|
||||
## Scope Events Propagation
|
||||
|
||||
Scopes can propagate events in similar fashion to DOM events. The event can be {@link
|
||||
api/ng.$rootScope.Scope#$broadcast broadcasted} to the scope children or {@link
|
||||
api/ng.$rootScope.Scope#$emit emitted} to scope parents.
|
||||
|
||||
<example>
|
||||
<file name="script.js">
|
||||
function EventController($scope) {
|
||||
$scope.count = 0;
|
||||
$scope.$on('MyEvent', function() {
|
||||
$scope.count++;
|
||||
});
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="EventController">
|
||||
Root scope <tt>MyEvent</tt> count: {{count}}
|
||||
<ul>
|
||||
<li ng-repeat="i in [1]" ng-controller="EventController">
|
||||
<button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
|
||||
<button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
|
||||
<br>
|
||||
Middle scope <tt>MyEvent</tt> count: {{count}}
|
||||
<ul>
|
||||
<li ng-repeat="item in [1, 2]" ng-controller="EventController">
|
||||
Leaf scope <tt>MyEvent</tt> count: {{count}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
|
||||
## Scope Life Cycle
|
||||
|
||||
The normal flow of browser receiving an event is that it executes a corresponding JavaScript
|
||||
callback. Once the callback completes the browser re-renders the DOM and returns to waiting for
|
||||
more events.
|
||||
|
||||
When the browser calls into JavaScript the code executes outside the Angular execution context,
|
||||
which means that Angular is unaware of model modifications. To properly process model
|
||||
modifications the execution has to enter the Angular execution context using the {@link
|
||||
api/ng.$rootScope.Scope#$apply `$apply`} method. Only model modifications which
|
||||
execute inside the `$apply` method will be properly accounted for by Angular. For example if a
|
||||
directive listens on DOM events, such as {@link
|
||||
api/ng.directive:ngClick `ng-click`} it must evaluate the
|
||||
expression inside the `$apply` method.
|
||||
|
||||
After evaluating the expression, the `$apply` method performs a {@link
|
||||
api/ng.$rootScope.Scope#$digest `$digest`}. In the $digest phase the scope examines all
|
||||
of the `$watch` expressions and compares them with the previous value. This dirty checking is done
|
||||
asynchronously. This means that assignment such as `$scope.username="angular"` will not
|
||||
immediately cause a `$watch` to be notified, instead the `$watch` notification is delayed until
|
||||
the `$digest` phase. This delay is desirable, since it coalesces multiple model updates into one
|
||||
`$watch` notification as well as it guarantees that during the `$watch` notification no other
|
||||
`$watch`es are running. If a `$watch` changes the value of the model, it will force additional
|
||||
`$digest` cycle.
|
||||
|
||||
1. **Creation**
|
||||
|
||||
The {@link api/ng.$rootScope root scope} is created during the application
|
||||
bootstrap by the {@link api/AUTO.$injector $injector}. During template
|
||||
linking, some directives create new child scopes.
|
||||
|
||||
2. **Watcher registration**
|
||||
|
||||
During template linking directives register {@link
|
||||
api/ng.$rootScope.Scope#$watch watches} on the scope. These watches will be
|
||||
used to propagate model values to the DOM.
|
||||
|
||||
3. **Model mutation**
|
||||
|
||||
For mutations to be properly observed, you should make them only within the {@link
|
||||
api/ng.$rootScope.Scope#$apply scope.$apply()}. (Angular APIs do this
|
||||
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
|
||||
or asynchronous work with {@link api/ng.$http $http} or {@link
|
||||
api/ng.$timeout $timeout} services.
|
||||
|
||||
4. **Mutation observation**
|
||||
|
||||
At the end `$apply`, Angular performs a {@link api/ng.$rootScope.Scope#$digest
|
||||
$digest} cycle on the root scope, which then propagates throughout all child scopes. During
|
||||
the `$digest` cycle, all `$watch`ed expressions or functions are checked for model mutation
|
||||
and if a mutation is detected, the `$watch` listener is called.
|
||||
|
||||
5. **Scope destruction**
|
||||
|
||||
When child scopes are no longer needed, it is the responsibility of the child scope creator
|
||||
to destroy them via {@link api/ng.$rootScope.Scope#$destroy scope.$destroy()}
|
||||
API. This will stop propagation of `$digest` calls into the child scope and allow for memory
|
||||
used by the child scope models to be reclaimed by the garbage collector.
|
||||
|
||||
|
||||
### Scopes and Directives
|
||||
|
||||
During the compilation phase, the {@link compiler compiler} matches {@link
|
||||
api/ng.$compileProvider#directive directives} against the DOM template. The directives
|
||||
usually fall into one of two categories:
|
||||
|
||||
- Observing {@link api/ng.$compileProvider#directive directives}, such as
|
||||
double-curly expressions `{{expression}}`, register listeners using the {@link
|
||||
api/ng.$rootScope.Scope#$watch $watch()} method. This type of directive needs
|
||||
to be notified whenever the expression changes so that it can update the view.
|
||||
|
||||
- Listener directives, such as {@link api/ng.directive:ngClick
|
||||
ng-click}, register a listener with the DOM. When the DOM listener fires, the directive
|
||||
executes the associated expression and updates the view using the {@link
|
||||
api/ng.$rootScope.Scope#$apply $apply()} method.
|
||||
|
||||
When an external event (such as a user action, timer or XHR) is received, the associated {@link
|
||||
expression expression} must be applied to the scope through the {@link
|
||||
api/ng.$rootScope.Scope#$apply $apply()} method so that all listeners are updated
|
||||
correctly.
|
||||
|
||||
### Directives that Create Scopes
|
||||
|
||||
In most cases, {@link api/ng.$compileProvider#directive directives} and scopes interact
|
||||
but do not create new instances of scope. However, some directives, such as {@link
|
||||
api/ng.directive:ngController ng-controller} and {@link
|
||||
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
|
||||
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
||||
element by using an `angular.element(aDomElement).scope()` method call.
|
||||
|
||||
### Controllers and Scopes
|
||||
|
||||
Scopes and controllers interact with each other in the following situations:
|
||||
|
||||
- Controllers use scopes to expose controller methods to templates (see {@link
|
||||
api/ng.directive:ngController ng-controller}).
|
||||
|
||||
- Controllers define methods (behavior) that can mutate the model (properties on the scope).
|
||||
|
||||
- Controllers may register {@link api/ng.$rootScope.Scope#$watch watches} on
|
||||
the model. These watches execute immediately after the controller behavior executes.
|
||||
|
||||
See the {@link api/ng.directive:ngController ng-controller} for more
|
||||
information.
|
||||
|
||||
|
||||
### Scope `$watch` Performance Considerations
|
||||
|
||||
Dirty checking the scope for property changes is a common operation in Angular and for this reason
|
||||
the dirty checking function must be efficient. Care should be taken that the dirty checking
|
||||
function does not do any DOM access, as DOM access is orders of magnitude slower then property
|
||||
access on JavaScript object.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Type
|
||||
@description
|
||||
@@ -93,6 +93,7 @@ pre-packaged bundle.
|
||||
* install q: `npm install q`
|
||||
* install qq: `npm install qq`
|
||||
* install q-fs: `npm install q-fs`
|
||||
* install jasmine-node: `npm install jasmine`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Downloading
|
||||
@description
|
||||
@@ -17,14 +16,14 @@ development.
|
||||
production.
|
||||
|
||||
To point your code to an angular script on the angular server, use the following template. This
|
||||
example points to (non-minified) version 0.9.12:
|
||||
example points to (non-minified) version 0.10.6:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<html ng-app>
|
||||
<head>
|
||||
<title>My Angular App</title>
|
||||
<script src="http://code.angularjs.org/angular-0.9.12.js" ng:autobind></script>
|
||||
<script src="http://code.angularjs.org/angular-0.10.6.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
||||
+54
-38
@@ -1,58 +1,71 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name FAQ
|
||||
@description
|
||||
|
||||
#FAQ
|
||||
|
||||
### Why is this project called "angular"? Why is the namespace called "ng"?
|
||||
### Why is this project called "AngularJS"? Why is the namespace called "ng"?
|
||||
|
||||
Because HTML has angular brackets and "ng" sounds like "angular".
|
||||
Because HTML has Angular brackets and "ng" sounds like "Angular".
|
||||
|
||||
### Is <angular/> an HTML5 tag?
|
||||
|
||||
No, <angular/> is not an HTML5 tag. angular is an orthogonal project to HTML5; you can use the two
|
||||
together.
|
||||
### Is AngularJS a library, framework, plugin or a browser extension?
|
||||
|
||||
### Is angular a {library, framework, DOM manipulation library, widget library, native plugin}?
|
||||
AngularJS fits the definition of a framework the best, even though it's much more lightweight than
|
||||
a typical framework and that's why many confuse it with a library.
|
||||
|
||||
No, angular is none of these. You don't call its functions, it does not call your functions,
|
||||
it does not provide a way to manipulate DOM, but does provide primitives to create UI projections
|
||||
of your data. There are lots of existing widget libraries which you can integrate with angular.
|
||||
It is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers.
|
||||
AngularJS is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers.
|
||||
So it's definitely not a plugin or some other native browser extension.
|
||||
|
||||
### Do I need to worry about security holes in angular?
|
||||
|
||||
Like with any technology, angular is not impervious to attack. angular does, however, provide
|
||||
built-in protection from basic security holes including cross-site scripting and HTML injection
|
||||
attacks. angular does round-trip escaping on all strings for you.
|
||||
### Is AngularJS a templating system?
|
||||
|
||||
### Can I download the source, build, and host the angular environment locally?
|
||||
|
||||
Yes. See instructions in {@link downloading}.
|
||||
|
||||
### Is angular a templating system?
|
||||
|
||||
At the highest level, angular does look like a just another templating system. But there is one
|
||||
important reason why angular templating system is different and makes it very good fit for
|
||||
application development: bidirectional data binding. The template is compiled on the browser and
|
||||
the compilation step produces a live view. This means you, the developer, don't need to write
|
||||
At the highest level, Angular does look like a just another templating system. But there is one
|
||||
important reason why Angular templating system is different and makes it very good fit for
|
||||
application development: bidirectional data binding. The template is compiled in the browser and
|
||||
the compilation step produces a live view. This means you, the developers, don't need to write
|
||||
code to constantly sync the view with the model and the model with the view as in other
|
||||
templating systems.
|
||||
|
||||
|
||||
### Do I need to worry about security holes in AngularJS?
|
||||
|
||||
Like with any technology, AngularJS is not impervious to attack. angular does, however, provide
|
||||
built-in protection from basic security holes including cross-site scripting and HTML injection
|
||||
attacks. AngularJS does round-trip escaping on all strings for you and even offers XSRF protection
|
||||
for server-side communication.
|
||||
|
||||
AngularJS was designed to be compatible with other security measures like Content Security Policy
|
||||
(CSP), HTTPS (SSL/TLS) and server-side authentication and authorization that greatly reduce the
|
||||
possible attack vectors and we highly recommended their use.
|
||||
|
||||
|
||||
### Can I download the source, build, and host the AngularJS environment locally?
|
||||
|
||||
Yes. See instructions in {@link downloading}.
|
||||
|
||||
|
||||
|
||||
### What browsers does angular work with?
|
||||
|
||||
Webkit-based browsers (Safari, Chrome, iPhone, Android, WebOS, BlackBerry 6), Firefox, IE6 and
|
||||
above. Note that CSS only works on IE7 and above.
|
||||
Our we run our extensive test suite against the following browsers: Safari, Chrome, Firefox, Opera,
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari).
|
||||
|
||||
### What's angular's performance like?
|
||||
|
||||
angular takes ~300ms to load, render, and compile. In Chrome it uses about 2-5MB of memory. Your
|
||||
app's performance will vary depending on how many bindings you use.
|
||||
### What's Angular's performance like?
|
||||
|
||||
### How big is the angular bootstrap JS file that I need to include?
|
||||
The startup time heavily depends on your network connection, state of the cache, browser used and
|
||||
available hardware, but typically we measure bootstrap time in tens or hundreds of milliseconds.
|
||||
|
||||
The runtime performance will vary depending on the number and complexity of bindings on the page
|
||||
as well as the speed of your backend (for apps that fetch data from the backend). Just for an
|
||||
illustration we typically build snappy apps with hundreds or thousands of active bindings.
|
||||
|
||||
|
||||
### How big is the angular.js file that I need to include?
|
||||
|
||||
The size of the file is < 29KB compressed and minified.
|
||||
|
||||
The size of the library itself is < 50KB compressed and obfuscated.
|
||||
|
||||
### Can I use the open-source Closure Library with angular?
|
||||
|
||||
@@ -61,15 +74,17 @@ in angular.
|
||||
|
||||
### Does angular use the jQuery library?
|
||||
|
||||
Yes, angular uses {@link http://jquery.com/ jQuery}, the open source DOM manipulation library.
|
||||
If jQuery is not present in your script path, angular falls back on its own implementation of
|
||||
{@link api/angular.element jQuery lite}. If jQuery is present in the path, angular uses it to
|
||||
manipulate the DOM.
|
||||
Yes, Angular can use {@link http://jquery.com/ jQuery} if it's present in your app when the
|
||||
application is being bootstrapped. If jQuery is not present in your script path, Angular falls back
|
||||
to its own implementation of the subset of jQuery that we call {@link api/angular.element jQLite}.
|
||||
|
||||
|
||||
### What is testability like in angular?
|
||||
|
||||
Very testable. It has an integrated dependency injection framework. See
|
||||
{@link api/angular.service service} for details.
|
||||
Very testable and designed this way from ground up. It has an integrated dependency injection
|
||||
framework, provides mocks for many heavy dependencies (server-side communication). See
|
||||
{@link api/ng service} for details.
|
||||
|
||||
|
||||
### How can I learn more about angular?
|
||||
|
||||
@@ -77,6 +92,7 @@ Watch the July 28, 2010 talk
|
||||
"{@link http://www.youtube.com/watch?v=elvcgVSynRg| Angular: A Radically Different Way of Building
|
||||
AJAX Apps}".
|
||||
|
||||
|
||||
### How is angular licensed?
|
||||
|
||||
The MIT License.
|
||||
|
||||
+24
-131
@@ -2,144 +2,37 @@
|
||||
@name Getting Started
|
||||
@description
|
||||
|
||||
# Hello World!
|
||||
We want you to have an easy time while starting to use Angular. We've put together the following steps on your path to
|
||||
becoming an Angular expert.
|
||||
|
||||
A great way for you to get started with AngularJS is to create the tradtional
|
||||
"Hello World!" app:
|
||||
|
||||
1. In your favorite text editor, create an HTML file
|
||||
(for example, `helloworld.html`).
|
||||
2. From the __Source__ box below, copy and paste the code into your HTML file.
|
||||
(Double-click on the source to easily select all.)
|
||||
3. Open the file in your web browser.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Hello {{'World'}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
The resulting web page should look something like the following:
|
||||
|
||||
<img class="center" src="img/helloworld.png" border="1" />
|
||||
|
||||
Now let's take a closer look at that code, and see what is going on behind
|
||||
the scenes.
|
||||
|
||||
The first line of interest defines the `ng` namespace, which makes
|
||||
AngularJS work across all browsers (especially important for IE):
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
</pre>
|
||||
|
||||
The next line downloads the angular script, and instructs angular to process
|
||||
the entire HTML page when it is loaded:
|
||||
|
||||
<pre>
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular-?.?.?.min.js"
|
||||
ng:autobind></script>
|
||||
</pre>
|
||||
|
||||
(For details on what happens when angular processes an HTML page,
|
||||
see {@link guide/dev_guide.bootstrap Bootstrap}.)
|
||||
|
||||
Finally, this line in the `<body>` of the page is the template that describes
|
||||
how to display our greeting in the UI:
|
||||
|
||||
<pre>
|
||||
Hello {{'World'}}!
|
||||
</pre>
|
||||
|
||||
Note the use of the double curly brace markup (`{{ }}`) to bind the expression to
|
||||
the greeting text. Here the expression is the string literal 'World'.
|
||||
|
||||
Next let's look at a more interesting example, that uses AngularJS to
|
||||
bind a dynamic expression to our greeting text.
|
||||
|
||||
# Hello AngularJS World!
|
||||
|
||||
This example demonstrates angular's two-way data binding:
|
||||
|
||||
1. Edit the HTML file you created in the "Hello World!" example above.
|
||||
2. Replace the contents of `<body>` with the code from the __Source__ box below.
|
||||
3. Refresh your browser window.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" name="yourname" value="World"/>
|
||||
<hr/>
|
||||
Hello {{yourname}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
After the refresh, the page should look something like this:
|
||||
|
||||
<img class="left" src="img/helloworld_2way.png" border="1" />
|
||||
|
||||
These are some of the important points to note from this example:
|
||||
|
||||
* The text input {@link api/angular.widget widget} called `yourname` is bound to a model variable
|
||||
called `yourname`.
|
||||
* The double curly braces notation binds the `yourname` model to the greeting text.
|
||||
|
||||
* You did not need to explicitly register an event listener or define an event handler for events!
|
||||
|
||||
Now try typing your name into the input box, and notice the immediate change to
|
||||
the displayed greeting. This demonstrates the concept of angular's
|
||||
{@link guide/dev_guide.templates.databinding bi-directional data binding}. Any changes to the input
|
||||
field are immediately
|
||||
reflected in the model (one direction), and any changes to the model are
|
||||
reflected in the greeting text (the other direction).
|
||||
1. Read the {@link guide/concepts conceptual overview}.<br/>Understand Angular's vocabulary and how all the Angular
|
||||
components work together.
|
||||
1. Do the {@link tutorial/ AngularJS Tutorial}.<br/>Walk end-to-end through building and application complete with tests
|
||||
on top of a node.js web server. Covers every major AngularJS feature and show you how to set up your development
|
||||
environment.
|
||||
1. Download or clone the {@link https://github.com/angular/angular-seed Seed App project template}.<br/>Gives you a
|
||||
starter app with a directory layout, test harness, and scripts to begin building your application.
|
||||
|
||||
|
||||
# Anatomy Of An Angular App
|
||||
#Further Steps
|
||||
|
||||
This section describes the 3 parts of an angular app, and explains how they map to the
|
||||
Model-View-Controller design pattern:
|
||||
##Watch Videos
|
||||
|
||||
## Templates
|
||||
If you haven’t had a chance to watch the videos from the homepage, please check out:
|
||||
* {@link http://www.youtube.com/watch?v=WuiHuZq_cg4&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Introduction to AngularJS}
|
||||
* {@link http://www.youtube.com/watch?v=Yg-R1gchccg&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Creating Directives}
|
||||
* {@link http://www.youtube.com/watch?v=IRelx4-ISbs&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Communicating with Servers}
|
||||
|
||||
Templates, which you write in HTML and CSS, serve as the View. You add elements, attributes, and
|
||||
markup to HTML, which serve as instructions to the angular compiler. The angular compiler is fully
|
||||
extensible, meaning that with angular you can build your own declarative language on top of HTML!
|
||||
And visit our {@link http://www.youtube.com/user/angularjs YouTube channel} for more AngularJS video presentations and
|
||||
tutorials.
|
||||
|
||||
##Subscribe
|
||||
|
||||
## Application Logic and Behavior
|
||||
* Subscribe to the {@link http://groups.google.com/forum/?fromgroups#!forum/angular mailing list}. Ask questions here!
|
||||
* Follow us on {@link https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F®ion=follow_link&screen_name=angularjs&source=followbutton&variant=2.0 Twitter}
|
||||
* Add us to your circles on {@link https://plus.google.com/110323587230527980117/posts Google+}
|
||||
|
||||
Application Logic and Behavior, which you define in JavaScript, serve as the Controller. With
|
||||
angular (unlike with standard AJAX applications) you don't need to write additional listeners or
|
||||
DOM manipulators, because they are built-in. This feature makes your application logic very easy to
|
||||
write, test, maintain, and understand.
|
||||
##Read more
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
The Model is referenced from properties on {@link guide/dev_guide.scopes angular scope objects}.
|
||||
The data in your model could be Javascript objects, arrays, or primitives, it doesn't matter. What
|
||||
matters is that these are all referenced by the scope object.
|
||||
|
||||
Angular employs scopes to keep your data model and your UI in sync. Whenever something occurs to
|
||||
change the state of the model, angular immediately reflects that change in the UI, and vice versa.
|
||||
|
||||
The following illustration shows the parts of an angular application and how they work together:
|
||||
|
||||
<img class="left" src="img/angular_parts.png" border="0" />
|
||||
|
||||
In addition, angular comes with a set of Services, which have the following properties:
|
||||
|
||||
* The services provided are very useful for building web applications.
|
||||
* You can extend and add application-specific behavior to services.
|
||||
* Services include Dependency-Injection, XHR, caching, URL routing, and browser abstraction.
|
||||
|
||||
|
||||
# Where To Go Next
|
||||
|
||||
* If you like what you've learned so far, you should definitely check out our awesome {@link
|
||||
tutorial/ Tutorial}, which walk you through the process of building real apps with AngularJS.
|
||||
|
||||
* For further explanations and examples of the AngularJS concepts presented on this page, see the
|
||||
{@link guide/index Developer Guide}.
|
||||
|
||||
* For additional hands-on examples of using AngularJS, including more source code that you can
|
||||
copy and paste into your own pages, take a look through the {@link cookbook/ Cookbook}.
|
||||
The AngularJS documentation includes the {@link guide/index Developer Guide} covering concepts and the
|
||||
{@link api/ API Reference} for syntax and usage.
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
@name Tutorial
|
||||
@description
|
||||
|
||||
A great way to get introduced to Angular is to work through this tutorial, which walks you through
|
||||
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
|
||||
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
|
||||
of Android devices, lets you filter the list to see only devices that interest you, and then view
|
||||
details for any device.
|
||||
|
||||
<img src="img/tutorial/catalog_screen.png">
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
|
||||
|
||||
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
|
||||
or plug-ins. As you work through the tutorial, you will:
|
||||
@@ -32,7 +32,7 @@ When you finish the tutorial you will be able to:
|
||||
|
||||
The tutorial guides you through the entire process of building a simple application, including
|
||||
writing and running unit and end-to-end tests. Experiments at the end of each step provide
|
||||
suggestions for you learn more about AngularJS and the application you are building.
|
||||
suggestions for you to learn more about AngularJS and the application you are building.
|
||||
|
||||
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
|
||||
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
|
||||
@@ -52,99 +52,97 @@ code management or to use scripts that copy snapshots of project files into your
|
||||
(`sandbox`) directory. Select one of the tabs below and follow the instructions for setting up your
|
||||
computer for your preferred option.
|
||||
|
||||
<doc:tutorial-instructions show="true">
|
||||
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the
|
||||
following command in a terminal window:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<pre>java -version</pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Download Git from the <a href="http://git-scm.com/download">Git</a> site.</p>
|
||||
<p>You can build Git from source or use the pre-compiled package.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre><code>cd angular-phonecat</code></pre>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
directory.</p></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can <a
|
||||
href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
|
||||
node.js</a>. Use <code>node</code> to run <code>scripts/web-server.js</code>, a simple bundled
|
||||
http server.</p></li>
|
||||
href="http://nodejs.org/#download">install node.js</a>. Use <code>node</code> to run
|
||||
<code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
</div>
|
||||
|
||||
<doc:tutorial-instruction id="git-win" title="Git on Windows">
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Java to run unit tests, so run the following command to verify that you
|
||||
have <a href="http://java.com/">Java</a> installed and that the <code>java</code> executable is on
|
||||
your <code>PATH</code>.</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<pre>java -version</pre>
|
||||
<p></p></li>
|
||||
<li><p>Install msysGit from <a href="http://git-scm.com/download">the Git</a> site.</p></li>
|
||||
<li><p>Open msysGit bash and clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat.</p>
|
||||
<pre><code>cd angular-phonecat</code></pre>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
directory.</p>
|
||||
<p>You should run all <code>git</code> commands from msysGit bash.</p>
|
||||
<p>Other commands like <code>test-server.bat</code> or <code>test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system. If you don't already have one
|
||||
installed, you can install <a href="http://nodejs.org/">node.js</a>. Download the <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them, and then add
|
||||
<code>nodejs\bin</code> into your <code>PATH</code>. Use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code>, a simple, bundled http server.</p></li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can install <a href="http://nodejs.org/#download">node.js</a>. Make sure that
|
||||
<code>nodejs\bin</code> was added into your <code>PATH</code>. Use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
</div>
|
||||
|
||||
<doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<div class="tab-pane well" id="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You need Java to run unit tests, so verify that you have <a
|
||||
href="http://java.com/">Java</a> installed by running the following command in a terminal
|
||||
window:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<pre>java -version</pre>
|
||||
<li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
|
||||
containing all of the files and unzip them into the [tutorial-dir] directory</p>.</li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sandbox, as follows:</p>
|
||||
<pre><code>cd [tutorial-dir]/sandbox</code></pre>
|
||||
<pre>cd [tutorial-dir]/sandbox</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from your
|
||||
<code>sandbox</code> directory.</p></li>
|
||||
<li><p>You need an http server running on your system and Mac and Linux machines typically
|
||||
have Apache pre-installed. If you don't have an http server installed, you can <a
|
||||
href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
|
||||
node.js</a> and use it to run <code>scripts/web-server.js</code>, a simple bundled http
|
||||
server.</p></li>
|
||||
href="http://nodejs.org/#download">install node.js</a> and use it to run
|
||||
<code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
</div>
|
||||
|
||||
<doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
|
||||
<div class="tab-pane well" id="ss-win" title="Snapshots on Windows">
|
||||
<ol>
|
||||
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the
|
||||
<code>java</code> executable is on your <code>PATH</code> by running the following command in the
|
||||
Windows command line:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<pre>java -version</pre>
|
||||
<p>You need Java to run unit tests, so download the <a
|
||||
href="http://code.angularjs.org/angular-phonecat/">zip archive</a> that contains all of the files
|
||||
and unzip the files into the [tutorial-dir] directory</p></li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sandbox, as follows:</p>
|
||||
<pre><code>cd [tutorial-dir]/sandbox</code></pre>
|
||||
<pre>cd [tutorial-dir]/sandbox</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can install <a href="http://nodejs.org/">node.js</a>. Download the <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them, and then add
|
||||
<code>nodejs\bin</code> into your <code>PATH</code>. Use <code>node</code> to run
|
||||
already installed, you can install <a href="http://nodejs.org/#download">node.js</a>. Make sure that
|
||||
<code>nodejs\bin</code> was added into your <code>PATH</code>. Use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
</doc:tutorial-instructions>
|
||||
</div>
|
||||
</divs>
|
||||
|
||||
The last thing to do is to make sure your computer has a web browser and a good text editor
|
||||
installed. Now, let's get going with {@link step_00 step 0}.
|
||||
installed. Now, let's get some cool stuff done!
|
||||
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user