pax_global_header00006660000000000000000000000064135647232520014523gustar00rootroot0000000000000052 comment=d257c4564d069a279c5625ad6b68008d4a4dbb11 angular.js-1.7.9/000077500000000000000000000000001356472325200136055ustar00rootroot00000000000000angular.js-1.7.9/.editorconfig000066400000000000000000000004611356472325200162630ustar00rootroot00000000000000# https://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [dropdown-toggle.js] trim_trailing_whitespace = false insert_final_newline = false [htmlparser.js] insert_final_newline = false angular.js-1.7.9/.eslintignore000066400000000000000000000002671356472325200163150ustar00rootroot00000000000000build/** docs/app/assets/js/angular-bootstrap/** docs/config/templates/** node_modules/** lib/htmlparser/** src/angular.bind.js src/ngParseExt/ucd.js i18n/closure/** tmp/** vendor/** angular.js-1.7.9/.eslintrc-base.json000066400000000000000000000072621356472325200173200ustar00rootroot00000000000000{ "rules": { // Rules are divided into sections from http://eslint.org/docs/rules/ // Possible errors "comma-dangle": ["error", "never"], "no-cond-assign": ["error", "except-parens"], "no-constant-condition": ["error", {"checkLoops": false}], "no-control-regex": "error", "no-debugger": "error", "no-dupe-args": "error", "no-dupe-keys": "error", "no-duplicate-case": "error", "no-empty-character-class": "error", "no-empty": "error", "no-ex-assign": "error", "no-extra-boolean-cast": "error", "no-extra-semi": "error", "no-func-assign": "error", "no-inner-declarations": "error", "no-invalid-regexp": "error", "no-irregular-whitespace": "error", "no-negated-in-lhs": "error", "no-obj-calls": "error", "no-regex-spaces": "error", "no-sparse-arrays": "error", "no-unreachable": "error", "use-isnan": "error", "no-unsafe-finally": "error", "valid-typeof": "error", "no-unexpected-multiline": "error", // Best practices "accessor-pairs": "error", "array-callback-return": "error", "eqeqeq": ["error", "allow-null"], "no-alert": "error", "no-caller": "error", "no-case-declarations": "error", "no-eval": "error", "no-extend-native": "error", "no-extra-bind": "error", "no-extra-label": "error", "no-fallthrough": "error", "no-floating-decimal": "error", "no-implied-eval": "error", "no-invalid-this": "error", "no-iterator": "error", "no-multi-str": "error", "no-new-func": "error", "no-new-wrappers": "error", "no-new": "error", "no-octal-escape": "error", "no-octal": "error", "no-proto": "error", "no-redeclare": "error", "no-return-assign": "error", "no-script-url": "error", "no-self-assign": "error", "no-self-compare": "error", "no-sequences": "error", "no-throw-literal": "error", "no-unmodified-loop-condition": "error", "no-unused-expressions": "error", "no-unused-labels": "error", "no-useless-call": "error", "no-useless-concat": "error", "no-useless-escape": "error", "no-void": "error", "no-with": "error", "radix": "error", "wrap-iife": ["error", "inside"], // Strict mode "strict": ["error", "global"], // Variables "no-delete-var": "error", "no-label-var": "error", "no-restricted-globals": ["error", "event"], "no-shadow-restricted-names": "error", "no-undef-init": "error", "no-undef": "error", "no-unused-vars": ["error", { "vars": "local", "args": "none" }], // Node.js "handle-callback-err": "error", // Stylistic issues "array-bracket-spacing": ["error", "never"], "brace-style": ["error", "1tbs", { "allowSingleLine": true }], "comma-style": ["error", "last"], "eol-last": "error", "keyword-spacing": "error", "linebreak-style": ["error", "unix"], "max-len": ["error", { "code": 200, "ignoreComments": true, "ignoreUrls": true }], "new-cap": "error", "new-parens": "error", "no-array-constructor": "error", "no-bitwise": "error", "no-mixed-spaces-and-tabs": "error", "no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1 }], "no-whitespace-before-property": "error", "no-spaced-func": "error", "no-trailing-spaces": "error", "no-unneeded-ternary": "error", "quotes": ["error", "single"], "semi-spacing": "error", "semi": "error", "space-before-blocks": ["error", "always"], "space-before-function-paren": ["error", "never"], "space-in-parens": ["error", "never"], "space-infix-ops": "error", "space-unary-ops": ["error", { "words": true, "nonwords": false }], "unicode-bom": ["error", "never"] } } angular.js-1.7.9/.eslintrc-browser.json000066400000000000000000000006121356472325200200610ustar00rootroot00000000000000{ "extends": "./.eslintrc-base.json", "env": { // Note: don't set `"browser": true`; code in "src/" should be compatible with // non-browser environments like Node.js with a custom window implementation // like jsdom. All browser globals should be taken from window. "browser": false, "node": false }, "globals": { "window": false, "angular": false } } angular.js-1.7.9/.eslintrc-node.json000066400000000000000000000002641356472325200173260ustar00rootroot00000000000000{ "extends": "./.eslintrc-base.json", "env": { "browser": false, "node": true }, "parserOptions": { "ecmaVersion": 2017 }, "plugins": [ "promise" ] } angular.js-1.7.9/.eslintrc-todo.json000066400000000000000000000020231356472325200173410ustar00rootroot00000000000000{ // This config contains proposed rules that we'd like to have enabled but haven't // converted the code to adhere yet. If a decision comes to not enable one of these // rules, it should be removed from the file. Every rule that got enabled in the // end should be moved from here to a respective section in .eslintrc.json "rules": { // Rules are divided into sections from http://eslint.org/docs/rules/ // Best practices "complexity": ["error", 10], "dot-notation": "error", "dot-location": ["error", "property"], // Stylistic issues "block-spacing": ["error", "always"], "comma-spacing": "error", "id-blacklist": ["error", "event"], "indent": ["error", 2], "key-spacing": ["error", { "beforeColon": false, "afterColon": true, "mode": "minimum" }], "object-curly-spacing": ["error", "never"], "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], "operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before" }}] } } angular.js-1.7.9/.eslintrc.json000066400000000000000000000000711356472325200163770ustar00rootroot00000000000000{ "root": true, "extends": "./.eslintrc-node.json" } angular.js-1.7.9/.gitattributes000066400000000000000000000001771356472325200165050ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # JS files must always use LF for tools to work *.js eol=lf angular.js-1.7.9/.github/000077500000000000000000000000001356472325200151455ustar00rootroot00000000000000angular.js-1.7.9/.github/ISSUE_TEMPLATE.md000066400000000000000000000035701356472325200176570ustar00rootroot00000000000000# AngularJS is in LTS mode We are no longer accepting changes that are not critical bug fixes into this project. See https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c for more detail. **I'm submitting a ...** - [ ] regression from 1.7.0 - [ ] security issue - [ ] issue caused by a new browser version - [ ] other **Current behavior:** **Expected / new behavior:** **Minimal reproduction of the problem with instructions:** **AngularJS version:** 1.7.x **Browser:** [all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView | Opera XX ] **Anything else:** angular.js-1.7.9/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000021001356472325200207370ustar00rootroot00000000000000# AngularJS is in LTS mode We are no longer accepting changes that are not critical bug fixes into this project. See https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c for more detail. **Does this PR fix a regression since 1.7.0, a security flaw, or a problem caused by a new browser version?** **What is the current behavior? (You can also link to an open issue here)** **What is the new behavior (if this is a feature change)?** **Does this PR introduce a breaking change?** **Please check if the PR fulfills these requirements** - [ ] The commit message follows our [guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) - [ ] Fix/Feature: [Docs](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#documentation) have been added/updated - [ ] Fix/Feature: Tests have been added; existing tests pass **Other information**: angular.js-1.7.9/.gitignore000066400000000000000000000004521356472325200155760ustar00rootroot00000000000000/build/ /deploy/ /benchpress-build/ .DS_Store gen_docs.disable test.disable regression/temp*.html performance/temp*.html .idea/workspace.xml *~ *.swp angular.js.tmproj node_modules/ angular.xcodeproj .idea *.iml .agignore .lvimrc libpeerconnection.log npm-debug.log /tmp/ .vscode *.log *.stackdump angular.js-1.7.9/.mailmap000066400000000000000000000025101356472325200152240ustar00rootroot00000000000000Andres Ornelas Caitlin Potter Caitlin Potter Di Peng Di Peng Georgios Kalpakas Georgios Kalpakas Julie Ralph Lucas Galfaso Martin Staffa Martin Staffa Matias Niemelä Michał Gołębiowski-Owczarek Misko Hevery Misko Hevery Igor Minar Igor Minar Igor Minar Igor Minar Pawel Kozlowski Peter Bacon Darwin Rodric Haddad Shahar Talmi Shahar Talmi Shyam Seshadri Shyam Seshadri Vojta Jina Vojta Jina Vojta Jina angular.js-1.7.9/.nvmrc000066400000000000000000000000021356472325200147230ustar00rootroot000000000000008 angular.js-1.7.9/.travis.yml000066400000000000000000000071771356472325200157320ustar00rootroot00000000000000language: node_js sudo: false node_js: - '8' cache: yarn: true branches: except: - "/^g3_.*$/" env: matrix: - JOB=ci-checks - JOB=unit-core BROWSER_PROVIDER=saucelabs - JOB=unit-jquery BROWSER_PROVIDER=saucelabs - JOB=unit-modules BROWSER_PROVIDER=saucelabs - JOB=docs-app BROWSER_PROVIDER=saucelabs - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs global: - SAUCE_USERNAME=angular-ci - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987 - LOGS_DIR=/tmp/angular-build/logs - BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready - secure: oTBjhnOKhs0qDSKTf7fE4f6DYiNDPycvB7qfSF5QRIbJK/LK/J4UtFwetXuXj79HhUZG9qnoT+5e7lPaiaMlpsIKn9ann7ffqFWN1E8TMtpJF+AGigx3djYElwfgf5nEnFUFhwjFzvbfpZNnxVGgX5YbIZpe/WUbHkP4ffU0Wks= before_install: - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.10.1 - export PATH="$HOME/.yarn/bin:$PATH" before_script: - du -sh ./node_modules || true - "./scripts/travis/before_build.sh" script: - "./scripts/travis/build.sh" after_script: - "./scripts/travis/tear_down_browser_provider.sh" - "./scripts/travis/print_logs.sh" notifications: webhooks: urls: - https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2 - http://104.197.9.155:8484/hubot/travis/activity #hubot-server on_success: always # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: always # default: false jobs: include: - stage: deploy # Don't deploy from PRs. Only deploy from our default branches, or if commit is tagged. # This is a Travis-specific boolean language: https://docs.travis-ci.com/user/conditional-builds-stages-jobs#Specifying-conditions # The deployment logic for pushed branches is further defined in scripts\travis\build.sh if: type != pull_request and (branch =~ ^(v1\.\d+\.x|master)$ or tag IS present) env: - JOB=deploy before_script: skip script: # Export the variables into the current process - . ./scripts/travis/build.sh - "echo DEPLOY_DOCS: $DEPLOY_DOCS, DEPLOY_CODE: $DEPLOY_CODE" after_script: skip # Work around the 10min Travis timeout so the code.angularjs firebase+gcs code deploy can complete # Only run the keep_alive once (before_deploy is run for each provider) before_deploy: | if ! [ "$BEFORE_DEPLOY_RUN" ]; then export BEFORE_DEPLOY_RUN=1; function keep_alive() { while true; do echo -en "\a" sleep 10 done } keep_alive & fi deploy: - provider: firebase # the upload folder for firebase is configured in /firebase.json skip_cleanup: true project: docs-angularjs-org-9p2 token: secure: $FIREBASE_TOKEN on: repo: angular/angular.js all_branches: true condition: "$DEPLOY_DOCS == true" - provider: gcs skip_cleanup: true access_key_id: GOOGLDB7W2J3LFHICF3R secret_access_key: secure: tHIFdSq55qkyZf9zT/3+VkhUrTvOTMuswxXU3KyWaBrSieZqG0UnUDyNm+n3lSfX95zEl/+rJAWbfvhVSxZi13ndOtvRF+MdI1cvow2JynP0aDSiPffEvVrZOmihD6mt2SlMfhskr5FTduQ69kZG6DfLcve1PPDaIwnbOv3phb8= bucket: code-angularjs-org-338b8.appspot.com local-dir: deploy/code detect_encoding: true # detects gzip compression on: repo: angular/angular.js all_branches: true condition: "$DEPLOY_CODE == true" angular.js-1.7.9/CHANGELOG.md000066400000000000000000035142251356472325200154320ustar00rootroot00000000000000 # 1.7.9 pollution-eradication (2019-11-19) ## Bug Fixes - **angular.merge:** do not merge __proto__ property ([726f49](https://github.com/angular/angular.js/commit/726f49dcf6c23106ddaf5cfd5e2e592841db743a)) - **ngStyle:** correctly remove old style when new style value is invalid ([5edd25](https://github.com/angular/angular.js/commit/5edd25364f617083363dc2bd61f9230b38267578), [#16860](https://github.com/angular/angular.js/issues/16860), [#16868](https://github.com/angular/angular.js/issues/16868)) # 1.7.8 enthusiastic-oblation (2019-03-11) ## Bug Fixes - **required:** correctly validate required on non-input element surrounded by ngIf ([a4c7bd](https://github.com/angular/angular.js/commit/a4c7bdccd76c39c30e33f6215da9a00cc8acde2c), [#16830](https://github.com/angular/angular.js/issues/16830), [#16836](https://github.com/angular/angular.js/issues/16836)) # 1.7.7 kingly-exiting (2019-02-04) ## Bug Fixes - **ngRequired:** set error correctly when inside ngRepeat and false by default ([5ad4f5](https://github.com/angular/angular.js/commit/5ad4f5562c37b1cb575e3e5fddd96e9dd10408e2), [#16814](https://github.com/angular/angular.js/issues/16814), [#16820](https://github.com/angular/angular.js/issues/16820)) # 1.7.6 gravity-manipulation (2019-01-17) ## Bug Fixes - **$compile:** fix ng-prop-* with undefined values ([772440](https://github.com/angular/angular.js/commit/772440cdaf9a9bfa40de1675e20a5f0e356089ed), [#16797](https://github.com/angular/angular.js/issues/16797), [#16798](https://github.com/angular/angular.js/issues/16798)) - **compile:** properly handle false value for boolean attrs with jQuery ([27486b](https://github.com/angular/angular.js/commit/27486bd15e70946ece2ba713e4e8654b7f9bddad), [#16778](https://github.com/angular/angular.js/issues/16778), [#16779](https://github.com/angular/angular.js/issues/16779)) - **ngRepeat:** - fix reference to last collection value remaining across linkages ([cf919a](https://github.com/angular/angular.js/commit/cf919a6fb7fc655f3fa37a74899a797ea5b8073e)) - fix trackBy function being invoked with incorrect scope ([d4d103](https://github.com/angular/angular.js/commit/d4d1031bcd9b30ae6a58bd60a79bcc9d20f0f2b7), [#16776](https://github.com/angular/angular.js/issues/16776), [#16777](https://github.com/angular/angular.js/issues/16777)) - **aria/ngClick:** check if element is `contenteditable` before blocking spacebar ([289374](https://github.com/angular/angular.js/commit/289374a43c1b2fd715ddf7455db225b17afebbaf), [#16762](https://github.com/angular/angular.js/issues/16762)) - **input:** prevent browsers from autofilling hidden inputs ([7cbb10](https://github.com/angular/angular.js/commit/7cbb1044fcb3576cdad791bd22ebea3dfd533ff8)) - **Angular:** add workaround for Safari / Webdriver problem ([eb49f6](https://github.com/angular/angular.js/commit/eb49f6b7555cfd7ab03fd35581adb6b4bd49044e)) - **$browser:** normalize inputted URLs ([2f72a6](https://github.com/angular/angular.js/commit/2f72a69ded53a122afad3ec28d91f9bd2f41eb4f), [#16606](https://github.com/angular/angular.js/issues/16606)) - **interpolate:** do not create directives for constant media URL attributes ([90a41d](https://github.com/angular/angular.js/commit/90a41d415c83abdbf28317f49df0fd0a7e07db86), [#16734](https://github.com/angular/angular.js/issues/16734)) - **$q:** allow third-party promise libraries ([eefaa7](https://github.com/angular/angular.js/commit/eefaa76a90dbef08fdc7d734a205cc2de50d9f91), [#16164](https://github.com/angular/angular.js/issues/16164), [#16471](https://github.com/angular/angular.js/issues/16471)) - **urlUtils:** make IPv6 URL's hostname wrapped in square brackets in IE/Edge ([0e1bd7](https://github.com/angular/angular.js/commit/0e1bd7822e61822a48b8fd7ba5913a8702e6dabf), [#16692](https://github.com/angular/angular.js/issues/16692), [#16715](https://github.com/angular/angular.js/issues/16715)) - **ngAnimateSwap:** make it compatible with `ngIf` on the same element ([b27080](https://github.com/angular/angular.js/commit/b27080d52546409fb4e483f212f03616e2ca8037), [#16616](https://github.com/angular/angular.js/issues/16616), [#16729](https://github.com/angular/angular.js/issues/16729)) - **ngMock:** make matchLatestDefinitionEnabled work ([3cdffc](https://github.com/angular/angular.js/commit/3cdffcecbae71189b4db69b57fadda6608a23b61), [#16702](https://github.com/angular/angular.js/issues/16702)) - **ngStyle:** skip setting empty value when new style has the property ([d6098e](https://github.com/angular/angular.js/commit/d6098eeb1c9510d599e9bd3cfdba7dd21e7a55a5), [#16709](https://github.com/angular/angular.js/issues/16709)) ## Performance Improvements - **input:** prevent multiple validations on initialization ([692622](https://github.com/angular/angular.js/commit/69262239632027b373258e75c670b89132ad9edb), [#14691](https://github.com/angular/angular.js/issues/14691), [#16760](https://github.com/angular/angular.js/issues/16760)) # 1.7.5 anti-prettification (2018-10-04) ## Bug Fixes - **ngClass:** do not break on invalid values ([f3a565](https://github.com/angular/angular.js/commit/f3a565872d802c94bb213944791b11b483d52f73), [#16697](https://github.com/angular/angular.js/issues/16697), [#16699](https://github.com/angular/angular.js/issues/16699)) # 1.7.4 interstellar-exploration (2018-09-07) ## Bug Fixes - **ngAria.ngClick:** prevent default event on space/enter only for non-interactive elements ([61b335](https://github.com/angular/angular.js/commit/61b33543ff8e7f32464dec98a46bf0a35e9b03a4), [#16664](https://github.com/angular/angular.js/issues/16664), [#16680](https://github.com/angular/angular.js/issues/16680)) - **ngAnimate:** remove the "prepare" classes with multiple structural animations ([3105b2](https://github.com/angular/angular.js/commit/3105b2c26a71594c4e7904efc18f4b2e9da25b1b), [#16681](https://github.com/angular/angular.js/issues/16681), [#16677](https://github.com/angular/angular.js/issues/16677)) - **$route:** correctly extract path params if the path contains a question mark or a hash ([2ceeb7](https://github.com/angular/angular.js/commit/2ceeb739f35e01fcebcabac4beeeb7684ae9f86d)) - **ngHref:** allow numbers and other objects in interpolation ([30084c](https://github.com/angular/angular.js/commit/30084c13699c814ff6703d7aa2d3947a9b2f7067), [#16652](https://github.com/angular/angular.js/issues/16652), [#16626](https://github.com/angular/angular.js/issues/16626)) - **select:** allow to select first option with value `undefined` ([668a33](https://github.com/angular/angular.js/commit/668a33da3439f17e61dfa8f6d9b114ebde8c9d87), [#16653](https://github.com/angular/angular.js/issues/16653), [#16656](https://github.com/angular/angular.js/issues/16656)) # 1.7.3 eventful-proposal (2018-08-03) ## Bug Fixes - **$location:** - fix infinite recursion/digest on URLs with special characters ([e68697](https://github.com/angular/angular.js/commit/e68697e2e30695f509e6c2c1e43c2c02b7af41f0), [#16592](https://github.com/angular/angular.js/issues/16592), [#16611](https://github.com/angular/angular.js/issues/16611)) - avoid unnecessary `$locationChange*` events due to empty hash ([1144b1](https://github.com/angular/angular.js/commit/1144b1eccb886ea0e4a80bcb07d38a305c3263b4), [#16632](https://github.com/angular/angular.js/issues/16632), [#16636](https://github.com/angular/angular.js/issues/16636)) - **ngMock.$httpBackend:** - pass failed HTTP expectations to `$exceptionHandler` ([4adbf8](https://github.com/angular/angular.js/commit/4adbf82a84a564a8d3f0982c17a64c6163200bcd), [#16644](https://github.com/angular/angular.js/issues/16644)) - correctly ignore query params in {expect,when}Route ([be417f](https://github.com/angular/angular.js/commit/be417f28549e184fbc3c7f74251ac21fca965ae8), [#14173](https://github.com/angular/angular.js/issues/14173), [#16589](https://github.com/angular/angular.js/issues/16589)) - **Angular:** add workaround for Safari / Webdriver problem ([0a1db2](https://github.com/angular/angular.js/commit/0a1db2ad5f8da6902b1711a738ae4177ce9685fa), [#16645](https://github.com/angular/angular.js/issues/16645)) - **$animate:** avoid memory leak with `$animate.enabled(element, enabled)` ([4bd424](https://github.com/angular/angular.js/commit/4bd424690612885ca06028e9b27de585edc3d3c3), [#16649](https://github.com/angular/angular.js/issues/16649)) - **$compile:** - use correct parent element when requiring on html element ([05ac70](https://github.com/angular/angular.js/commit/05ac702bc7edae5f89c363ea661774910735ea8b), [#16535](https://github.com/angular/angular.js/issues/16535), [#16647](https://github.com/angular/angular.js/issues/16647)) - work around Firefox `DocumentFragment` bug ([10973c](https://github.com/angular/angular.js/commit/10973c3366676ac8e5b2728b1e006cdef4ea197e), [#16607](https://github.com/angular/angular.js/issues/16607), [#16615](https://github.com/angular/angular.js/issues/16615)) - **ngEventDirs:** - pass error in handler to $exceptionHandler when event was triggered in a digest ([688211](https://github.com/angular/angular.js/commit/6882113bc194fb10081db9bab3dd7d69dd59f311)) - don't wrap the event handler in $apply if already in $digest ([535ee3](https://github.com/angular/angular.js/commit/535ee32a0b4881c9fd526fb5e0ffc10919ba1800), [#14673](https://github.com/angular/angular.js/issues/14673), [#14674](https://github.com/angular/angular.js/issues/14674)) - **angular.element:** do not break on `cleanData()` if `_data()` returns undefined ([7cf4a2](https://github.com/angular/angular.js/commit/7cf4a2933cb017e45b0c97b0a836cbbd905ee31a), [#16641](https://github.com/angular/angular.js/issues/16641), [#16642](https://github.com/angular/angular.js/issues/16642)) - **ngAria:** do not scroll when pressing spacebar on custom buttons ([3a517c](https://github.com/angular/angular.js/commit/3a517c25f677294a7a9eca1660654a3edcc9e103), [#14665](https://github.com/angular/angular.js/issues/14665), [#16604](https://github.com/angular/angular.js/issues/16604)) ## New Features - **$compile:** add support for arbitrary DOM property and event bindings ([a5914c](https://github.com/angular/angular.js/commit/a5914c94a8fa5b1eceeab9e4e6849cbf467bc26d), [#16428](https://github.com/angular/angular.js/issues/16428), [#16235](https://github.com/angular/angular.js/issues/16235), [#16614](https://github.com/angular/angular.js/issues/16614)) - **ngMock:** add `$flushPendingTasks()` and `$verifyNoPendingTasks()` ([6f7674](https://github.com/angular/angular.js/commit/6f7674a7d063d434205f75f5b861f167e8125999), [#14336](https://github.com/angular/angular.js/issues/14336)) - **core:** implement more granular pending task tracking ([17b139](https://github.com/angular/angular.js/commit/17b139f107e5471a9351af638093a8e13a69e42a)) - **$animate:** add option data to event callbacks ([fc64e6](https://github.com/angular/angular.js/commit/fc64e6807642512b567deb52b497bd2bff570a1f), [#12697](https://github.com/angular/angular.js/issues/12697), [#13059](https://github.com/angular/angular.js/issues/13059)) - **form.FormController:** add $getControls() ([c9d1e6](https://github.com/angular/angular.js/commit/c9d1e690aa597283373b78e646676fa8f1ba1b4d), [#16601](https://github.com/angular/angular.js/issues/16601), [#14749](https://github.com/angular/angular.js/issues/14749), [#14517](https://github.com/angular/angular.js/issues/14517), [#13202](https://github.com/angular/angular.js/issues/13202)) - **ngModelOptions:** add `timeStripZeroSeconds` and `timeSecondsFormat` ([b68221](https://github.com/angular/angular.js/commit/b682213d72d65c996a6a31ea57b79d4c4f4e3c98), [#10721](https://github.com/angular/angular.js/issues/10721), [#16510](https://github.com/angular/angular.js/issues/16510), [#16584](https://github.com/angular/angular.js/issues/16584)) ## Performance Improvements - **ngAnimate:** avoid repeated calls to addClass/removeClass when animation has no duration ([093635](https://github.com/angular/angular.js/commit/0936353e9a03f072bc3c4056888fd154a96530ef), [#14165](https://github.com/angular/angular.js/issues/14165), [#14166](https://github.com/angular/angular.js/issues/14166), [#16613](https://github.com/angular/angular.js/issues/16613)) # 1.7.2 extreme-compatiplication (2018-06-12) In the previous release, we removed a private, undocumented API that was no longer used by AngularJS. It turned out that several popular UI libraries (such as [AngularJS Material](https://material.angularjs.org/), [UI Bootstrap](https://angular-ui.github.io/bootstrap/), [ngDialog](http://likeastore.github.io/ngDialog/) and probably others) relied on that API. In order to avoid unnecessary pain for developers, this release reverts the removal of the private API and restores compatibility of the aforementioned libraries with the latest AngularJS. ## Reverts - **$compile:** remove `preAssignBindingsEnabled` leftovers ([2da495](https://github.com/angular/angular.js/commit/2da49504065e9e2b71a7a5622e45118d8abbe87e), [#16580](https://github.com/angular/angular.js/pull/16580), [a81232](https://github.com/angular/angular.js/commit/a812327acda8bc890a4c4e809f0debb761c29625), [#16595](https://github.com/angular/angular.js/pull/16595)) # 1.7.1 momentum-defiance (2018-06-08) ## Bug Fixes - **$compile:** support transcluding multi-element directives ([789db8](https://github.com/angular/angular.js/commit/789db83a8ae0e2db5db13289b2c29e56093d967a), [#15554](https://github.com/angular/angular.js/issues/15554), [#15555](https://github.com/angular/angular.js/issues/15555)) - **ngModel:** do not throw if view value changes on destroyed scope ([2b6c98](https://github.com/angular/angular.js/commit/2b6c9867369fd3ef1ddb687af1153478ab62ee1b), [#16583](https://github.com/angular/angular.js/issues/16583), [#16585](https://github.com/angular/angular.js/issues/16585)) ## New Features - **$compile:** add one-way collection bindings ([f9d1ca](https://github.com/angular/angular.js/commit/f9d1ca20c38f065f15769fbe23aee5314cb58bd4), [#14039](https://github.com/angular/angular.js/issues/14039), [#16553](https://github.com/angular/angular.js/issues/16553), [#15874](https://github.com/angular/angular.js/issues/15874)) - **ngRef:** add directive to publish controller, or element into scope ([bf841d](https://github.com/angular/angular.js/commit/bf841d35120bf3c4655fde46af4105c85a0f1cdc), [#16511](https://github.com/angular/angular.js/issues/16511)) - **errorHandlingConfig:** add option to exclude error params from url ([3d6c45](https://github.com/angular/angular.js/commit/3d6c45d76e30b1b3c4eb9672cf4a93e5251c06b3), [#14744](https://github.com/angular/angular.js/issues/14744), [#15707](https://github.com/angular/angular.js/issues/15707), [#16283](https://github.com/angular/angular.js/issues/16283), [#16299](https://github.com/angular/angular.js/issues/16299), [#16591](https://github.com/angular/angular.js/issues/16591)) - **ngAria:** add support for ignoring a specific element ([7d9d38](https://github.com/angular/angular.js/commit/7d9d387195292cb5e04984602b752d31853cfea6), [#14602](https://github.com/angular/angular.js/issues/14602), [#14672](https://github.com/angular/angular.js/issues/14672), [#14833](https://github.com/angular/angular.js/issues/14833)) - **ngCookies:** support samesite option ([10a229](https://github.com/angular/angular.js/commit/10a229ce1befdeaf6295d1635dc11391c252a91a), [#16543](https://github.com/angular/angular.js/issues/16543), [#16544](https://github.com/angular/angular.js/issues/16544)) - **ngMessages:** add support for default message ([a8c263](https://github.com/angular/angular.js/commit/a8c263c1947cc85ee60b4732f7e4bcdc7ba463e8), [#12008](https://github.com/angular/angular.js/issues/12008), [#12213](https://github.com/angular/angular.js/issues/12213), [#16587](https://github.com/angular/angular.js/issues/16587)) - **ngMock, ngMockE2E:** add option to match latest definition for `$httpBackend` request ([773f39](https://github.com/angular/angular.js/commit/773f39c9345479f5f8b6321236ce6ad96f77aa92), [#16251](https://github.com/angular/angular.js/issues/16251), [#11637](https://github.com/angular/angular.js/issues/11637), [#16560](https://github.com/angular/angular.js/issues/16560)) - **$route:** add support for the `reloadOnUrl` configuration option ([f4f571](https://github.com/angular/angular.js/commit/f4f571efdf86d6acbcd5c6b1de66b4b33a259125), [#7925](https://github.com/angular/angular.js/issues/7925), [#15002](https://github.com/angular/angular.js/issues/15002)) # 1.7.0 nonexistent-physiology (2018-05-11) **Here are the full changes for the release of 1.7.0 that are not already released in the 1.6.x branch, which includes commits from 1.7.0-rc.0 and commits from 1.7.0 directly.** 1.7.0 is the last scheduled release of AngularJS that includes breaking changes. 1.7.x patch releases will continue to receive bug fixes and non-breaking features until AngularJS enters Long Term Support mode (LTS) on July 1st 2018. ## Bug Fixes - **input:** - listen on "change" instead of "click" for radio/checkbox ngModels ([656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e), [#4516](https://github.com/angular/angular.js/issues/4516), [#14667](https://github.com/angular/angular.js/issues/14667), [#14685](https://github.com/angular/angular.js/issues/14685)) - **input\[number\]:** validate min/max against viewValue ([aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec), [#12761](https://github.com/angular/angular.js/issues/12761), [#16325](https://github.com/angular/angular.js/issues/16325)) - **input\[date\]:** correctly parse 2-digit years ([627180](https://github.com/angular/angular.js/commit/627180fb71b92048d5b9ca2606b9eff1fd99387e), [#16537](https://github.com/angular/angular.js/issues/16537), [#16539](https://github.com/angular/angular.js/issues/16539)) - **jqLite:** make removeData() not remove event handlers ([b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a), [#15869](https://github.com/angular/angular.js/issues/15869), [#16512](https://github.com/angular/angular.js/issues/16512)) - **$compile:** - remove the preAssignBindingsEnabled flag ([38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb), [#15782](https://github.com/angular/angular.js/issues/15782)) - add `base[href]` to the list of RESOURCE_URL context attributes ([1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e), [#15597](https://github.com/angular/angular.js/issues/15597)) - **$interval:** throw when trying to cancel non-$interval promise ([a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4), [#16424](https://github.com/angular/angular.js/issues/16424), [#16476](https://github.com/angular/angular.js/issues/16476)) - **$timeout:** throw when trying to cancel non-$timeout promise ([336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828), [#16424](https://github.com/angular/angular.js/issues/16424), [#16476](https://github.com/angular/angular.js/issues/16476)) - **$cookies:** remove the deprecated $cookieStore factory ([73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77), [#16465](https://github.com/angular/angular.js/issues/16465)) - **$resource:** fix interceptors and success/error callbacks ([ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd), [#6731](https://github.com/angular/angular.js/issues/6731), [#9334](https://github.com/angular/angular.js/issues/9334), [#6865](https://github.com/angular/angular.js/issues/6865), [#16446](https://github.com/angular/angular.js/issues/16446)) - **$templateRequest:** - give tpload error the correct namespace ([c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)) - always return the template that is stored in the cache ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e), [#16225](https://github.com/angular/angular.js/issues/16225)) - **$animate:** let cancel() reject the runner promise ([16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83), [#14204](https://github.com/angular/angular.js/issues/14204), [#16373](https://github.com/angular/angular.js/issues/16373)) - **ngTouch:** - deprecate the module and its contents ([67f54b](https://github.com/angular/angular.js/commit/67f54b660038de2b4346b3e76d66a8dc8ccb1f9b), [#16427](https://github.com/angular/angular.js/issues/16427), [#16431](https://github.com/angular/angular.js/issues/16431)) - remove ngClick override, `$touchProvider`, and `$touch` ([11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50), [#15761](https://github.com/angular/angular.js/issues/15761), [#15755](https://github.com/angular/angular.js/issues/15755)) - **ngScenario:** completely remove the angular scenario runner ([0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60), [#9405](https://github.com/angular/angular.js/issues/9405)) - **form:** set $submitted to true on child forms when parent is submitted ([223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77), [#10071](https://github.com/angular/angular.js/issues/10071)) - **$rootScope:** - provide correct value of one-time bindings in watchGroup ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)) - don't allow explicit digest calls to affect $evalAsync ([02c046](https://github.com/angular/angular.js/commit/02c04690da16a9bef55694f5db0b8368dc0125c9), [#15127](https://github.com/angular/angular.js/issues/15127), [#15494](https://github.com/angular/angular.js/issues/15494)) - **ngAria:** do not set aria attributes on input[type="hidden"] ([6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b), [#15113](https://github.com/angular/angular.js/issues/15113), [#16367](https://github.com/angular/angular.js/issues/16367)) - **ngModel, input:** improve handling of built-in named parsers ([74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140), [#14292](https://github.com/angular/angular.js/issues/14292), [#10076](https://github.com/angular/angular.js/issues/10076), [#16347](https://github.com/angular/angular.js/issues/16347)) - **$httpParamSerializerJQLike:** - call functions as jQuery does ([a784fa](https://github.com/angular/angular.js/commit/a784fab605d825f1158c6292b3c42f8c4a502fdf), [#16138](https://github.com/angular/angular.js/issues/16138), [#16139](https://github.com/angular/angular.js/issues/16139)) - follow jQuery for `null` and `undefined` ([301fdd](https://github.com/angular/angular.js/commit/301fdda648680d89ccab607c413a7ddede7b0165)) - **$parse:** - do not pass scope/locals to interceptors of one-time bindings ([87a586](https://github.com/angular/angular.js/commit/87a586eb9a23cfd0d0bb681cc778b4b8e5c8451d)) - always pass the intercepted value to watchers ([2ee503](https://github.com/angular/angular.js/commit/2ee5033967d5f87a516bad137686b0592e25d26b), [#16021](https://github.com/angular/angular.js/issues/16021)) - respect the interceptor.$stateful flag ([de7403](https://github.com/angular/angular.js/commit/de74034ddf6f92505ccdb61be413a6df2c723f87)) - **Angular:** remove `angular.lowercase` and `angular.uppercase` ([1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de), [#15445](https://github.com/angular/angular.js/issues/15445)) - **$controller:** remove instantiating controllers defined on window ([e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16), [#15349](https://github.com/angular/angular.js/issues/15349), [#15762](https://github.com/angular/angular.js/issues/15762)) ## New Features - **angular.isArray:** support Array subclasses in `angular.isArray()` ([e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948), [#15533](https://github.com/angular/angular.js/issues/15533), [#15541](https://github.com/angular/angular.js/issues/15541)) - **$sce:** handle URL sanitization through the `$sce` service ([1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)) - **orderBy:** consider `null` and `undefined` greater than other values ([1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8), [#15294](https://github.com/angular/angular.js/issues/15294), [#16376](https://github.com/angular/angular.js/issues/16376)) - **$resource:** add support for `request` and `requestError` interceptors (#15674) ([240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded), [#5146](https://github.com/angular/angular.js/issues/5146)) - **ngModelOptions:** add debounce catch-all + allow debouncing 'default' only ([55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68), [#15411](https://github.com/angular/angular.js/issues/15411), [#16335](https://github.com/angular/angular.js/issues/16335)) - **$compile:** lower the `xlink:href` security context for SVG's `a` and `image` elements ([6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd), [#15736](https://github.com/angular/angular.js/issues/15736)) ## Performance Improvements - **$rootScope:** allow $watchCollection use of expression input watching ([97b00c](https://github.com/angular/angular.js/commit/97b00ca497676aaff8a803762a9f8c7ff4aa24dd)) - **ngStyle:** use $watchCollection ([15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0), [#15947](https://github.com/angular/angular.js/issues/15947)) - **$compile:** do not use deepWatch in literal one-way bindings ([fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727), [#15301](https://github.com/angular/angular.js/issues/15301)) ## Breaking Changes ### **jqLite** due to: - **[b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**: make removeData() not remove event handlers Before this commit `removeData()` invoked on an element removed its event handlers as well. If you want to trigger a full cleanup of an element, change: ```js elem.removeData(); ``` to: ```js angular.element.cleanData(elem); ``` In most cases, though, cleaning up after an element is supposed to be done only when it's removed from the DOM as well; in such cases the following: ```js elem.remove(); ``` will remove event handlers as well. ### **$cookies** due to: - **[73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**: remove the deprecated $cookieStore factory The $cookieStore has been removed. Migrate to the $cookies service. Note that for object values you need to use the `putObject` & `getObject` methods as `get`/`put` will not correctly save/retrieve them. Before: ```js $cookieStore.put('name', {key: 'value'}); $cookieStore.get('name'); // {key: 'value'} $cookieStore.remove('name'); ``` After: ```js $cookies.putObject('name', {key: 'value'}); $cookies.getObject('name'); // {key: 'value'} $cookies.remove('name'); ``` ### **$resource** due to: - **[ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**: fix interceptors and success/error callbacks If you are not using `success` or `error` callbacks with `$resource`, your app should not be affected by this change. If you are using `success` or `error` callbacks (with or without response interceptors), one (subtle) difference is that throwing an error inside the callbacks will not propagate to the returned `$promise`. Therefore, you should try to use the promises whenever possible. E.g.: ```js // Avoid User.query(function onSuccess(users) { throw new Error(); }). $promise. catch(function onError() { /* Will not be called. */ }); // Prefer User.query(). $promise. then(function onSuccess(users) { throw new Error(); }). catch(function onError() { /* Will be called. */ }); ``` Finally, if you are using `success` or `error` callbacks with response interceptors, the callbacks will now always run _after_ the interceptors (and wait for them to resolve in case they return a promise). Previously, the `error` callback was called before the `responseError` interceptor and the `success` callback was synchronously called after the `response` interceptor. E.g.: ```js var User = $resource('/api/users/:id', {id: '@id'}, { get: { method: 'get', interceptor: { response: function(response) { console.log('responseInterceptor-1'); return $timeout(1000).then(function() { console.log('responseInterceptor-2'); return response.resource; }); }, responseError: function(response) { console.log('responseErrorInterceptor-1'); return $timeout(1000).then(function() { console.log('responseErrorInterceptor-2'); return $q.reject('Ooops!'); }); } } } }); var onSuccess = function(value) { console.log('successCallback', value); }; var onError = function(error) { console.log('errorCallback', error); }; // Assuming the following call is successful... User.get({id: 1}, onSuccess, onError); // Old behavior: // responseInterceptor-1 // successCallback, {/* Promise object */} // responseInterceptor-2 // New behavior: // responseInterceptor-1 // responseInterceptor-2 // successCallback, {/* User object */} // Assuming the following call returns an error... User.get({id: 2}, onSuccess, onError); // Old behavior: // errorCallback, {/* Response object */} // responseErrorInterceptor-1 // responseErrorInterceptor-2 // New behavior: // responseErrorInterceptor-1 // responseErrorInterceptor-2 // errorCallback, Ooops! ``` - **[240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**: add support for `request` and `requestError` interceptors (#15674) Previously, calling a `$resource` method would synchronously call `$http`. Now, it will be called asynchronously (regardless if a `request`/`requestError` interceptor has been defined. This is not expected to affect applications at runtime, since the overall operation is asynchronous already, but may affect assertions in tests. For example, if you want to assert that `$http` has been called with specific arguments as a result of a `$resource` call, you now need to run a `$digest` first, to ensure the (possibly empty) request interceptor promise has been resolved. Before: ```js it('...', function() { $httpBackend.expectGET('/api/things').respond(...); var Things = $resource('/api/things'); Things.query(); expect($http).toHaveBeenCalledWith(...); }); ``` After: ```js it('...', function() { $httpBackend.expectGET('/api/things').respond(...); var Things = $resource('/api/things'); Things.query(); $rootScope.$digest(); expect($http).toHaveBeenCalledWith(...); }); ``` ### **$templateRequest**: - due to **[c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**: give tpload error the correct namespace Previously the `tpload` error was namespaced to `$compile`. If you have code that matches errors of the form `[$compile:tpload]` it will no longer run. You should change the code to match `[$templateRequest:tpload]`. - due to **([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**: always return the template that is stored in the cache The service now returns the result of `$templateCache.put()` when making a server request to the template. Previously it would return the content of the response directly. This now means if you are decorating `$templateCache.put()` to manipulate the template, you will now get this manipulated result also on the first `$templateRequest` rather than only on subsequent calls (when the template is retrived from the cache). In practice this should not affect any apps, as it is unlikely that they rely on the template being different in the first and subsequent calls. ### **$animate** due to: - **[16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**: let cancel() reject the runner promise $animate.cancel(runner) now rejects the underlying promise and calls the catch() handler on the runner returned by $animate functions (enter, leave, move, addClass, removeClass, setClass, animate). Previously it would resolve the promise as if the animation had ended successfully. Example: ```js var runner = $animate.addClass('red'); runner.then(function() { console.log('success')}); runner.catch(function() { console.log('cancelled')}); runner.cancel(); ``` Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'. To migrate, add a catch() handler to your animation runners. ### **angular.isArray** due to: - **[e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**: support Array subclasses in `angular.isArray()` Previously, `angular.isArray()` was an alias for `Array.isArray()`. Therefore, objects that prototypally inherit from `Array` where not considered arrays. Now such objects are considered arrays too. This change affects several other methods that use `angular.isArray()` under the hood, such as `angular.copy()`, `angular.equals()`, `angular.forEach()`, and `angular.merge()`. This in turn affects how dirty checking treats objects that prototypally inherit from `Array` (e.g. MobX observable arrays). AngularJS will now be able to handle these objects better when copying or watching. ### **$sce** : - due to **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service If you use `attrs.$set` for URL attributes (a[href] and img[src]) there will no longer be any automated sanitization of the value. This is in line with other programmatic operations, such as writing to the innerHTML of an element. If you are programmatically writing URL values to attributes from untrusted input then you must sanitize it yourself. You could write your own sanitizer or copy the private `$$sanitizeUri` service. Note that values that have been passed through the `$interpolate` service within the `URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize these values again. - due to **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service binding `trustAs()` and the short versions (`trustAsResourceUrl()` et al.) to `ngSrc`, `ngSrcset`, and `ngHref` will now raise an infinite digest error: ```js $scope.imgThumbFn = function(id) { return $sce.trustAsResourceUrl(someService.someUrl(id)); }; ``` ```html ``` This is because the `$interpolate` service is now responsible for sanitizing the attribute value, and its watcher receives a new object from `trustAs()` on every digest. To migrate, compute the trusted value only when the input value changes: ```js $scope.$watch('imgId', function(id) { $scope.imgThumb = $sce.trustAsResourceUrl(someService.someUrl(id)); }); ``` ```html ``` ### **orderBy** due to: - **[1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**: consider `null` and `undefined` greater than other values When using `orderBy` to sort arrays containing `null` values, the `null` values will be considered "greater than" all other values, except for `undefined`. Previously, they were sorted as strings. This will result in different (but more intuitive) sorting order. Before: ```js orderByFilter(['a', undefined, 'o', null, 'z']); //--> 'a', null, 'o', 'z', undefined ``` After: ```js orderByFilter(['a', undefined, 'o', null, 'z']); //--> 'a', 'o', 'z', null, undefined ``` ### **ngScenario** due to: - **[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**: completely remove the angular scenario runner The angular scenario runner end-to-end test framework has been removed from the project and will no longer be available on npm or bower starting with 1.7.0. It was deprecated and removed from the documentation in 2014. Applications that still use it should migrate to [Protractor](http://www.protractortest.org). Technically, it should also be possible to continue using an older version of the scenario runner, as the underlying APIs have not changed. However, we do not guarantee future compatibility. ### **form** due to: - **[223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**: set $submitted to true on child forms when parent is submitted Forms will now set $submitted on child forms when they are submitted. For example: ```
``` Submitting this form will set $submitted on "parentform" and "childform". Previously, it was only set on "parentform". This change was introduced because mixing form and ngForm does not create logically separate forms, but rather something like input groups. Therefore, child forms should inherit the submission state from their parent form. ### **ngAria** due to: - **[6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**: do not set aria attributes on input[type="hidden"] ngAria no longer sets aria-* attributes on input[type="hidden"] with ngModel. This can affect apps that test for the presence of aria attributes on hidden inputs. To migrate, remove these assertions. In actual apps, this should not have a user-facing effect, as the previous behavior was incorrect, and the new behavior is correct for accessibility. ### **ngModel, input** due to: - **[74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**: improve handling of built-in named parsers *Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month", "time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no longer set `ngModelController.$error.number` and the `ng-invalid-number` class. Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and `ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers and custom parsers easier. ### **ngModelOptions** due to: - **[55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**: add debounce catch-all + allow debouncing 'default' only the 'default' key in 'debounce' now only debounces the default event, i.e. the event that is added as an update trigger by the different input directives automatically. Previously, it also applied to other update triggers defined in 'updateOn' that did not have a corresponding key in the 'debounce'. This behavior is now supported via a special wildcard / catch-all key: '*'. See the following example: Pre-1.7: 'mouseup' is also debounced by 500 milliseconds because 'default' is applied: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { 'default': 500, 'blur': 0 } } ``` 1.7: The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { '*': 500, 'blur': 0 } } ``` In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { 'default': 500 } } ``` ### **input\[number\]** due to: - **[aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**: validate min/max against viewValue `input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`. This affects apps that use `$parsers` or `$formatters` to transform the input / model value. If you rely on the $modelValue validation, you can overwrite the `min`/`max` validator from a custom directive, as seen in the following example directive definition object: ``` { restrict: 'A', require: 'ngModel', link: function(scope, element, attrs, ctrl) { var maxValidator = ctrl.$validators.max; ctrl.$validators.max = function(modelValue, viewValue) { return maxValidator(modelValue, modelValue); }; } } ``` ### **input** due to: - **[656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**: listen on "change" instead of "click" for radio/checkbox ngModels `input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. Most apps should not be affected, as "change" is automatically fired by browsers after "click" happens. Two scenarios might need migration: - Custom click events: Before this change, custom click event listeners on radio / checkbox would be called after the input element and `ngModel` had been updated, unless they were specifically registered before the built-in click handlers. After this change, they are called before the input is updated, and can call event.preventDefault() to prevent the input from updating. If an app uses a click event listener that expects ngModel to be updated when it is called, it now needs to register a change event listener instead. - Triggering click events: Conventional trigger functions: The change event might not be fired when the input element is not attached to the document. This can happen in **tests** that compile input elements and trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, the change event will not be fired when the input isn't attached to the document. Before: ```js it('should update the model', inject(function($compile, $rootScope) { var inputElm = $compile('')($rootScope); inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() expect($rootScope.checkbox).toBe(true); }); ``` With this patch, `$rootScope.checkbox` might not be true, because the click event hasn't triggered the change event. To make the test, work append the inputElm to the app's `$rootElement`, and the `$rootElement` to the `$document`. After: ```js it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { var inputElm = $compile('')($rootScope); $rootElement.append(inputElm); $document.append($rootElement); inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() expect($rootScope.checkbox).toBe(true); }); ``` `triggerHandler()`: If you are using this jQuery / jqLite function on the input elements, you don't have to attach the elements to the document, but instead change the triggered event to "change". This is because `triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite. ### **ngStyle** due to: - **[15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**: use $watchCollection Previously the use of deep watch by ng-style would trigger styles to be re-applied when nested state changed. Now only changes to direct properties of the watched object will trigger changes. ### **$compile** due to: - **[38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**: remove the preAssignBindingsEnabled flag Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported. The flag controlled whether bindings were available inside the controller constructor or only in the `$onInit` hook. The bindings are now no longer available in the constructor. To migrate your code: 1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you don't have to do anything to migrate. 2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you can remove that statement - since AngularJS 1.6.0 this is the default so your app should still work even in AngularJS 1.6 after such removal. Afterwards, migrating to AngularJS 1.7.0 shouldn't require any further action. 3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need to first migrate your code so that the flag can be flipped to `false`. The instructions on how to do that are available in the "Migrating from 1.5 to 1.6" guide: https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6 Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)` statement. - **[6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**: lower the `xlink:href` security context for SVG's `a` and `image` elements In the unlikely case that an app relied on RESOURCE_URL whitelisting for the purpose of binding to the `xlink:href` property of SVG's `` or `` elements and if the values do not pass the regular URL sanitization, they will break. To fix this you need to ensure that the values used for binding to the affected `xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in `$compileProvider`'s `aHrefSanitizationWhitelist` (for `` elements) or `imgSrcSanitizationWhitelist` (for `` elements). - **[fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**: do not use deepWatch in literal one-way bindings Previously when a literal value was passed into a directive/component via one-way binding it would be watched with a deep watcher. For example, for ``, a new instance of the array would be passed into the directive/component (and trigger $onChanges) not only if `a` changed but also if any sub property of `a` changed such as `a.b` or `a.b.c.d.e` etc. This also means a new but equal value for `a` would NOT trigger such a change. Now literal values use an input-based watch similar to other directive/component one-way bindings. In this context inputs are the non-constant parts of the literal. In the example above the input would be `a`. Changes are only triggered when the inputs to the literal change. - **[1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**: add `base[href]` to the list of RESOURCE_URL context attributes Previously, `` would not require `baseUrl` to be trusted as a RESOURCE_URL. Now, `baseUrl` will be sent to `$sce`'s RESOURCE_URL checks. By default, it will break unless `baseUrl` is of the same origin as the application document. Refer to the [`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce) for more info on how to trust a value in a RESOURCE_URL context. Also, concatenation in trusted contexts is not allowed, which means that the following won't work: ``. Either construct complex values in a controller (recommended): ```js this.baseUrl = '/something/' + this.partialPath; ``` ```html ``` Or use string concatenation in the interpolation expression (not recommended except for the simplest of cases): ```html ``` ### **ngTouch** due to: - **[11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**: remove ngClick override, `$touchProvider`, and `$touch` The `ngClick` directive from the ngTouch module has been removed, and with it the corresponding `$touchProvider` and `$touch` service. If you have included ngTouch v1.5.0 or higher in your application, and have not changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch` service, then there are no migration steps for your code. Otherwise you must remove references to the provider and service. The `ngClick` override directive had been deprecated and by default disabled since v1.5.0, because of buggy behavior in edge cases, and a general trend to avoid special touch based overrides of click events. In modern browsers, it should not be necessary to use a touch override library: - Chrome, Firefox, Edge, and Safari remove the 300ms delay when `` is set. - Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the `touch-action` css property is set to `manipulation`. You can find out more in these articles: https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8 https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/ ### **Angular** due to: - **[1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**: remove `angular.lowercase` and `angular.uppercase` The helper functions `angular.lowercase` `and angular.uppercase` have been removed. These functions have been deprecated since 1.5.0. They are internally used, but should not be exposed as they contain special locale handling (for Turkish) to maintain internal consistency regardless of user-set locale. Developers should generally use the built-ins `toLowerCase` and `toUpperCase` or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases. Further, we generally discourage using the angular.x helpers in application code. ### **$controller** due to: - **[e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**: remove instantiating controllers defined on window The option to instantiate controllers from constructors on the global `window` object has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()` method that could enable this behavior, has been removed. This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope is bad. To migrate, remove the call to $controllerProvider.allowGlobals() in the config, and register your controller via the Module API or the $controllerProvider, e.g. ``` angular.module('myModule', []).controller('myController', function() {...}); angular.module('myModule', []).config(function($controllerProvider) { $controllerProvider.register('myController', function() {...}); }); ``` ### **$rootScope** due to: - **[c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)**: provide correct value of one-time bindings in watchGroup Previously when using `$watchGroup` the entries in `newValues` and `oldValues` represented the *most recent change of each entry*. Now the entries in `oldValues` will always equal the `newValues` of the previous call of the listener. This means comparing the entries in `newValues` and `oldValues` can be used to determine which individual expressions changed. For example `$scope.$watchGroup(['a', 'b'], fn)` would previously: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `a=2` | [2, undefined] | [1, undefined] | | `b=3` | [2, 3] | [1, undefined] | Now the `oldValue` will always equal the previous `newValue`: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `a=2` | [2, undefined] | [1, undefined] | | `b=3` | [2, 3] | [2, undefined] | Note the last call now shows `a === 2` in the `oldValues` array. This also makes the `oldValue` of one-time watchers more clear. Previously the `oldValue` of a one-time watcher would remain `undefined` forever. For example `$scope.$watchGroup(['a', '::b'], fn)` would previously: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `b=2` | [1, 2] | [undefined, undefined] | | `a=b=3` | [3, 2] | [1, undefined] | Where now the `oldValue` will always equal the previous `newValue`: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `b=2` | [1, 2] | [1, undefined] | | `a=b=3` | [3, 2] | [1, 2] | ### **$interval** due to: - **[a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**: throw when trying to cancel non-$interval promise `$interval.cancel()` will throw an error if called with a promise that was not generated by `$interval()`. Previously, it would silently do nothing. Before: ```js var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); $interval.cancel(promise); // No error; interval NOT canceled. ``` After: ```js var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); $interval.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $interval(doSomething, 1000, 5); var newPromise = promise.then(doSomethingElse); $interval.cancel(promise); // Interval canceled. ``` ### **$timeout** due to: - **[336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**: throw when trying to cancel non-$timeout promise `$timeout.cancel()` will throw an error if called with a promise that was not generated by `$timeout()`. Previously, it would silently do nothing. Before: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // No error; timeout NOT canceled. ``` After: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $timeout(doSomething, 1000); var newPromise = promise.then(doSomethingElse); $timeout.cancel(promise); // Timeout canceled. ``` # 1.7.0-rc.0 maximum-overdrive (2018-04-19) ## Bug Fixes - **input:** - listen on "change" instead of "click" for radio/checkbox ngModels ([656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e), [#4516](https://github.com/angular/angular.js/issues/4516), [#14667](https://github.com/angular/angular.js/issues/14667), [#14685](https://github.com/angular/angular.js/issues/14685)) - **input\[number\]:** validate min/max against viewValue ([aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec), [#12761](https://github.com/angular/angular.js/issues/12761), [#16325](https://github.com/angular/angular.js/issues/16325)) - **jqLite:** make removeData() not remove event handlers ([b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a), [#15869](https://github.com/angular/angular.js/issues/15869), [#16512](https://github.com/angular/angular.js/issues/16512)) - **$compile:** - remove the preAssignBindingsEnabled flag ([38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb), [#15782](https://github.com/angular/angular.js/issues/15782)) - add `base[href]` to the list of RESOURCE_URL context attributes ([1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e), [#15597](https://github.com/angular/angular.js/issues/15597)) - **$interval:** throw when trying to cancel non-$interval promise ([a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4), [#16424](https://github.com/angular/angular.js/issues/16424), [#16476](https://github.com/angular/angular.js/issues/16476)) - **$timeout:** throw when trying to cancel non-$timeout promise ([336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828), [#16424](https://github.com/angular/angular.js/issues/16424), [#16476](https://github.com/angular/angular.js/issues/16476)) - **$cookies:** remove the deprecated $cookieStore factory ([73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77), [#16465](https://github.com/angular/angular.js/issues/16465)) - **$resource:** fix interceptors and success/error callbacks ([ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd), [#6731](https://github.com/angular/angular.js/issues/6731), [#9334](https://github.com/angular/angular.js/issues/9334), [#6865](https://github.com/angular/angular.js/issues/6865), [#16446](https://github.com/angular/angular.js/issues/16446)) - **$templateRequest:** - give tpload error the correct namespace ([c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)) - always return the template that is stored in the cache ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e), [#16225](https://github.com/angular/angular.js/issues/16225)) - **$animate:** let cancel() reject the runner promise ([16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83), [#14204](https://github.com/angular/angular.js/issues/14204), [#16373](https://github.com/angular/angular.js/issues/16373)) - **ngTouch:** - deprecate the module and its contents ([67f54b](https://github.com/angular/angular.js/commit/67f54b660038de2b4346b3e76d66a8dc8ccb1f9b), [#16427](https://github.com/angular/angular.js/issues/16427), [#16431](https://github.com/angular/angular.js/issues/16431)) - remove ngClick override, `$touchProvider`, and `$touch` ([11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50), [#15761](https://github.com/angular/angular.js/issues/15761), [#15755](https://github.com/angular/angular.js/issues/15755)) - **ngScenario:** completely remove the angular scenario runner ([0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60), [#9405](https://github.com/angular/angular.js/issues/9405)) - **form:** set $submitted to true on child forms when parent is submitted ([223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77), [#10071](https://github.com/angular/angular.js/issues/10071)) - **$rootScope:** - provide correct value of one-time bindings in watchGroup ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)) - **ngAria:** do not set aria attributes on input[type="hidden"] ([6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b), [#15113](https://github.com/angular/angular.js/issues/15113), [#16367](https://github.com/angular/angular.js/issues/16367)) - **ngModel, input:** improve handling of built-in named parsers ([74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140), [#14292](https://github.com/angular/angular.js/issues/14292), [#10076](https://github.com/angular/angular.js/issues/10076), [#16347](https://github.com/angular/angular.js/issues/16347)) - **$httpParamSerializerJQLike:** - call functions as jQuery does ([a784fa](https://github.com/angular/angular.js/commit/a784fab605d825f1158c6292b3c42f8c4a502fdf), [#16138](https://github.com/angular/angular.js/issues/16138), [#16139](https://github.com/angular/angular.js/issues/16139)) - follow jQuery for `null` and `undefined` ([301fdd](https://github.com/angular/angular.js/commit/301fdda648680d89ccab607c413a7ddede7b0165)) - **$parse:** - do not pass scope/locals to interceptors of one-time bindings ([87a586](https://github.com/angular/angular.js/commit/87a586eb9a23cfd0d0bb681cc778b4b8e5c8451d)) - always pass the intercepted value to watchers ([2ee503](https://github.com/angular/angular.js/commit/2ee5033967d5f87a516bad137686b0592e25d26b), [#16021](https://github.com/angular/angular.js/issues/16021)) - respect the interceptor.$stateful flag ([de7403](https://github.com/angular/angular.js/commit/de74034ddf6f92505ccdb61be413a6df2c723f87)) - **Angular:** remove `angular.lowercase` and `angular.uppercase` ([1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de), [#15445](https://github.com/angular/angular.js/issues/15445)) - **$controller:** remove instantiating controllers defined on window ([e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16), [#15349](https://github.com/angular/angular.js/issues/15349), [#15762](https://github.com/angular/angular.js/issues/15762)) ## New Features - **angular.isArray:** support Array subclasses in `angular.isArray()` ([e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948), [#15533](https://github.com/angular/angular.js/issues/15533), [#15541](https://github.com/angular/angular.js/issues/15541)) - **$sce:** handle URL sanitization through the `$sce` service ([1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)) - **orderBy:** consider `null` and `undefined` greater than other values ([1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8), [#15294](https://github.com/angular/angular.js/issues/15294), [#16376](https://github.com/angular/angular.js/issues/16376)) - **$resource:** add support for `request` and `requestError` interceptors (#15674) ([240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded), [#5146](https://github.com/angular/angular.js/issues/5146)) - **ngModelOptions:** add debounce catch-all + allow debouncing 'default' only ([55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68), [#15411](https://github.com/angular/angular.js/issues/15411), [#16335](https://github.com/angular/angular.js/issues/16335)) - **$compile:** lower the `xlink:href` security context for SVG's `a` and `image` elements ([6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd), [#15736](https://github.com/angular/angular.js/issues/15736)) ## Performance Improvements - **$rootScope:** allow $watchCollection use of expression input watching ([97b00c](https://github.com/angular/angular.js/commit/97b00ca497676aaff8a803762a9f8c7ff4aa24dd)) - **ngStyle:** use $watchCollection ([15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0), [#15947](https://github.com/angular/angular.js/issues/15947)) - **$compile:** do not use deepWatch in literal one-way bindings ([fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727), [#15301](https://github.com/angular/angular.js/issues/15301)) ## Breaking Changes ### **jqLite** due to: - **[b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**: make removeData() not remove event handlers Before this commit `removeData()` invoked on an element removed its event handlers as well. If you want to trigger a full cleanup of an element, change: ```js elem.removeData(); ``` to: ```js angular.element.cleanData(elem); ``` In most cases, though, cleaning up after an element is supposed to be done only when it's removed from the DOM as well; in such cases the following: ```js elem.remove(); ``` will remove event handlers as well. ### **$cookies** due to: - **[73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**: remove the deprecated $cookieStore factory The $cookieStore has been removed. Migrate to the $cookies service. Note that for object values you need to use the `putObject` & `getObject` methods as `get`/`put` will not correctly save/retrieve them. Before: ```js $cookieStore.put('name', {key: 'value'}); $cookieStore.get('name'); // {key: 'value'} $cookieStore.remove('name'); ``` After: ```js $cookies.putObject('name', {key: 'value'}); $cookies.getObject('name'); // {key: 'value'} $cookies.remove('name'); ``` ### **$resource** due to: - **[ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**: fix interceptors and success/error callbacks If you are not using `success` or `error` callbacks with `$resource`, your app should not be affected by this change. If you are using `success` or `error` callbacks (with or without response interceptors), one (subtle) difference is that throwing an error inside the callbacks will not propagate to the returned `$promise`. Therefore, you should try to use the promises whenever possible. E.g.: ```js // Avoid User.query(function onSuccess(users) { throw new Error(); }). $promise. catch(function onError() { /* Will not be called. */ }); // Prefer User.query(). $promise. then(function onSuccess(users) { throw new Error(); }). catch(function onError() { /* Will be called. */ }); ``` Finally, if you are using `success` or `error` callbacks with response interceptors, the callbacks will now always run _after_ the interceptors (and wait for them to resolve in case they return a promise). Previously, the `error` callback was called before the `responseError` interceptor and the `success` callback was synchronously called after the `response` interceptor. E.g.: ```js var User = $resource('/api/users/:id', {id: '@id'}, { get: { method: 'get', interceptor: { response: function(response) { console.log('responseInterceptor-1'); return $timeout(1000).then(function() { console.log('responseInterceptor-2'); return response.resource; }); }, responseError: function(response) { console.log('responseErrorInterceptor-1'); return $timeout(1000).then(function() { console.log('responseErrorInterceptor-2'); return $q.reject('Ooops!'); }); } } } }); var onSuccess = function(value) { console.log('successCallback', value); }; var onError = function(error) { console.log('errorCallback', error); }; // Assuming the following call is successful... User.get({id: 1}, onSuccess, onError); // Old behavior: // responseInterceptor-1 // successCallback, {/* Promise object */} // responseInterceptor-2 // New behavior: // responseInterceptor-1 // responseInterceptor-2 // successCallback, {/* User object */} // Assuming the following call returns an error... User.get({id: 2}, onSuccess, onError); // Old behavior: // errorCallback, {/* Response object */} // responseErrorInterceptor-1 // responseErrorInterceptor-2 // New behavior: // responseErrorInterceptor-1 // responseErrorInterceptor-2 // errorCallback, Ooops! ``` - **[240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**: add support for `request` and `requestError` interceptors (#15674) Previously, calling a `$resource` method would synchronously call `$http`. Now, it will be called asynchronously (regardless if a `request`/`requestError` interceptor has been defined. This is not expected to affect applications at runtime, since the overall operation is asynchronous already, but may affect assertions in tests. For example, if you want to assert that `$http` has been called with specific arguments as a result of a `$resource` call, you now need to run a `$digest` first, to ensure the (possibly empty) request interceptor promise has been resolved. Before: ```js it('...', function() { $httpBackend.expectGET('/api/things').respond(...); var Things = $resource('/api/things'); Things.query(); expect($http).toHaveBeenCalledWith(...); }); ``` After: ```js it('...', function() { $httpBackend.expectGET('/api/things').respond(...); var Things = $resource('/api/things'); Things.query(); $rootScope.$digest(); expect($http).toHaveBeenCalledWith(...); }); ``` ### **$templateRequest**: - due to **[c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**: give tpload error the correct namespace Previously the `tpload` error was namespaced to `$compile`. If you have code that matches errors of the form `[$compile:tpload]` it will no longer run. You should change the code to match `[$templateRequest:tpload]`. - due to **([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**: always return the template that is stored in the cache The service now returns the result of `$templateCache.put()` when making a server request to the template. Previously it would return the content of the response directly. This now means if you are decorating `$templateCache.put()` to manipulate the template, you will now get this manipulated result also on the first `$templateRequest` rather than only on subsequent calls (when the template is retrived from the cache). In practice this should not affect any apps, as it is unlikely that they rely on the template being different in the first and subsequent calls. ### **$animate** due to: - **[16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**: let cancel() reject the runner promise $animate.cancel(runner) now rejects the underlying promise and calls the catch() handler on the runner returned by $animate functions (enter, leave, move, addClass, removeClass, setClass, animate). Previously it would resolve the promise as if the animation had ended successfully. Example: ```js var runner = $animate.addClass('red'); runner.then(function() { console.log('success')}); runner.catch(function() { console.log('cancelled')}); runner.cancel(); ``` Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'. To migrate, add a catch() handler to your animation runners. ### **angular.isArray** due to: - **[e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**: support Array subclasses in `angular.isArray()` Previously, `angular.isArray()` was an alias for `Array.isArray()`. Therefore, objects that prototypally inherit from `Array` where not considered arrays. Now such objects are considered arrays too. This change affects several other methods that use `angular.isArray()` under the hood, such as `angular.copy()`, `angular.equals()`, `angular.forEach()`, and `angular.merge()`. This in turn affects how dirty checking treats objects that prototypally inherit from `Array` (e.g. MobX observable arrays). AngularJS will now be able to handle these objects better when copying or watching. ### **$sce** due to: - **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service If you use `attrs.$set` for URL attributes (a[href] and img[src]) there will no longer be any automated sanitization of the value. This is in line with other programmatic operations, such as writing to the innerHTML of an element. If you are programmatically writing URL values to attributes from untrusted input then you must sanitize it yourself. You could write your own sanitizer or copy the private `$$sanitizeUri` service. Note that values that have been passed through the `$interpolate` service within the `URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize these values again. ### **orderBy** due to: - **[1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**: consider `null` and `undefined` greater than other values When using `orderBy` to sort arrays containing `null` values, the `null` values will be considered "greater than" all other values, except for `undefined`. Previously, they were sorted as strings. This will result in different (but more intuitive) sorting order. Before: ```js orderByFilter(['a', undefined, 'o', null, 'z']); //--> 'a', null, 'o', 'z', undefined ``` After: ```js orderByFilter(['a', undefined, 'o', null, 'z']); //--> 'a', 'o', 'z', null, undefined ``` ### **ngScenario** due to: - **[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**: completely remove the angular scenario runner The angular scenario runner end-to-end test framework has been removed from the project and will no longer be available on npm or bower starting with 1.7.0. It was deprecated and removed from the documentation in 2014. Applications that still use it should migrate to [Protractor](http://www.protractortest.org). Technically, it should also be possible to continue using an older version of the scenario runner, as the underlying APIs have not changed. However, we do not guarantee future compatibility. ### **form** due to: - **[223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**: set $submitted to true on child forms when parent is submitted Forms will now set $submitted on child forms when they are submitted. For example: ```
``` Submitting this form will set $submitted on "parentform" and "childform". Previously, it was only set on "parentform". This change was introduced because mixing form and ngForm does not create logically separate forms, but rather something like input groups. Therefore, child forms should inherit the submission state from their parent form. ### **ngAria** due to: - **[6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**: do not set aria attributes on input[type="hidden"] ngAria no longer sets aria-* attributes on input[type="hidden"] with ngModel. This can affect apps that test for the presence of aria attributes on hidden inputs. To migrate, remove these assertions. In actual apps, this should not have a user-facing effect, as the previous behavior was incorrect, and the new behavior is correct for accessibility. ### **ngModel, input** due to: - **[74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**: improve handling of built-in named parsers *Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month", "time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no longer set `ngModelController.$error.number` and the `ng-invalid-number` class. Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and `ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers and custom parsers easier. ### **ngModelOptions** due to: - **[55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**: add debounce catch-all + allow debouncing 'default' only the 'default' key in 'debounce' now only debounces the default event, i.e. the event that is added as an update trigger by the different input directives automatically. Previously, it also applied to other update triggers defined in 'updateOn' that did not have a corresponding key in the 'debounce'. This behavior is now supported via a special wildcard / catch-all key: '*'. See the following example: Pre-1.7: 'mouseup' is also debounced by 500 milliseconds because 'default' is applied: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { 'default': 500, 'blur': 0 } } ``` 1.7: The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { '*': 500, 'blur': 0 } } ``` In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: ``` ng-model-options="{ updateOn: 'default blur mouseup', debounce: { 'default': 500 } } ``` ### **input\[number\]** due to: - **[aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**: validate min/max against viewValue `input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`. This affects apps that use `$parsers` or `$formatters` to transform the input / model value. If you rely on the $modelValue validation, you can overwrite the `min`/`max` validator from a custom directive, as seen in the following example directive definition object: ``` { restrict: 'A', require: 'ngModel', link: function(scope, element, attrs, ctrl) { var maxValidator = ctrl.$validators.max; ctrl.$validators.max = function(modelValue, viewValue) { return maxValidator(modelValue, modelValue); }; } } ``` ### **input** due to: - **[656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**: listen on "change" instead of "click" for radio/checkbox ngModels `input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. Most apps should not be affected, as "change" is automatically fired by browsers after "click" happens. Two scenarios might need migration: - Custom click events: Before this change, custom click event listeners on radio / checkbox would be called after the input element and `ngModel` had been updated, unless they were specifically registered before the built-in click handlers. After this change, they are called before the input is updated, and can call event.preventDefault() to prevent the input from updating. If an app uses a click event listener that expects ngModel to be updated when it is called, it now needs to register a change event listener instead. - Triggering click events: Conventional trigger functions: The change event might not be fired when the input element is not attached to the document. This can happen in **tests** that compile input elements and trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, the change event will not be fired when the input isn't attached to the document. Before: ```js it('should update the model', inject(function($compile, $rootScope) { var inputElm = $compile('')($rootScope); inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() expect($rootScope.checkbox).toBe(true); }); ``` With this patch, `$rootScope.checkbox` might not be true, because the click event hasn't triggered the change event. To make the test, work append the inputElm to the app's `$rootElement`, and the `$rootElement` to the `$document`. After: ```js it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { var inputElm = $compile('')($rootScope); $rootElement.append(inputElm); $document.append($rootElement); inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() expect($rootScope.checkbox).toBe(true); }); ``` `triggerHandler()`: If you are using this jQuery / jqLite function on the input elements, you don't have to attach the elements to the document, but instead change the triggered event to "change". This is because `triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite. ### **ngStyle** due to: - **[15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**: use $watchCollection Previously the use of deep watch by ng-style would trigger styles to be re-applied when nested state changed. Now only changes to direct properties of the watched object will trigger changes. ### **$compile** due to: - **[38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**: remove the preAssignBindingsEnabled flag Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported. The flag controlled whether bindings were available inside the controller constructor or only in the `$onInit` hook. The bindings are now no longer available in the constructor. To migrate your code: 1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you don't have to do anything to migrate. 2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you can remove that statement - since AngularJS 1.6.0 this is the default so your app should still work even in AngularJS 1.6 after such removal. Afterwards, migrating to AngularJS 1.7.0 shouldn't require any further action. 3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need to first migrate your code so that the flag can be flipped to `false`. The instructions on how to do that are available in the "Migrating from 1.5 to 1.6" guide: https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6 Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)` statement. - **[6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**: lower the `xlink:href` security context for SVG's `a` and `image` elements In the unlikely case that an app relied on RESOURCE_URL whitelisting for the purpose of binding to the `xlink:href` property of SVG's `` or `` elements and if the values do not pass the regular URL sanitization, they will break. To fix this you need to ensure that the values used for binding to the affected `xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in `$compileProvider`'s `aHrefSanitizationWhitelist` (for `` elements) or `imgSrcSanitizationWhitelist` (for `` elements). - **[fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**: do not use deepWatch in literal one-way bindings Previously when a literal value was passed into a directive/component via one-way binding it would be watched with a deep watcher. For example, for ``, a new instance of the array would be passed into the directive/component (and trigger $onChanges) not only if `a` changed but also if any sub property of `a` changed such as `a.b` or `a.b.c.d.e` etc. This also means a new but equal value for `a` would NOT trigger such a change. Now literal values use an input-based watch similar to other directive/component one-way bindings. In this context inputs are the non-constant parts of the literal. In the example above the input would be `a`. Changes are only triggered when the inputs to the literal change. - **[1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**: add `base[href]` to the list of RESOURCE_URL context attributes Previously, `` would not require `baseUrl` to be trusted as a RESOURCE_URL. Now, `baseUrl` will be sent to `$sce`'s RESOURCE_URL checks. By default, it will break unless `baseUrl` is of the same origin as the application document. Refer to the [`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce) for more info on how to trust a value in a RESOURCE_URL context. Also, concatenation in trusted contexts is not allowed, which means that the following won't work: ``. Either construct complex values in a controller (recommended): ```js this.baseUrl = '/something/' + this.partialPath; ``` ```html ``` Or use string concatenation in the interpolation expression (not recommended except for the simplest of cases): ```html ``` ### **ngTouch** due to: - **[11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**: remove ngClick override, `$touchProvider`, and `$touch` The `ngClick` directive from the ngTouch module has been removed, and with it the corresponding `$touchProvider` and `$touch` service. If you have included ngTouch v1.5.0 or higher in your application, and have not changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch` service, then there are no migration steps for your code. Otherwise you must remove references to the provider and service. The `ngClick` override directive had been deprecated and by default disabled since v1.5.0, because of buggy behavior in edge cases, and a general trend to avoid special touch based overrides of click events. In modern browsers, it should not be necessary to use a touch override library: - Chrome, Firefox, Edge, and Safari remove the 300ms delay when `` is set. - Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the `touch-action` css property is set to `manipulation`. You can find out more in these articles: https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8 https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/ ### **Angular** due to: - **[1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**: remove `angular.lowercase` and `angular.uppercase` The helper functions `angular.lowercase` `and angular.uppercase` have been removed. These functions have been deprecated since 1.5.0. They are internally used, but should not be exposed as they contain special locale handling (for Turkish) to maintain internal consistency regardless of user-set locale. Developers should generally use the built-ins `toLowerCase` and `toUpperCase` or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases. Further, we generally discourage using the angular.x helpers in application code. ### **$controller** due to: - **[e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**: remove instantiating controllers defined on window The option to instantiate controllers from constructors on the global `window` object has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()` method that could enable this behavior, has been removed. This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope is bad. To migrate, remove the call to $controllerProvider.allowGlobals() in the config, and register your controller via the Module API or the $controllerProvider, e.g. ``` angular.module('myModule', []).controller('myController', function() {...}); angular.module('myModule', []).config(function($controllerProvider) { $controllerProvider.register('myController', function() {...}); }); ``` ### **$rootScope** due to: - **[c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)**: provide correct value of one-time bindings in watchGroup Previously when using `$watchGroup` the entries in `newValues` and `oldValues` represented the *most recent change of each entry*. Now the entries in `oldValues` will always equal the `newValues` of the previous call of the listener. This means comparing the entries in `newValues` and `oldValues` can be used to determine which individual expressions changed. For example `$scope.$watchGroup(['a', 'b'], fn)` would previously: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `a=2` | [2, undefined] | [1, undefined] | | `b=3` | [2, 3] | [1, undefined] | Now the `oldValue` will always equal the previous `newValue`: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `a=2` | [2, undefined] | [1, undefined] | | `b=3` | [2, 3] | [2, undefined] | Note the last call now shows `a === 2` in the `oldValues` array. This also makes the `oldValue` of one-time watchers more clear. Previously the `oldValue` of a one-time watcher would remain `undefined` forever. For example `$scope.$watchGroup(['a', '::b'], fn)` would previously: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `b=2` | [1, 2] | [undefined, undefined] | | `a=b=3` | [3, 2] | [1, undefined] | Where now the `oldValue` will always equal the previous `newValue`: | Action | newValue | oldValue | |----------|------------|------------| | (init) | [undefined, undefined] | [undefined, undefined] | | `a=1` | [1, undefined] | [undefined, undefined] | | `b=2` | [1, 2] | [1, undefined] | | `a=b=3` | [3, 2] | [1, 2] | ### **$interval** due to: - **[a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**: throw when trying to cancel non-$interval promise `$interval.cancel()` will throw an error if called with a promise that was not generated by `$interval()`. Previously, it would silently do nothing. Before: ```js var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); $interval.cancel(promise); // No error; interval NOT canceled. ``` After: ```js var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); $interval.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $interval(doSomething, 1000, 5); var newPromise = promise.then(doSomethingElse); $interval.cancel(promise); // Interval canceled. ``` ### **$timeout** due to: - **[336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**: throw when trying to cancel non-$timeout promise `$timeout.cancel()` will throw an error if called with a promise that was not generated by `$timeout()`. Previously, it would silently do nothing. Before: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // No error; timeout NOT canceled. ``` After: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $timeout(doSomething, 1000); var newPromise = promise.then(doSomethingElse); $timeout.cancel(promise); // Timeout canceled. ``` # 1.6.10 crystalline-persuasion (2018-04-17) ## Bug Fixes - **$compile:** - correctly handle `null`/`undefined` href `attrs.$set()` ([f04e04](https://github.com/angular/angular.js/commit/f04e04e0e63e0d30c29718abd5cae634901793b2), [#16520](https://github.com/angular/angular.js/issues/16520)) - throw error in `$onChanges` immediately ([b7d1e0fbd](https://github.com/angular/angular.js/commit/983e27b628fd1eab653e2b3966d90a270f27cc93), [#15578](https://github.com/angular/angular.js/issues/15578), [#16492](https://github.com/angular/angular.js/issues/16492)) - **input:** - allow overriding timezone for date input types ([4355de](https://github.com/angular/angular.js/commit/4355dee21d26667bb7f6f21bf75c081351315033), [#16181](https://github.com/angular/angular.js/issues/16181), [#13382](https://github.com/angular/angular.js/issues/13382), [#16336](https://github.com/angular/angular.js/issues/16336)) - take timezone into account when validating minimum and maximum in date types ([2f0ac6](https://github.com/angular/angular.js/commit/2f0ac696cb09aec3e291bb8c9c8a1092cbe3a061), [#16342](https://github.com/angular/angular.js/issues/16342), [#16390](https://github.com/angular/angular.js/issues/16390)) - fix composition mode in IE for Korean input ([9a1b7c](https://github.com/angular/angular.js/commit/9a1b7c9fa135d1dae3f9b4ccf48f081675796e92), [#6656](https://github.com/angular/angular.js/issues/6656), [#16273](https://github.com/angular/angular.js/issues/16273)) - **jqLite:** use XHTML-compliant HTML as input for jqLite ([a0c55a](https://github.com/angular/angular.js/commit/a0c55af9858075ab268a88dd7a4464788a46f4b7), [#6917](https://github.com/angular/angular.js/issues/6917), [#16518](https://github.com/angular/angular.js/issues/16518)) - **minErr:** update url to https ([52e466](https://github.com/angular/angular.js/commit/52e46683bfcc0ce0dc9a3d2ee42b389508423799)) - **$http:** set correct xhrStatus in response when using 'timeout' ([1faf7e](https://github.com/angular/angular.js/commit/1faf7ec30d55bba107b18efbcf0ef07732c55b91)) - **browserTrigger:** support CompositionEvent ([c33fd1](https://github.com/angular/angular.js/commit/c33fd1325417fdc6d7d6abc90cd935130653b149)) ## New Features - **$http:** support sending XSRF token to whitelisted origins ([bc7757](https://github.com/angular/angular.js/commit/bc775759c88b2221c2bb71d2335bc233c93f43b0), [#7862](https://github.com/angular/angular.js/issues/7862)) - **minErr:** strip error url from error parameters ([980b69](https://github.com/angular/angular.js/commit/980b69dcae73dd8a3d0b9d91b63fa7711cd0ba36)) - **$sanitize:** support enhancing elements/attributes white-lists ([ee8e05](https://github.com/angular/angular.js/commit/ee8e05cfafe086188fc318ed4115fb56ba335112), [#5900](https://github.com/angular/angular.js/issues/5900), [#16326](https://github.com/angular/angular.js/issues/16326)) - **$rootScope:** allow suspending and resuming watchers on scope ([efb822c58](https://github.com/angular/angular.js/commit/41d5c90f170cc054b0f8f88220c22ef1ef6cc0a6), [#16308](https://github.com/angular/angular.js/issues/5301)) # 1.6.9 fiery-basilisk (2018-02-02) ## Bug Fixes - **input:** add `drop` event support for IE ([5dc076](https://github.com/angular/angular.js/commit/5dc07667de00c5e85fd69c5b7b7fe4fb5fd65a77)) - **ngMessages:** prevent memory leak from messages that are never attached ([9d058d](https://github.com/angular/angular.js/commit/9d058de04bb78694b83179e9b97bc40214eca01a), [#16389](https://github.com/angular/angular.js/issues/16389), [#16404](https://github.com/angular/angular.js/issues/16404), [#16406](https://github.com/angular/angular.js/issues/16406)) - **ngTransclude:** remove terminal: true ([1d826e](https://github.com/angular/angular.js/commit/1d826e2f1e941d14c3c56d7a0249f5796ba11f85), [#16411](https://github.com/angular/angular.js/issues/16411), [#16412](https://github.com/angular/angular.js/issues/16412)) - **$sanitize:** sanitize `xml:base` attributes ([b9ef65](https://github.com/angular/angular.js/commit/b9ef6585e10477fbbf912a971fe0b390bca692a6)) ## New Features - **currencyFilter:** trim whitespace around an empty currency symbol ([367390](https://github.com/angular/angular.js/commit/3673909896efb6ff47546caf7fc61549f193e043), [#15018](https://github.com/angular/angular.js/issues/15018), [#15085](https://github.com/angular/angular.js/issues/15085), [#15105](https://github.com/angular/angular.js/issues/15105)) # 1.6.8 beneficial-tincture (2017-12-18) ## Bug Fixes - **$location:** - always decode special chars in `$location.url(value)` ([2bdf71](https://github.com/angular/angular.js/commit/2bdf7126878c87474bb7588ce093d0a3c57b0026)) - decode non-component special chars in Hashbang URLS ([57b626](https://github.com/angular/angular.js/commit/57b626a673b7530399d3377dfe770165bec35f8a)) - **ngModelController:** allow $overrideModelOptions to set updateOn ([55516d](https://github.com/angular/angular.js/commit/55516da2dfc7c5798dce24e9fa930c5ac90c900c), [#16351](https://github.com/angular/angular.js/issues/16351), [#16364](https://github.com/angular/angular.js/issues/16364)) ## New Features - **$parse:** add a hidden interface to retrieve an expression's AST ([f33d95](https://github.com/angular/angular.js/commit/f33d95cfcff6fd0270f92a142df8794cca2013ad), [#16253](https://github.com/angular/angular.js/issues/16253), [#16260](https://github.com/angular/angular.js/issues/16260)) # 1.6.7 imperial-backstroke (2017-11-24) ## Bug Fixes - **$compile:** sanitize special chars in directive name ([c4003f](https://github.com/angular/angular.js/commit/c4003fd03489f876b646f06838f4edb576bacf6f), [#16314](https://github.com/angular/angular.js/issues/16314), [#16278](https://github.com/angular/angular.js/issues/16278)) - **$location:** do not decode forward slashes in the path in HTML5 mode ([e06ebf](https://github.com/angular/angular.js/commit/e06ebfdbb558544602fe9da4d7d98045a965f468), [#16312](https://github.com/angular/angular.js/issues/16312)) - **sanitizeUri:** sanitize URIs that contain IDEOGRAPHIC SPACE chars ([ddeb1d](https://github.com/angular/angular.js/commit/ddeb1df15a23de93eb95dbe202e83e93673e1c4e), [#16288](https://github.com/angular/angular.js/issues/16288)) - **$rootScope:** fix potential memory leak when removing scope listeners ([358a69](https://github.com/angular/angular.js/commit/358a69fa8b89b251ee44e523458d6c7f40b92b2d), [#16135](https://github.com/angular/angular.js/issues/16135), [#16161](https://github.com/angular/angular.js/issues/16161)) - **http:** do not allow encoded callback params in jsonp requests ([569e90](https://github.com/angular/angular.js/commit/569e906a5818271416ad0b749be2f58dc34938bd)) - **ngMock:** pass unexpected request failures in `$httpBackend` to the error handler ([1555a4](https://github.com/angular/angular.js/commit/1555a4911ad5360c145c0ddc8ec6c4bf9a381c13), [#16150](https://github.com/angular/angular.js/issues/16150), [#15855](https://github.com/angular/angular.js/issues/15855)) - **ngAnimate:** don't close transitions when child transitions close ([1391e9](https://github.com/angular/angular.js/commit/1391e99c7f73795180b792af21ad4402f96e225d), [#16210](https://github.com/angular/angular.js/issues/16210)) - **ngMock.browserTrigger:** add 'bubbles' to Transition/Animation Event ([7a5f06](https://github.com/angular/angular.js/commit/7a5f06d55d123a39bb7b030667fb1ab672939598)) ## New Features - **$sanitize, $compileProvider, linky:** add support for the "sftp" protocol in links ([a675ea](https://github.com/angular/angular.js/commit/a675ea034366fbb0fcf0d73fed65216aa99bce11), [#16102](https://github.com/angular/angular.js/issues/16102)) - **ngModel.NgModelController:** expose $processModelValue to run model -> view pipeline ([145194](https://github.com/angular/angular.js/commit/14519488ce9218aa891d34e89fc3271fd4ed0f04), [#3407](https://github.com/angular/angular.js/issues/3407), [#10764](https://github.com/angular/angular.js/issues/10764), [#16237](https://github.com/angular/angular.js/issues/16237)) - **$injector:** ability to load new modules after bootstrapping ([6e78fe](https://github.com/angular/angular.js/commit/6e78fee73258bb0ae36414f9db2e8734273e481b)) ## Performance Improvements - **jqLite:** - avoid setting class attribute when not changed ([9c95f6](https://github.com/angular/angular.js/commit/9c95f6d5e00ee7e054aabb3e363f5bfb3b7b4103)) - avoid repeated add/removeAttribute in jqLiteRemoveClass ([cab9eb](https://github.com/angular/angular.js/commit/cab9ebfd5a02e897f802bf6321b8471e4843c5d3), [#16078](https://github.com/angular/angular.js/issues/16078), [#16131](https://github.com/angular/angular.js/issues/16131)) # 1.6.6 interdimensional-cable (2017-08-18) ## Bug Fixes - **$httpParamSerializer:** ignore functions ([b51ded](https://github.com/angular/angular.js/commit/b51ded67366865f36c5781dd5d9b801488ec95ea), [#16133](https://github.com/angular/angular.js/issues/16133)) - **$resource:** do not throw when calling old `$cancelRequest()` ([009ebe](https://github.com/angular/angular.js/commit/009ebec64c81d11b280c635167050e8906e191c6), [#16037](https://github.com/angular/angular.js/issues/16037)) - **$parse:** - do not shallow-watch computed property keys ([750465](https://github.com/angular/angular.js/commit/7504656a26202de591e4ac9674333254304edf8a)) - support constants in computed keys ([9d6c3f](https://github.com/angular/angular.js/commit/9d6c3f3ec233279885e37a250d25860d5c15f716)) - **$http:** do not throw error if `Content-Type` is not `application/json` but response is JSON-like ([2e1163](https://github.com/angular/angular.js/commit/2e1163ef5cb56d1933e8ecd7b74020b9df9c6693), [#16027](https://github.com/angular/angular.js/issues/16027), [#16075](https://github.com/angular/angular.js/issues/16075)) ## New Features - **$compile:** add `strictComponentBindingsEnabled()` method ([3ec181](https://github.com/angular/angular.js/commit/3ec1819b913c8edf0649e06217dbd5920f29f126), [#16129](https://github.com/angular/angular.js/issues/16129)) - **$resource:** add resource to response for error interceptors ([9256db](https://github.com/angular/angular.js/commit/9256dbc4201343ce5cd63a9eadf98da4793f45af), [#16109](https://github.com/angular/angular.js/issues/16109)) - **$http:** allow differentiation between XHR completion, error, abort, timeout ([5e2bc5](https://github.com/angular/angular.js/commit/5e2bc5bbf347a9dfadc08b1514b8be06fd550913), [#15924](https://github.com/angular/angular.js/issues/15924), [#15847](https://github.com/angular/angular.js/issues/15847)) # 1.6.5 toffee-salinization (2017-07-03) ## Bug Fixes - **core:** - correctly detect Error instances from different contexts ([6daca0](https://github.com/angular/angular.js/commit/6daca023e42098f7098b9bf153c8e53a17af84f1), [#15868](https://github.com/angular/angular.js/issues/15868), [#15872](https://github.com/angular/angular.js/issues/15872)) - deprecate `angular.merge` ([dc41f4](https://github.com/angular/angular.js/commit/dc41f465baae9bc91418a61f446596157c530b6e), [#12653](https://github.com/angular/angular.js/issues/12653), [#14941](https://github.com/angular/angular.js/issues/14941), [#15180](https://github.com/angular/angular.js/issues/15180), [#15992](https://github.com/angular/angular.js/issues/15992), [#16036](https://github.com/angular/angular.js/issues/16036)) - **ngOptions:** - re-render after empty option has been removed ([510d0f](https://github.com/angular/angular.js/commit/510d0f946fa1a443ad43fa31bc9337676ef31332)) - allow empty option to be removed and re-added ([71b4da](https://github.com/angular/angular.js/commit/71b4daa4e10b6912891927ee2a7930c604b538f8)) - select unknown option if unmatched model does not match empty option ([17d34b](https://github.com/angular/angular.js/commit/17d34b7a983a0ef63f6cf404490385c696fb0da1)) - **orderBy:** guarantee stable sort ([e50ed4](https://github.com/angular/angular.js/commit/e50ed4da9e8177168f67da68bdf02f07da4e7bcf), [#14881](https://github.com/angular/angular.js/issues/14881), [#15914](https://github.com/angular/angular.js/issues/15914)) - **$parse:** - do not shallow-watch inputs to one-time intercepted expressions ([6e3b5a](https://github.com/angular/angular.js/commit/6e3b5a57cd921823f3eca7200a79ac5c2ef0567a)) - standardize one-time literal vs non-literal and interceptors ([f003d9](https://github.com/angular/angular.js/commit/f003d93a3dd052dccddef41125d9c51034ac3605)) - do not shallow-watch inputs when wrapped in an interceptor fn ([aac562](https://github.com/angular/angular.js/commit/aac5623247a86681cbe0e1c8179617b816394c1d), [#15905](https://github.com/angular/angular.js/issues/15905)) - always re-evaluate filters within literals when an input is an object ([ec9768](https://github.com/angular/angular.js/commit/ec97686f2f4a5481cc806462313a664fc7a1c893), [#15964](https://github.com/angular/angular.js/issues/15964), [#15990](https://github.com/angular/angular.js/issues/15990)) - **$sanitize:** use appropriate inert document strategy for Firefox and Safari ([8f31f1](https://github.com/angular/angular.js/commit/8f31f1ff43b673a24f84422d5c13d6312b2c4d94)) - **$timeout/$interval:** do not trigger a digest on cancel ([a222d0](https://github.com/angular/angular.js/commit/a222d0b452622624dc498ef0b9d3c43647fd4fbc), [#16057](https://github.com/angular/angular.js/issues/16057), [#16064](https://github.com/angular/angular.js/issues/16064))
This change might affect the use of `$timeout.flush()` in unit tests. See the commit message for more info. - **ngMock/$interval:** add support for zero-delay intervals in tests ([a1e3f8](https://github.com/angular/angular.js/commit/a1e3f8728e0a80396f980e48f8dc68dde6721b2b), [#15952](https://github.com/angular/angular.js/issues/15952), [#15953](https://github.com/angular/angular.js/issues/15953)) - **angular-loader:** do not depend on "closure" globals that may not be available ([a3226d](https://github.com/angular/angular.js/commit/a3226d01fadaf145713518dc5b8022b581c34e81), [#15880](https://github.com/angular/angular.js/issues/15880), [#15881](https://github.com/angular/angular.js/issues/15881)) ## New Features - **select:** expose info about selection state in controller ([0b962d](https://github.com/angular/angular.js/commit/0b962d4881e98327a91c37f7317da557aa991663), [#13172](https://github.com/angular/angular.js/issues/13172), [#10127](https://github.com/angular/angular.js/issues/10127)) - **$animate:** add support for `customFilter` ([ab114a](https://github.com/angular/angular.js/commit/ab114af8508bdbdb1fa5fd1e070d08818d882e28), [#14891](https://github.com/angular/angular.js/issues/14891)) - **$compile:** overload `.component()` to accept object map of components ([210112](https://github.com/angular/angular.js/commit/2101126ce72308d8fc468ca2411bb9972e614f79), [#14579](https://github.com/angular/angular.js/issues/14579), [#16062](https://github.com/angular/angular.js/issues/16062)) - **$log:** log all parameters in IE 9, not just the first two. ([3671a4](https://github.com/angular/angular.js/commit/3671a43be43d05b00c90dfb3a3f746c013139581)) - **ngMock:** describe unflushed http requests ([d9128e](https://github.com/angular/angular.js/commit/d9128e7b2371ab2bb5169ba854b21c78baa784d2), [#10596](https://github.com/angular/angular.js/issues/10596), [#15928](https://github.com/angular/angular.js/issues/15928)) ## Performance Improvements - **ngOptions:** prevent initial options repainting ([ff52b1](https://github.com/angular/angular.js/commit/ff52b188a759f2cc7ee6ee78a8c646c2354a47eb), [#15801](https://github.com/angular/angular.js/issues/15801), [#15812](https://github.com/angular/angular.js/issues/15812), [#16071](https://github.com/angular/angular.js/issues/16071)) - **$animate:** - avoid unnecessary computations if animations are globally disabled ([ce5ffb](https://github.com/angular/angular.js/commit/ce5ffbf667464bd58eae4c4af0917eb2685f1f6a), [#14914](https://github.com/angular/angular.js/issues/14914)) - do not retrieve `className` unless `classNameFilter` is used ([275978](https://github.com/angular/angular.js/commit/27597887379a1904cd86832602e286894b449a75)) # 1.6.4 phenomenal-footnote (2017-03-31) ## Bug Fixes - **$parse:** - standardize one-time literal vs non-literal and interceptors ([60394a](https://github.com/angular/angular.js/commit/60394a9d91dad8932fa900af7c8529837f1d4557), [#15858](https://github.com/angular/angular.js/issues/15858)) - fix infinite digest errors when watching objects with .valueOf in literals ([f5ddb1](https://github.com/angular/angular.js/commit/f5ddb10b56676c2ad912ce453acb87f0a7a94e01), [#15867](https://github.com/angular/angular.js/issues/15867)) - **ngModel:** prevent internal scope reference from being copied ([e1f8a6](https://github.com/angular/angular.js/commit/e1f8a6e82bb8a70079ef3db9a891b1c08b5bae31), [#15833](https://github.com/angular/angular.js/issues/15833)) - **jqLite:** make jqLite invoke jqLite.cleanData as a method ([9cde98](https://github.com/angular/angular.js/commit/9cde98cbc770f8d33fc074ba563b7ab6e2baaf8b), [#15846](https://github.com/angular/angular.js/issues/15846)) - **$http:** throw more informative error on invalid JSON response ([df8887](https://github.com/angular/angular.js/commit/df88873bb79213057057adb47151b626a7ec0e5d), [#15695](https://github.com/angular/angular.js/issues/15695), [#15724](https://github.com/angular/angular.js/issues/15724)) - **dateFilter:** correctly handle newlines in `format` string ([982271](https://github.com/angular/angular.js/commit/9822711ad2a401c2449239edc13d18b301714757), [#15794](https://github.com/angular/angular.js/issues/15794), [#15792](https://github.com/angular/angular.js/issues/15792)) ## New Features - **$resource:** add `hasBody` action configuration option ([a9f987](https://github.com/angular/angular.js/commit/a9f987a0c9653246ea471a89197907d94c0cea2a), [#10128](https://github.com/angular/angular.js/issues/10128), [#12181](https://github.com/angular/angular.js/issues/12181)) # 1.6.3 scriptalicious-bootstrapping (2017-03-08) ## Bug Fixes - **AngularJS:** - do not auto-bootstrap if the `src` exists but is empty ([3536e8](https://github.com/angular/angular.js/commit/3536e83d8a085b02bd6dcec8324800b7e6c734e4)) - do not auto bootstrap if the currentScript has been clobbered ([95f964](https://github.com/angular/angular.js/commit/95f964b827b6f5b5aab10af54f7831316c7a9935)) - do not auto-bootstrap if the script source is bad and inside SVG ([c8f78a](https://github.com/angular/angular.js/commit/c8f78a8ca9debc33a6deaf951f344b8d372bf210)) - **$log:** don't parse error stacks manually outside of IE/Edge ([64e5af](https://github.com/angular/angular.js/commit/64e5afc4786fdfd850c6bdb488a5aa2b8b077f74), [#15590](https://github.com/angular/angular.js/issues/15590), [#15767](https://github.com/angular/angular.js/issues/15767)) - **$sanitize:** prevent clobbered elements from freezing the browser ([3bb1dd](https://github.com/angular/angular.js/commit/3bb1dd5d7f7dcde6fea5a3148f8f10e92f451e9d), [#15699](https://github.com/angular/angular.js/issues/15699)) - **$animate:** - reset `classNameFilter` to `null` when a disallowed RegExp is used ([a584fb](https://github.com/angular/angular.js/commit/a584fb6e1569fc1dd85e23b251a7c126edc2dd5b), [#14913](https://github.com/angular/angular.js/issues/14913)) - improve detection on `ng-animate` in `classNameFilter` RegExp ([1f1331](https://github.com/angular/angular.js/commit/1f13313f403381581e1c31c57ebfe7a96546c6e4), [#14806](https://github.com/angular/angular.js/issues/14806)) - **filterFilter:** don't throw if `key.charAt` is not a function ([f27d19](https://github.com/angular/angular.js/commit/f27d19ed606bf05ba41698159ebbc5fbc195033e), [#15644](https://github.com/angular/angular.js/issues/15644), [#15660](https://github.com/angular/angular.js/issues/15660)) - **select:** - add attribute "selected" for `select[multiple]` ([851367](https://github.com/angular/angular.js/commit/8513674911300b27d518383a905fde9b3f25f7ae)) - keep original selection when using shift to add options in IE/Edge ([97b74a](https://github.com/angular/angular.js/commit/97b74ad6fbcbc4b63e37e9eb44962d6f8de83e8b), [#15675](https://github.com/angular/angular.js/issues/15675), [#15676](https://github.com/angular/angular.js/issues/15676)) - **$jsonpCallbacks:** allow `$window` to be mocked in unit tests ([5ca0de](https://github.com/angular/angular.js/commit/5ca0de64873c32ab2f540a3226e73c4175a15c50), [#15685](https://github.com/angular/angular.js/issues/15685), [#15686](https://github.com/angular/angular.js/issues/15686)) ## New Features - **info:** add `angularVersion` info to each module ([1e582e](https://github.com/angular/angular.js/commit/1e582e4fa486f340150bba95927f1b26d9142de2)) - **$injector:** add new `modules` property ([742123](https://github.com/angular/angular.js/commit/7421235f247e5b7113345401bc5727cfbf81ddc2)) - **Module:** add `info()` method ([09ba69](https://github.com/angular/angular.js/commit/09ba69078de6ba52c70571b82b6205929f6facc5), [#15225](https://github.com/angular/angular.js/issues/15225)) - **errorHandlingConfig:** make the depth for object stringification in errors configurable ([4a5eaf](https://github.com/angular/angular.js/commit/4a5eaf7bec85ceca8b934ebaff4d1834a1a09f57), [#15402](https://github.com/angular/angular.js/issues/15402), [#15433](https://github.com/angular/angular.js/issues/15433)) # 1.6.2 llamacorn-lovehug (2017-02-07) ## Bug Fixes - **$compile:** - do not swallow thrown errors in testsg ([0377c6](https://github.com/angular/angular.js/commit/0377c6f0e890cb4ed3eb020b96720b4b34f75df3), [#15629](https://github.com/angular/angular.js/issues/15629), [#15631](https://github.com/angular/angular.js/issues/15631)) - allow the usage of "$" in isolate scope property alias ([7f2af3](https://github.com/angular/angular.js/commit/7f2af3f923e7a3f85c8862d0ed57d21c72eae904), [#15594](https://github.com/angular/angular.js/issues/15594)) - **$location:** correctly handle external URL change during `$digest` ([b60761](https://github.com/angular/angular.js/commit/b607618342d6c4fab364966fe05f152be6bd4d5f), [#11075](https://github.com/angular/angular.js/issues/11075), [#12571](https://github.com/angular/angular.js/issues/12571), [#15556](https://github.com/angular/angular.js/issues/15556), [#15561](https://github.com/angular/angular.js/issues/15561)) - **$browser:** detect external changes in `history.state` ([fa50fb](https://github.com/angular/angular.js/commit/fa50fbaf57b3437be7a410ecaba7008dbe0ef239)) - **$resource:** - do not swallow errors in `success` callback ([27146e](https://github.com/angular/angular.js/commit/27146e8a7fad54c1342179b6d291b1b5c2ebe816), [#15624](https://github.com/angular/angular.js/issues/15624), [#15628](https://github.com/angular/angular.js/issues/15628)) - correctly unescape `/\.` even if `\.` comes from a param value ([419a48](https://github.com/angular/angular.js/commit/419a4813e354496bdf0df44e3f8afaa198df1ab1), [#15627](https://github.com/angular/angular.js/issues/15627)) - delete `$cancelRequest()` in `toJSON()` ([086c5d](https://github.com/angular/angular.js/commit/086c5d0354db8cb3d106b9ff966fb48d6fb46ef8), [#15244](https://github.com/angular/angular.js/issues/15244)) - **$animate:** correctly animate transcluded clones with `templateUrl` ([f01212](https://github.com/angular/angular.js/commit/f01212ab5287ac7a154da7d75037ed444e81eb34), [#15510](https://github.com/angular/angular.js/issues/15510), [#15514](https://github.com/angular/angular.js/issues/15514)) - **$route:** make asynchronous tasks count as pending requests ([eb968c](https://github.com/angular/angular.js/commit/eb968c4a6884838db05369a04459066424c5bba8), [#14159](https://github.com/angular/angular.js/issues/14159)) - **$parse:** make sure ES6 object computed properties are watched ([5e418b](https://github.com/angular/angular.js/commit/5e418b1145a1045da598c7863e785d647ea83850), [#15678](https://github.com/angular/angular.js/issues/15678)) - **$sniffer:** allow `history` for NW.js apps ([4a593d](https://github.com/angular/angular.js/commit/4a593db79ba1e21a6aa600a82cf6d757cad94d01), [#15474](https://github.com/angular/angular.js/issues/15474), [#15633](https://github.com/angular/angular.js/issues/15633)) - **input:** fix `step` validation for `input[type=number/range]` ([c95a67](https://github.com/angular/angular.js/commit/c95a6737fbd277e40c064bd9f68f383bf119505c), [#15504](https://github.com/angular/angular.js/issues/15504), [#15506](https://github.com/angular/angular.js/issues/15506)) - **select:** keep `ngModel` when selected option is recreated by `ngRepeat` ([131af8](https://github.com/angular/angular.js/commit/131af8272d269a541d04cb522c264a91e0ec8b6a), [#15630](https://github.com/angular/angular.js/issues/15630), [#15632](https://github.com/angular/angular.js/issues/15632)) - **ngValue:** correctly update the `value` property when `value` is undefined ([05aab6](https://github.com/angular/angular.js/commit/05aab660ce74f526f2110d3b5faf9a5b4f4e664b) [#15603](https://github.com/angular/angular.js/issues/15603), [#15605](https://github.com/angular/angular.js/issues/15605)) - **angularInit:** allow auto-bootstrapping from inline script ([bb464d](https://github.com/angular/angular.js/commit/bb464d16b434b9e2de2fecf80c192d4741cba879), [#15567](https://github.com/angular/angular.js/issues/15567), [#15571](https://github.com/angular/angular.js/issues/15571)) - **ngMockE2E:** ensure that mocked `$httpBackend` uses correct `$browser` ([bd63b2](https://github.com/angular/angular.js/commit/bd63b2235cd410251cb83eebd9a47d3102830b6b), [#15593](https://github.com/angular/angular.js/issues/15593)) ## New Features - **ngModel:** add `$overrideModelOptions` support ([2546c2](https://github.com/angular/angular.js/commit/2546c29f811b68eea4d68be7fa1c8f7bb562dc11), [#15415](https://github.com/angular/angular.js/issues/15415)) - **$parse:** allow watching array/object literals with non-primitive values ([25f008](https://github.com/angular/angular.js/commit/25f008f541d68b09efd7b428b648c6d4899e6972), [#15301](https://github.com/angular/angular.js/issues/15301)) # 1.5.11 princely-quest (2017-01-13) ## Bug Fixes - **$compile:** allow the usage of "$" in isolate scope property alias ([e75fbc](https://github.com/angular/angular.js/commit/e75fbc494e6a0da6a9231b40bb0382431b62be07), [#15586](https://github.com/angular/angular.js/issues/15586), [#15594](https://github.com/angular/angular.js/issues/15594)) - **angularInit:** allow auto-bootstrapping from inline script ([41aa91](https://github.com/angular/angular.js/commit/41aa9125b9aaf771addb250642f524a4e6f9d8d3), [#15567](https://github.com/angular/angular.js/issues/15567), [#15571](https://github.com/angular/angular.js/issues/15571)) - **$resource:** delete `$cancelRequest()` in `toJSON()` ([4f3858](https://github.com/angular/angular.js/commit/4f3858e7c371f87534397f45b9d002add33b00cc), [#15244](https://github.com/angular/angular.js/issues/15244)) - **$$cookieReader:** correctly handle forbidden access to `document.cookie` ([6933cf](https://github.com/angular/angular.js/commit/6933cf64fe51f54b10d1639f2b95bab3c1178df9), [#15523](https://github.com/angular/angular.js/issues/15523), [#15532](https://github.com/angular/angular.js/issues/15532)) # 1.6.1 promise-rectification (2016-12-23) ## Bug Fixes - **$q:** Add traceback to unhandled promise rejections ([174cb4](https://github.com/angular/angular.js/commit/174cb4a8c81e25581da5b452c2bb43b0fa377a9b), [#14631](https://github.com/angular/angular.js/issues/14631)) - **$$cookieReader:** correctly handle forbidden access to `document.cookie` ([33f769](https://github.com/angular/angular.js/commit/33f769b0a1214055c16fb59adad4897bf53d62bf), [#15523](https://github.com/angular/angular.js/issues/15523)) - **ngOptions:** do not unset the `selected` property unless necessary ([bc4844](https://github.com/angular/angular.js/commit/bc4844d3b297d80aecef89aa1b32615024decedc), [#15477](https://github.com/angular/angular.js/issues/15477)) - **ngModelOptions:** work correctly when on the template of `replace` directives ([5f8ed6](https://github.com/angular/angular.js/commit/5f8ed63f2ab02ffb9c21bf9c29d27c851d162e26), [#15492](https://github.com/angular/angular.js/issues/15492)) - **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously ([d52864](https://github.com/angular/angular.js/commit/d528644fe3e9ffd43999e7fc67806059f9e1083e)) - **jqLite:** silently ignore `after()` if element has no parent ([3d68b9](https://github.com/angular/angular.js/commit/3d68b9502848ff6714ef89bfb95b8e70ae34eff6), [#15331](https://github.com/angular/angular.js/issues/15331), [#15475](https://github.com/angular/angular.js/issues/15475)) - **$rootScope:** when adding/removing watchers during $digest ([163aca](https://github.com/angular/angular.js/commit/163aca336d7586a45255787af41b14b2a12361dd), [#15422](https://github.com/angular/angular.js/issues/15422)) ## Performance Improvements - **ngClass:** avoid unnecessary `.data()` accesses, deep-watching and copies ([1d3b65](https://github.com/angular/angular.js/commit/1d3b65adc2c22ff662159ef910089cf10d1edb7b), [#14404](https://github.com/angular/angular.js/issues/14404)) # 1.5.10 asynchronous-synchronization (2016-12-15) ## Bug Fixes - **$compile:** - don't throw tplrt error when there is whitespace around a top-level comment ([12752f](https://github.com/angular/angular.js/commit/12752f66ac425ab38a5ee574a4bfbf3516adc42c), [#15108](https://github.com/angular/angular.js/issues/15108)) - clean up `@`-binding observers when re-assigning bindings ([f3cb6e](https://github.com/angular/angular.js/commit/f3cb6e309aa1f676e5951ac745fa886d3581c2f4), [#15268](https://github.com/angular/angular.js/issues/15268)) - set attribute value even if `ngAttr*` contains no interpolation ([229799](https://github.com/angular/angular.js/commit/22979904fb754c59e9f6ee5d8763e3b8de0e18c2), [#15133](https://github.com/angular/angular.js/issues/15133)) - `bindToController` should work without `controllerAs` ([944989](https://github.com/angular/angular.js/commit/9449893763a4fd95ee8ff78b53c6966a874ec9ae), [#15088](https://github.com/angular/angular.js/issues/15088)) - do not overwrite values set in `$onInit()` for `<`-bound literals ([07e1ba](https://github.com/angular/angular.js/commit/07e1ba365fb5e8a049be732bd7b62f71e0aa1672), [#15118](https://github.com/angular/angular.js/issues/15118)) - avoid calling `$onChanges()` twice for `NaN` initial values ([0cf5be](https://github.com/angular/angular.js/commit/0cf5be52642f7e9d81a708b3005042eac6492572)) - **$location:** prevent infinite digest with IDN urls in Edge ([4bf892](https://github.com/angular/angular.js/commit/4bf89218130d434771089fdfe643490b8d2ee259), [#15217](https://github.com/angular/angular.js/issues/15217)) - **$rootScope:** correctly handle adding/removing watchers during `$digest` ([a9708d](https://github.com/angular/angular.js/commit/a9708de84b50f06eacda33834d5bbdfc97c97f37), [#15422](https://github.com/angular/angular.js/issues/15422)) - **$sce:** fix `adjustMatcher` to replace multiple `*` and `**` ([78eecb](https://github.com/angular/angular.js/commit/78eecb43dbb0500358d333aea8955bd0646a7790)) - **jqLite:** silently ignore `after()` if element has no parent ([77ed85](https://github.com/angular/angular.js/commit/77ed85bcd3be057a5a79231565ac7accc6d644c6), [#15331](https://github.com/angular/angular.js/issues/15331)) - **input[radio]:** use non-strict comparison for checkedness ([593a50](https://github.com/angular/angular.js/commit/593a5034841b3b7661d3bcbdd06b7a9d0876fd34)) - **select, ngOptions:** - let `ngValue` take precedence over option text with multiple interpolations ([5b7ec8](https://github.com/angular/angular.js/commit/5b7ec8c84e88ee08aacaf9404853eda0016093f5), [#15413](https://github.com/angular/angular.js/issues/15413)) - don't add comment nodes as empty options ([1d29c9](https://github.com/angular/angular.js/commit/1d29c91c3429de96e4103533752700d1266741be), [#15454](https://github.com/angular/angular.js/issues/15454)) - **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously ([e3d020](https://github.com/angular/angular.js/commit/e3d02070ab8a02c818dcc5114db6fba9d3f385d6)) - **$sanitize:** reduce stack height in IE <= 11 ([862dc2](https://github.com/angular/angular.js/commit/862dc2532f8126a4a71fd3d957884ba6f11f591c), [#14928](https://github.com/angular/angular.js/issues/14928)) - **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()` ([75c83f](https://github.com/angular/angular.js/commit/75c83ff3195931859a099f7a95bf81d32abf2eb3)) ## New Features - **bootstrap:** do not bootstrap from unknown schemes with a different origin ([bdeb33](https://github.com/angular/angular.js/commit/bdeb3392a8719131ab2b993f2a881c43a2860f92), [#15428](https://github.com/angular/angular.js/issues/15428)) - **$anchorScroll:** convert numeric hash targets to string ([a52640](https://github.com/angular/angular.js/commit/a5264090b66ad0cf9a93de84bb7b307868c0edef), [#14680](https://github.com/angular/angular.js/issues/14680)) - **$compile:** - add `preAssignBindingsEnabled` option ([f86576](https://github.com/angular/angular.js/commit/f86576def44005f180a66e3aa12d6cc73c1ac72c)) - throw error when directive name or factory function is invalid ([5c9399](https://github.com/angular/angular.js/commit/5c9399d18ae5cd79e6cf6fc4377d66df00f6fcc7), [#15056](https://github.com/angular/angular.js/issues/15056)) - **$controller:** throw when requested controller is not registered ([9ae793](https://github.com/angular/angular.js/commit/9ae793d8a69afe84370b601e07fc375fc18a576a), [#14980](https://github.com/angular/angular.js/issues/14980)) - **$location:** add support for selectively rewriting links based on attribute ([a4a222](https://github.com/angular/angular.js/commit/a4a22266f127d3b9a6818e6f4754f048e253f693)) - **$resource:** pass `status`/`statusText` to success callbacks ([a8da25](https://github.com/angular/angular.js/commit/a8da25c74d2c1f6265f0fafd95bf72c981d9d678), [#8341](https://github.com/angular/angular.js/issues/8841), [#8841](https://github.com/angular/angular.js/issues/8841)) - **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator` ([0e1651](https://github.com/angular/angular.js/commit/0e1651bfd28ba73ebd0e4943d85af48c4506e02c), [#3410](https://github.com/angular/angular.js/issues/3410), [#3516](https://github.com/angular/angular.js/issues/3516)) ## Performance Improvements - **all:** don't trigger digests after enter/leave of structural directives ([c57779](https://github.com/angular/angular.js/commit/c57779d8725493c5853dceda0105dafd5c0e3a7c), [#15322](https://github.com/angular/angular.js/issues/15322)) - **$compile:** validate `directive.restrict` property on directive init ([31d464](https://github.com/angular/angular.js/commit/31d464feef38b1cc950da6c8dccd0f194ebfc68b)) - **ngOptions:** avoid calls to `element.value` ([e269ad](https://github.com/angular/angular.js/commit/e269ad1244bc50fee9218f7c18fab3e9ab063aab)) - **jqLite:** move bind/unbind definitions out of the loop ([7717b9](https://github.com/angular/angular.js/commit/7717b96e950a5916a5f12fd611c73d3b06a8d717)) # 1.6.0 rainbow-tsunami (2016-12-08) **Here are the full changes for the release of 1.6.0 that are not already released in the 1.5.x branch, consolidating all the changes shown in the previous 1.6.0 release candidates.** ## New Features - **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa), [#10922](https://github.com/angular/angular.js/issues/10922)) - **$compile:** - add `preAssignBindingsEnabled` option ([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef)) - set `preAssignBindingsEnabled` to false by default ([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858), [#15352](https://github.com/angular/angular.js/issues/15352)) - throw error when directive name or factory function is invalid ([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec) [#15056](https://github.com/angular/angular.js/issues/15056)) - **jqLite:** - implement `jqLite(f)` as an alias to `jqLite(document).ready(f)` ([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e)) - don't throw for elements with missing `getAttribute` ([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417)) - don't get/set properties when getting/setting boolean attributes ([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304), [#14126](https://github.com/angular/angular.js/issues/14126)) - don't remove a boolean attribute for `.attr(attrName, '')` ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)) - remove the attribute for `.attr(attribute, null)` ([4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)) - return `[]` for `.val()` on ` ``` The migration strategy is to convert values that matched with non-strict conversion so that they will match with strict conversion. - **feat(ngModelOptions): allow options to be inherited from ancestor `ngModelOptions` ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa))**: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.$options.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes. One benefit of these changes, though, is that the `ngModelControler.$options` property is now guaranteed to be defined so there is no need to check before accessing. So, previously: ``` var myOption = ngModelController.$options && ngModelController.$options['my-option']; ``` and now: ``` var myOption = ngModelController.$options.getOption('my-option'); ``` ### **jqLite** due to: - **[fc0c11](https://github.com/angular/angular.js/commit/fc0c11db845d53061430b7f05e773dcb3fb5b860)**: camelCase keys in `jqLite#data` Previously, keys passed to the data method were left untouched. Now they are internally camelCased similarly to how jQuery handles it, i.e. only single (!) hyphens followed by a lowercase letter get converted to an uppercase letter. This means keys `a-b` and `aB` represent the same data piece; writing to one of them will also be reflected if you ask for the other one. If you use Angular with jQuery, it already behaved in this way so no changes are required on your part. To migrate the code follow the examples below: BEFORE: ```js /* 1 */ elem.data('my-key', 2); elem.data('myKey', 3); /* 2 */ elem.data('foo-bar', 42); elem.data()['foo-bar']; // 42 elem.data()['fooBar']; // undefined /* 3 */ elem.data()['foo-bar'] = 1; elem.data()['fooBar'] = 2; elem.data('foo-bar'); // 1 ``` AFTER: ```js /* 1 */ // Rename one of the keys as they would now map to the same data slot. elem.data('my-key', 2); elem.data('my-key2', 3); /* 2 */ elem.data('foo-bar', 42); elem.data()['foo-bar']; // undefined elem.data()['fooBar']; // 42 /* 3 */ elem.data()['foo-bar'] = 1; elem.data()['fooBar'] = 2; elem.data('foo-bar'); // 2 ``` - **[73050c](https://github.com/angular/angular.js/commit/73050cdda04675bfa6705dc841ddbbb6919eb048)**: align jqLite camelCasing logic with JQuery Before, when Angular was used without jQuery, the key passed to the css method was more heavily camelCased; now only a single (!) hyphen followed by a lowercase letter is getting transformed. This also affects APIs that rely on the css method, like ngStyle. If you use Angular with jQuery, it already behaved in this way so no changes are needed on your part. To migrate the code follow the example below: Before: HTML: ```html // All five versions used to be equivalent.
``` JS: ```js // All five versions used to be equivalent. elem.css('background_color', 'blue'); elem.css('background:color', 'blue'); elem.css('background-color', 'blue'); elem.css('background--color', 'blue'); elem.css('backgroundColor', 'blue'); // All five versions used to be equivalent. var bgColor = elem.css('background_color'); var bgColor = elem.css('background:color'); var bgColor = elem.css('background-color'); var bgColor = elem.css('background--color'); var bgColor = elem.css('backgroundColor'); ``` After: HTML: ```html // Previous five versions are no longer equivalent but these two still are.
``` JS: ```js // Previous five versions are no longer equivalent but these two still are. elem.css('background-color', 'blue'); elem.css('backgroundColor', 'blue'); // Previous five versions are no longer equivalent but these two still are. var bgColor = elem.css('background-color'); var bgColor = elem.css('backgroundColor'); ``` - **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes Previously, all boolean attributes were reflected into the corresponding property when calling a setter and from the corresponding property when calling a getter, even on elements that don't treat those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to know when to reflect the property. Note that this browser-level conversion differs between browsers; if you need to dynamically change the state of an element, you should modify the property, not the attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed description about a related change in jQuery 1.9. This change aligns jqLite with jQuery 3. To migrate the code follow the example below: Before: CSS: ```css input[checked="checked"] { ... } ``` JS: ```js elem1.attr('checked', 'checked'); elem2.attr('checked', false); ``` After: CSS: ```css input:checked { ... } ``` JS: ```js elem1.prop('checked', true); elem2.prop('checked', false); ``` - **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**: don't remove a boolean attribute for `.attr(attrName, '')` Before, using the `attr` method with an empty string as a value would remove the boolean attribute. Now it sets it to its lowercase name as was happening for every non-empty string so far. The only two values that remove the boolean attribute are now null & false, just like in jQuery. To migrate the code follow the example below: Before: ```js elem.attr(booleanAttrName, ''); ``` After: ```js elem.attr(booleanAttrName, false); ``` or: ```js elem.attr(booleanAttrName, null); ``` - **[4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)**: remove the attribute for `.attr(attribute, null)` Invoking `elem.attr(attributeName, null)` would set the `attributeName` attribute value to a string `"null"`, now it removes the attribute instead. To migrate the code follow the example below: Before: ```js elem.attr(attributeName, null); ``` After: ```js elem.attr(attributeName, "null"); ``` - **[d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8)**: return [] for .val() on ` ``` JavaScript: ```js var value = $element.val(); if (value) { /* do something */ } ``` After: HTML: ```html ``` JavaScript: ```js var value = $element.val(); if (value.length > 0) { /* do something */ } ``` ### `ngModel` due to: - **[7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b)**: treat synchronous validators as boolean always Previously, only a literal `false` return would resolve as the synchronous validator failing. Now, all falsy JavaScript values are treated as failing the validator, as one would naturally expect. Specifically, the values `0` (the number zero), `null`, `NaN` and `''` (the empty string) used to be considered valid (passing) and they are now considered invalid (failing). The value `undefined` was treated similarly to a pending asynchronous validator, causing the validation to be pending. `undefined` is also now considered invalid. To migrate, make sure your synchronous validators are returning either a literal `true` or a literal `false` value. For most code, we expect this to already be the case. Only a very small subset of projects will be affected. Namely, anyone using `undefined` or any falsy value as a return will now see their validation failing, whereas previously falsy values other than `undefined` would have been seen as passing and `undefined` would have been seen as pending. - **[9e24e7](https://github.com/angular/angular.js/commit/9e24e774a558143b3478536911a3a4c1714564ba)**: change controllers to use prototype methods The use of prototype methods instead of new methods per instance removes the ability to pass NgModelController and FormController methods without context. For example ```js $scope.$watch('something', myNgModelCtrl.$render) ``` will no longer work because the `$render` method is passed without any context. This must now be replaced with ```js $scope.$watch('something', function() { myNgModelCtrl.$render(); }) ``` or possibly by using `Function.prototype.bind` or `angular.bind`. ### `aria/ngModel` due to: - **[975a61](https://github.com/angular/angular.js/commit/975a6170efceb2a5e6377c57329731c0636eb8c8)**: do not overwrite the default `$isEmpty()` method for checkboxes Custom `checkbox`-shaped controls (e.g. checkboxes, menuitemcheckboxes), no longer have a custom `$isEmpty()` method on their `NgModelController` that checks for `value === false`. Unless overwritten, the default `$isEmpty()` method will be used, which treats `undefined`, `null`, `NaN` and `''` as "empty". **Note:** The `$isEmpty()` method is used to determine if the checkbox is checked ("not empty" means "checked") and thus it can indirectly affect other things, such as the control's validity with respect to the `required` validator (e.g. "empty" + "required" --> "invalid"). Before: ```js var template = ''; var customCheckbox = $compile(template)(scope); var ctrl = customCheckbox.controller('ngModel'); scope.$apply('value = false'); console.log(ctrl.$isEmpty()); //--> true scope.$apply('value = true'); console.log(ctrl.$isEmpty()); //--> false scope.$apply('value = undefined'/* or null or NaN or '' */); console.log(ctrl.$isEmpty()); //--> false ``` After: ```js var template = ''; var customCheckbox = $compile(template)(scope); var ctrl = customCheckbox.controller('ngModel'); scope.$apply('value = false'); console.log(ctrl.$isEmpty()); //--> false scope.$apply('value = true'); console.log(ctrl.$isEmpty()); //--> false scope.$apply('value = undefined'/* or null or NaN or '' */); console.log(ctrl.$isEmpty()); //--> true ``` -- If you want to have a custom `$isEmpty()` method, you need to overwrite the default. For example: ```js .directive('myCheckbox', function myCheckboxDirective() { return { require: 'ngModel', link: function myCheckboxPostLink(scope, elem, attrs, ngModelCtrl) { ngModelCtrl.$isEmpty = function myCheckboxIsEmpty(value) { return !value; // Any falsy value means "empty" // Or to restore the previous behavior: // return value === false; }; } }; }) ``` ### `$http` due to: - **[b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014)**: remove deprecated callback methods: `success()/error()` `$http`'s deprecated custom callback methods - `success()` and `error()` - have been removed. You can use the standard `then()`/`catch()` promise methods instead, but note that the method signatures and return values are different. `success(fn)` can be replaced with `then(fn)`, and `error(fn)` can be replaced with either `then(null, fn)` or `catch(fn)`. Before: ```js $http(...). success(function onSuccess(data, status, headers, config) { // Handle success ... }). error(function onError(data, status, headers, config) { // Handle error ... }); ``` After: ```js $http(...). then(function onSuccess(response) { // Handle success var data = response.data; var status = response.status; var statusText = response.statusText; var headers = response.headers; var config = response.config; ... }, function onError(response) { // Handle error var data = response.data; var status = response.status; var statusText = response.statusText; var headers = response.headers; var config = response.config; ... }); // or $http(...). then(function onSuccess(response) { // Handle success var data = response.data; var status = response.status; var statusText = response.statusText; var headers = response.headers; var config = response.config; ... }). catch(function onError(response) { // Handle error var data = response.data; var status = response.status; var statusText = response.statusText; var headers = response.headers; var config = response.config; ... }); ``` **Note:** There is a subtle difference between the variations showed above. When using `$http(...).success(onSuccess).error(onError)` or `$http(...).then(onSuccess, onError)`, the `onError()` callback will only handle errors/rejections produced by the `$http()` call. If the `onSuccess()` callback produces an error/rejection, it won't be handled by `onError()` and might go unnoticed. In contrast, when using `$http(...).then(onSuccess).catch(onError)`, `onError()` will handle errors/rejections produced by both `$http()` _and_ `onSuccess()`. - **[fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb)**: JSONP callback must be specified by `jsonpCallbackParam` config You can no longer use the `JSON_CALLBACK` placeholder in your JSONP requests. Instead you must provide the name of the query parameter that will pass the callback via the `jsonpCallbackParam` property of the config object, or app-wide via the `$http.defaults.jsonpCallbackParam` property, which is `"callback"` by default. Before this change: ```js $http.json('trusted/url?callback=JSON_CALLBACK'); $http.json('other/trusted/url', {params: {cb:'JSON_CALLBACK'}}); ``` After this change: ```js $http.json('trusted/url'); $http.json('other/trusted/url', {jsonpCallbackParam:'cb'}); ``` - **[6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4)**: JSONP requests now require a trusted resource URL All JSONP requests now require the URL to be trusted as resource URLs. There are two approaches to trust a URL: **Whitelisting with the `$sceDelegateProvider.resourceUrlWhitelist()` method.** You configure this list in a module configuration block: ```js appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) { $sceDelegateProvider.resourceUrlWhitelist([ // Allow same origin resource loads. 'self', // Allow JSONP calls that match this pattern 'https://some.dataserver.com/**.jsonp?**' ]); }]); ``` **Explicitly trusting the URL via the `$sce.trustAsResourceUrl(url)` method.** You can pass a trusted object instead of a string as a URL to the `$http` service: ```js var promise = $http.jsonp($sce.trustAsResourceUrl(url)); ``` - **[4f6f2b](https://github.com/angular/angular.js/commit/4f6f2bce4ac93b85320e42e5023c09d099779b7d)**: properly increment/decrement `$browser.outstandingRequestCount` HTTP requests now update the outstanding request count synchronously. Previously the request count would not have been updated until the request to the server is actually in flight. Now the request count is updated before the async interceptor is called. The new behaviour is correct but it may change the expected behaviour in a small number of e2e test cases where an async request interceptor is being used. ### `$q` due to: - **[e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9)**: treat thrown errors as regular rejections Previously, throwing an error from a promise's `onFulfilled` or `onRejection` handlers, would result in passing the error to the `$exceptionHandler()` (in addition to rejecting the promise with the error as reason). Now, a thrown error is treated exactly the same as a regular rejection. This applies to all services/controllers/filters etc that rely on `$q` (including built-in services, such as `$http` and `$route`). For example, `$http`'s `transformRequest/Response` functions or a route's `redirectTo` function as well as functions specified in a route's `resolve` object, will no longer result in a call to `$exceptionHandler()` if they throw an error. Other than that, everything will continue to behave in the same way; i.e. the promises will be rejected, route transition will be cancelled, `$routeChangeError` events will be broadcasted etc. - **[c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0)**: report promises with non rejection callback Unhandled rejected promises will be logged to $exceptionHandler. Tests that depend on specific order or number of messages in $exceptionHandler will need to handle rejected promises report. ### `ngTransclude` due to: - **[32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301)**: use fallback content if only whitespace is provided Previously whitespace only transclusion would be treated as the transclusion being "not empty", which meant that fallback content was not used in that case. Now if you only provide whitespace as the transclusion content, it will be assumed to be empty and the fallback content will be used instead. If you really do want whitespace then you can force it to be used by adding a comment to the whitespace. Previously this would not fallback to default content: ```html ``` Now the whitespace between the opening and closing tags is treated as empty. To force the previous behaviour simply add a comment: ```html ``` ### `$compile` due to: - **[13c252](https://github.com/angular/angular.js/commit/13c2522baf7c8f616b2efcaab4bffd54c8736591)**: correctly merge consecutive text nodes on IE11 **Note:** Everything described below affects **IE11 only**. Previously, consecutive text nodes would not get merged if they had no parent. They will now, which might have unexpected side effects in the following cases: 1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly: ```js // Assuming: var textNodes = [ document.createTextNode('{{'), document.createTextNode('"foo:"'), document.createTextNode('}}') ]; var compiledNodes = $compile(textNodes)($rootScope); // Before: console.log(compiledNodes.length); // 3 console.log(compiledNodes.text()); // {{'foo'}} // After: console.log(compiledNodes.length); // 1 console.log(compiledNodes.text()); // foo // To get the old behavior, compile each node separately: var textNodes = [ document.createTextNode('{{'), document.createTextNode('"foo"'), document.createTextNode('}}') ]; var compiledNodes = angular.element(textNodes.map(function (node) { return $compile(node)($rootScope)[0]; })); ``` 2. Using multi-slot transclusion with non-consecutive, default-content text nodes (that form interpolated expressions when merged): ```js // Assuming the following component: .component('someThing', { template: '' transclude: { ignored: 'veryImportantContent' } }) ``` ```html {{ Nooot 'foo'}} {{ <-- Two separate 'foo'}} <-- text nodes foo <-- The text nodes were merged into `{{'foo'}}`, which was then interpolated {{ Nooot 'foo'}} {{ <-- Two separate 'foo'}} <-- nodes ``` - **[b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4)**: move check for interpolation of `on-"event"` attributes to compile time Using interpolation in any on* event attributes (e.g. ` ``` In this setup, there are two ways to trigger ngFocus. First from a user interaction: * Click on the input control * The input control gets focus * The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to `$apply()` Second programmatically: * Click the button * The `ngClick` directive sets the value of `$scope.hasFocus` to true inside a call to `$apply` * The `$digest` runs, which triggers the watch inside the `setFocusIf` directive * The watch's handle runs, which gives the focus to the input * The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to `$apply()` In this second scenario, we are already inside a `$digest` when the ngFocus directive makes another call to `$apply()`, causing this error to be thrown. It is possible to workaround this problem by moving the call to set the focus outside of the digest, by using `$timeout(fn, 0, false)`, where the `false` value tells AngularJS not to wrap this `fn` in an `$apply` block: ``` myApp.directive('setFocusIf', function($timeout) { return { link: function($scope, $element, $attr) { $scope.$watch($attr.setFocusIf, function(value) { if ( value ) { $timeout(function() { // We must reevaluate the value in case it was changed by a subsequent // watch handler in the digest. if ( $scope.$eval($attr.setFocusIf) ) { $element[0].focus(); } }, 0, false); } }); } } }); ``` ## Diagnosing This Error When you get this error it can be rather daunting to diagnose the cause of the issue. The best course of action is to investigate the stack trace from the error. You need to look for places where `$apply` or `$digest` have been called and find the context in which this occurred. There should be two calls: * The first call is the good `$apply`/`$digest` and would normally be triggered by some event near the top of the call stack. * The second call is the bad `$apply`/`$digest` and this is the one to investigate. Once you have identified this call you work your way up the stack to see what the problem is. * If the second call was made in your application code then you should look at why this code has been called from within an `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the sync/async scenario described earlier. * If the second call was made inside an AngularJS directive then it is likely that it matches the second programmatic event trigger scenario described earlier. In this case you may need to look further up the tree to what triggered the event in the first place. ### Example Problem Let's look at how to investigate this error using the `setFocusIf` example from above. This example defines a new `setFocusIf` directive that sets the focus on the element where it is defined when the value of its attribute becomes true. angular.module('app', []).directive('setFocusIf', function() { return function link($scope, $element, $attr) { $scope.$watch($attr.setFocusIf, function(value) { if (value) { $element[0].focus(); } }); }; }); When you click on the button to cause the focus to occur we get our `$rootScope:inprog` error. The stacktrace looks like this: ``` Error: [$rootScope:inprog] at Error (native) at angular.min.js:6:467 at n (angular.min.js:105:60) at g.$get.g.$apply (angular.min.js:113:195) at HTMLInputElement. (angular.min.js:198:401) at angular.min.js:32:32 at Array.forEach (native) at q (angular.min.js:7:295) at HTMLInputElement.c (angular.min.js:32:14) at Object.fn (app.js:12:38) angular.js:10111 (anonymous function) angular.js:10111 $get angular.js:7412 $get.g.$apply angular.js:12738 <--- $apply (anonymous function) angular.js:19833 <--- called here (anonymous function) angular.js:2890 q angular.js:320 c angular.js:2889 (anonymous function) app.js:12 $get.g.$digest angular.js:12469 $get.g.$apply angular.js:12742 <--- $apply (anonymous function) angular.js:19833 <--- called here (anonymous function) angular.js:2890 q angular.js:320 ``` We can see (even though the AngularJS code is minified) that there were two calls to `$apply`, first on line `19833`, then on line `12738` of `angular.js`. It is this second call that caused the error. If we look at the angular.js code, we can see that this call is made by an AngularJS directive. ``` var ngEventDirectives = {}; forEach( 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), function(name) { var directiveName = directiveNormalize('ng-' + name); ngEventDirectives[directiveName] = ['$parse', function($parse) { return { compile: function($element, attr) { var fn = $parse(attr[directiveName]); return function(scope, element, attr) { element.on(lowercase(name), function(event) { scope.$apply(function() { fn(scope, {$event:event}); }); }); }; } }; }]; } ); ``` It is not possible to tell which from the stack trace, but we happen to know in this case that it is the `ngFocus` directive. Now look up the stack to see that our application code is only entered once in `app.js` at line `12`. This is where our problem is: ``` 10: link: function($scope, $element, $attr) { 11: $scope.$watch($attr.setFocusIf, function(value) { 12: if ( value ) { $element[0].focus(); } <---- This is the source of the problem 13: }); 14: } ``` We can now see that the second `$apply` was caused by us programmatically triggering a DOM event (i.e. focus) to occur. We must fix this by moving the code outside of the $apply block using `$timeout` as described above. ## Further Reading To learn more about AngularJS processing model please check out the {@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc. angular.js-1.7.9/docs/content/error/$route/000077500000000000000000000000001356472325200205425ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/$route/norout.ngdoc000066400000000000000000000004751356472325200231120ustar00rootroot00000000000000@ngdoc error @name $route:norout @fullName Tried updating route with no current route @description Occurs when an attempt is made to update the parameters on the current route when there is no current route. This can happen if you try to call `$route.updateParams();` before the first route transition has completed.angular.js-1.7.9/docs/content/error/$sanitize/000077500000000000000000000000001356472325200212325ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/$sanitize/elclob.ngdoc000066400000000000000000000011351356472325200235060ustar00rootroot00000000000000@ngdoc error @name $sanitize:elclob @fullName Failed to sanitize html because the element is clobbered @description This error occurs when `$sanitize` sanitizer is unable to traverse the HTML because one or more of the elements in the HTML have been "clobbered". This could be a sign that the payload contains code attempting to cause a DoS attack on the browser. Typically clobbering breaks the `nextSibling` property on an element so that it points to one of its child nodes. This makes it impossible to walk the HTML tree without getting stuck in an infinite loop, which causes the browser to freeze.angular.js-1.7.9/docs/content/error/$sanitize/noinert.ngdoc000066400000000000000000000010561356472325200237260ustar00rootroot00000000000000@ngdoc error @name $sanitize:noinert @fullName Can't create an inert html document @description This error occurs when `$sanitize` sanitizer determines that `document.implementation.createHTMLDocument ` api is not supported by the current browser. This api is necessary for safe parsing of HTML strings into DOM trees and without it the sanitizer can't sanitize the input. The api is present in all supported browsers including IE 9.0, so the presence of this error usually indicates that AngularJS's `$sanitize` is being used on an unsupported platform. angular.js-1.7.9/docs/content/error/$sanitize/uinput.ngdoc000066400000000000000000000013511356472325200235720ustar00rootroot00000000000000@ngdoc error @name $sanitize:uinput @fullName Failed to sanitize html because the input is unstable @description This error occurs when `$sanitize` sanitizer tries to check the input for possible mXSS payload and the verification errors due to the input mutating indefinitely. This could be a sign that the payload contains code exploiting an mXSS vulnerability in the browser. mXSS attack exploit browser bugs that cause some browsers parse a certain html strings into DOM, which once serialized doesn't match the original input. These browser bugs can be exploited by attackers to create payload which looks harmless to sanitizers, but due to mutations caused by the browser are turned into dangerous code once processed after sanitization. angular.js-1.7.9/docs/content/error/$sce/000077500000000000000000000000001356472325200201565ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/$sce/icontext.ngdoc000066400000000000000000000004251356472325200230300ustar00rootroot00000000000000@ngdoc error @name $sce:icontext @fullName Invalid / Unknown SCE context @description The context enum passed to {@link ng.$sce#trustAs $sce.trustAs} was not recognized. Please consult the list of {@link ng.$sce#contexts supported Strict Contextual Escaping (SCE) contexts}. angular.js-1.7.9/docs/content/error/$sce/iequirks.ngdoc000066400000000000000000000012231356472325200230240ustar00rootroot00000000000000@ngdoc error @name $sce:iequirks @fullName IE<11 in quirks mode is unsupported @description This error occurs when you are using AngularJS with {@link ng.$sce Strict Contextual Escaping (SCE)} mode enabled (the default) on IE10 or lower in quirks mode. In this mode, IE<11 allow one to execute arbitrary javascript by the use of the `expression()` syntax and is not supported. Refer [CSS expressions no longer supported for the Internet zone](http://msdn.microsoft.com/en-us/library/ie/dn384050(v=vs.85).aspx) to learn more about them. To resolve this error please specify the proper doctype at the top of your main html document: ``` ``` angular.js-1.7.9/docs/content/error/$sce/imatcher.ngdoc000066400000000000000000000005541356472325200227720ustar00rootroot00000000000000@ngdoc error @name $sce:imatcher @fullName Invalid matcher (only string patterns and RegExp instances are supported) @description Please see {@link $sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and {@link $sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} for the list of acceptable items. angular.js-1.7.9/docs/content/error/$sce/insecurl.ngdoc000066400000000000000000000030551356472325200230210ustar00rootroot00000000000000@ngdoc error @name $sce:insecurl @fullName Processing of a Resource from Untrusted Source Blocked @description AngularJS' {@link ng.$sce Strict Contextual Escaping (SCE)} mode (enabled by default) has blocked loading a resource from an insecure URL. Typically, this would occur if you're attempting to load an AngularJS template from an untrusted source. It's also possible that a custom directive threw this error for a similar reason. AngularJS only loads templates from trusted URLs (by calling {@link ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl} on the template URL). By default, only URLs that belong to the same origin are trusted. These are urls with the same domain, protocol and port as the application document. The {@link ng.directive:ngInclude ngInclude} directive and {@link guide/directive directives} that specify a `templateUrl` require a trusted resource URL. To load templates from other domains and/or protocols, either adjust the {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist}/ {@link ng.$sceDelegateProvider#resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link ng.$sce#trustAsResourceUrl $sce.trustAsResourceUrl}. **Note**: The browser's [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) policy apply that may further restrict whether the template is successfully loaded. (e.g. neither cross-domain requests won't work on all browsers nor `file://` requests on some browsers) angular.js-1.7.9/docs/content/error/$sce/itype.ngdoc000066400000000000000000000003601356472325200223230ustar00rootroot00000000000000@ngdoc error @name $sce:itype @fullName String Value is Required for SCE Trust Call @description {@link ng.$sce#trustAs $sce.trustAs} requires a string value. Read more about {@link ng.$sce Strict Contextual Escaping (SCE)} in AngularJS. angular.js-1.7.9/docs/content/error/$sce/iwcard.ngdoc000066400000000000000000000006231356472325200224440ustar00rootroot00000000000000@ngdoc error @name $sce:iwcard @fullName The sequence *** is not a valid pattern wildcard @description The strings in {@link $sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and {@link $sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} may not contain the undefined sequence `***`. Only `*` and `**` wildcard patterns are defined. angular.js-1.7.9/docs/content/error/$sce/unsafe.ngdoc000066400000000000000000000012551356472325200224560ustar00rootroot00000000000000@ngdoc error @name $sce:unsafe @fullName Require a safe/trusted value @description The value provided for use in a specific context was not found to be safe/trusted for use. AngularJS's {@link ng.$sce Strict Contextual Escaping (SCE)} mode (enabled by default), requires bindings in certain contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an AngularJS template from a URL requires that the URL is one considered safe for loading resources.) This helps prevent XSS and other security issues. Read more at {@link ng.$sce Strict Contextual Escaping (SCE)} You may want to include the ngSanitize module to use the automatic sanitizing. angular.js-1.7.9/docs/content/error/$templateRequest/000077500000000000000000000000001356472325200225705ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/$templateRequest/tpload.ngdoc000066400000000000000000000014561356472325200250750ustar00rootroot00000000000000@ngdoc error @name $templateRequest:tpload @fullName Error Loading Template @description This error occurs when {@link $templateRequest} attempts to fetch a template from some URL, and the request fails. The template URL might be defined in a directive/component definition, an instance of `ngInclude`, an instance of `ngMessagesInclude` or a templated route in a `$route` route definition. To resolve this error, ensure that the URL of the template is spelled correctly and resolves to correct absolute URL. The [Chrome Developer Tools](https://developers.google.com/chrome-developer-tools/docs/network#network_panel_overview) might also be helpful in determining why the request failed. If you are using {@link ng.$templateCache} to pre-load templates, ensure that the cache was populated with the template. angular.js-1.7.9/docs/content/error/$timeout/000077500000000000000000000000001356472325200210725ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/$timeout/badprom.ngdoc000066400000000000000000000016121356472325200235320ustar00rootroot00000000000000@ngdoc error @name $timeout:badprom @fullName Non-$timeout promise @description This error occurs when calling {@link ng.$timeout#cancel $timeout.cancel()} with a promise that was not generated by the {@link ng.$timeout $timeout} service. This can, for example, happen when calling {@link ng.$q#the-promise-api then()/catch()} on the returned promise, which creates a new promise, and pass that new promise to {@link ng.$timeout#cancel $timeout.cancel()}. Example of incorrect usage that leads to this error: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); ``` To fix the example above, keep a reference to the promise returned by {@link ng.$timeout $timeout()} and pass that to {@link ng.$timeout#cancel $timeout.cancel()}: ```js var promise = $timeout(doSomething, 1000); var newPromise = promise.then(doSomethingElse); $timeout.cancel(promise); ``` angular.js-1.7.9/docs/content/error/filter/000077500000000000000000000000001356472325200206255ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/filter/notarray.ngdoc000066400000000000000000000026401356472325200235020ustar00rootroot00000000000000@ngdoc error @name filter:notarray @fullName Not an array @description This error occurs when {@link ng.filter filter} is not used with an array: ```html
{{ key }} : {{ value }}
``` Filter must be used with an array so a subset of items can be returned. The array can be initialized asynchronously and therefore null or undefined won't throw this error. To filter an object by the value of its properties you can create your own custom filter: ```js angular.module('customFilter',[]) .filter('custom', function() { return function(input, search) { if (!input) return input; if (!search) return input; var expected = ('' + search).toLowerCase(); var result = {}; angular.forEach(input, function(value, key) { var actual = ('' + value).toLowerCase(); if (actual.indexOf(expected) !== -1) { result[key] = value; } }); return result; } }); ``` That can be used as: ```html
{{ key }} : {{ value }}
``` You could as well convert the object to an array using a filter such as [toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter): ```html
{{ item }}
``` angular.js-1.7.9/docs/content/error/index.ngdoc000066400000000000000000000007651356472325200214730ustar00rootroot00000000000000@ngdoc overview @name Error Reference @description # Error Reference Use the Error Reference manual to find information about error conditions in your AngularJS app. Errors thrown in production builds of AngularJS will log links to this site on the console. Other useful references for debugging your app include: - {@link api/ API Reference} for detailed information about specific features - {@link guide/ Developer Guide} for AngularJS concepts - {@link tutorial/ Tutorial} for getting started angular.js-1.7.9/docs/content/error/jqLite/000077500000000000000000000000001356472325200205705ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/jqLite/nosel.ngdoc000066400000000000000000000011341356472325200227230ustar00rootroot00000000000000@ngdoc error @name jqLite:nosel @fullName Unsupported Selector Lookup @description In order to keep AngularJS small, AngularJS implements only a subset of the selectors in {@link angular.element#angularjs-s-jqlite jqLite}. This error occurs when a jqLite instance is invoked with a selector other than this subset. In order to resolve this error, rewrite your code to only use tag name selectors and manually traverse the DOM using the APIs provided by jqLite. Alternatively, you can include a full version of jQuery, which AngularJS will automatically use and that will make all selectors available. angular.js-1.7.9/docs/content/error/jqLite/offargs.ngdoc000066400000000000000000000003601356472325200232320ustar00rootroot00000000000000@ngdoc error @name jqLite:offargs @fullName Invalid jqLite#off() parameter @description This error occurs when trying to pass too many arguments to `jqLite#off`. Note that `jqLite#off` does not support namespaces or selectors like jQuery. angular.js-1.7.9/docs/content/error/jqLite/onargs.ngdoc000066400000000000000000000004011356472325200230700ustar00rootroot00000000000000@ngdoc error @name jqLite:onargs @fullName Invalid jqLite#on() Parameters @description This error occurs when trying to pass too many arguments to `jqLite#on`. Note that `jqLite#on` does not support the `selector` or `eventData` parameters as jQuery does. angular.js-1.7.9/docs/content/error/linky/000077500000000000000000000000001356472325200204665ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/linky/notstring.ngdoc000066400000000000000000000011661356472325200235350ustar00rootroot00000000000000@ngdoc error @name linky:notstring @fullName Not a string @description This error occurs when {@link ngSanitize.linky linky} is used with a non-empty, non-string value: ```html
``` `linky` is supposed to be used with string values only, and therefore assumes that several methods (such as `.match()`) are available on the passed in value. The value can be initialized asynchronously and therefore null or undefined won't throw this error. If you want to pass non-string values to `linky` (e.g. Objects whose `.toString()` should be utilized), you need to manually convert them to strings. angular.js-1.7.9/docs/content/error/ng/000077500000000000000000000000001356472325200177445ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/ng/aobj.ngdoc000066400000000000000000000002721356472325200216740ustar00rootroot00000000000000@ngdoc error @name ng:aobj @fullName Invalid Argument @description The argument passed should be an object. Check the value that was passed to the function where this error was thrown. angular.js-1.7.9/docs/content/error/ng/areq.ngdoc000066400000000000000000000007511356472325200217130ustar00rootroot00000000000000@ngdoc error @name ng:areq @fullName Bad Argument @description AngularJS often asserts that certain values will be present and truthy using a helper function. If the assertion fails, this error is thrown. To fix this problem, make sure that the value the assertion expects is defined and matches the type mentioned in the error. If the type is `undefined`, make sure any newly added controllers/directives/services are properly defined and included in the script(s) loaded by your page. angular.js-1.7.9/docs/content/error/ng/badname.ngdoc000066400000000000000000000005011356472325200223430ustar00rootroot00000000000000@ngdoc error @name ng:badname @fullName Bad `hasOwnProperty` Name @description Occurs when you try to use the name `hasOwnProperty` in a context where it is not allowed. Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object and allowing such a name would break lookups on this object. angular.js-1.7.9/docs/content/error/ng/btstrpd.ngdoc000066400000000000000000000016321356472325200224440ustar00rootroot00000000000000@ngdoc error @name ng:btstrpd @fullName App Already Bootstrapped with this Element @description Occurs when calling {@link angular.bootstrap} on an element that has already been bootstrapped. This usually happens when you accidentally use both `ng-app` and `angular.bootstrap` to bootstrap an application. ``` ... ``` Note that for bootstrapping purposes, the `` element is the same as `document`, so the following will also throw an error. ``` ... ``` You can also get this error if you accidentally load AngularJS itself more than once. ``` ... ... ``` angular.js-1.7.9/docs/content/error/ng/cpi.ngdoc000066400000000000000000000006421356472325200215350ustar00rootroot00000000000000@ngdoc error @name ng:cpi @fullName Bad Copy @description This error occurs when attempting to copy an object to itself. Calling {@link angular.copy angular.copy} with a `destination` object deletes all of the elements or properties on `destination` before copying to it. Copying an object to itself is not supported. Make sure to check your calls to `angular.copy` and avoid copying objects or arrays to themselves. angular.js-1.7.9/docs/content/error/ng/cpta.ngdoc000066400000000000000000000003051356472325200217050ustar00rootroot00000000000000@ngdoc error @name ng:cpta @fullName Copying TypedArray @description Copying TypedArray's with a destination is not supported because TypedArray objects can not be mutated, they are fixed length. angular.js-1.7.9/docs/content/error/ng/cpws.ngdoc000066400000000000000000000006471356472325200217430ustar00rootroot00000000000000@ngdoc error @name ng:cpws @fullName Copying Window or Scope @description Copying Window or Scope instances is not supported because of cyclical and self references. Avoid copying windows and scopes, as well as any other cyclical or self-referential structures. Note that trying to deep copy an object containing cyclical references that is neither a window nor a scope will cause infinite recursion and a stack overflow. angular.js-1.7.9/docs/content/error/ng/test.ngdoc000066400000000000000000000005561356472325200217450ustar00rootroot00000000000000@ngdoc error @name ng:test @fullName Testability Not Found @description AngularJS's testability helper, getTestability, requires a root element to be passed in. This helps differentiate between different AngularJS apps on the same page. This error is thrown when no injector is found for root element. It is often because the root element is outside of the ng-app. angular.js-1.7.9/docs/content/error/ngModel/000077500000000000000000000000001356472325200207255ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/ngModel/constexpr.ngdoc000066400000000000000000000011201356472325200237600ustar00rootroot00000000000000@ngdoc error @name ngModel:constexpr @fullName Non-Constant Expression @description Some attributes used in conjunction with ngModel (such as ngTrueValue or ngFalseValue) will only accept constant expressions. Examples using constant expressions include: ``` ``` Examples of non-constant expressions include: ``` ``` angular.js-1.7.9/docs/content/error/ngModel/datefmt.ngdoc000066400000000000000000000006631356472325200233720ustar00rootroot00000000000000@ngdoc error @name ngModel:datefmt @fullName Model is not a date object @description All date-related inputs like `` require the model to be a `Date` object. If the model is something else, this error will be thrown. AngularJS does not set validation errors on the `` in this case as those errors are shown to the user, but the erroneous state was caused by incorrect application logic and not by the user. angular.js-1.7.9/docs/content/error/ngModel/nonassign.ngdoc000066400000000000000000000013031356472325200237350ustar00rootroot00000000000000@ngdoc error @name ngModel:nonassign @fullName Non-Assignable Expression @description This error occurs when expression the {@link ng.directive:ngModel ngModel} directive is bound to is a non-assignable expression. Examples using assignable expressions include: ``` ``` Examples of non-assignable expressions include: ``` ``` Always make sure that the expression bound via `ngModel` directive can be assigned to. For more information, see the {@link ng.directive:ngModel ngModel API doc}. angular.js-1.7.9/docs/content/error/ngModel/nopromise.ngdoc000066400000000000000000000014611356472325200237560ustar00rootroot00000000000000@ngdoc error @name ngModel:nopromise @fullName No promise @description The return value of an async validator, must always be a promise. If you want to return a non-promise value, you can convert it to a promise using {@link ng.$q#resolve `$q.resolve()`} or {@link ng.$q#reject `$q.reject()`}. Example: ``` .directive('asyncValidator', function($q) { return { require: 'ngModel', link: function(scope, elem, attrs, ngModel) { ngModel.$asyncValidators.myAsyncValidation = function(modelValue, viewValue) { if (/* I don't need to hit the backend API */) { return $q.resolve(); // to mark as valid or // return $q.reject(); // to mark as invalid } else { // ...send a request to the backend and return a promise } }; } }; }) ``` angular.js-1.7.9/docs/content/error/ngModel/numfmt.ngdoc000066400000000000000000000031031356472325200232440ustar00rootroot00000000000000@ngdoc error @name ngModel:numfmt @fullName Model is not of type `number` @description The `input[number]` and `input[range]` directives require the model to be a `number`. If the model is something else, this error will be thrown. AngularJS does not set validation errors on the `` in this case as this error is caused by incorrect application logic and not by bad input from the user. If your model does not contain actual numbers then it is up to the application developer to use a directive that will do the conversion in the `ngModel` `$formatters` and `$parsers` pipeline. ## Example In this example, our model stores the number as a string, so we provide the `stringToNumber` directive to convert it into the format the `input[number]` directive expects.
{{ x }} : {{ typeOf(x) }}
angular.module('numfmt-error-module', []) .run(function($rootScope) { $rootScope.typeOf = function(value) { return typeof value; }; }) .directive('stringToNumber', function() { return { require: 'ngModel', link: function(scope, element, attrs, ngModel) { ngModel.$parsers.push(function(value) { return '' + value; }); ngModel.$formatters.push(function(value) { return parseFloat(value); }); } }; });
angular.js-1.7.9/docs/content/error/ngOptions/000077500000000000000000000000001356472325200213205ustar00rootroot00000000000000angular.js-1.7.9/docs/content/error/ngOptions/iexp.ngdoc000066400000000000000000000006141356472325200233020ustar00rootroot00000000000000@ngdoc error @name ngOptions:iexp @fullName Invalid Expression @description This error occurs when 'ngOptions' is passed an expression that isn't in an expected form. Here's an example of correct syntax: ```
[{{ item.id }}] {{ item.name }}
``` You could as well convert the object to an array using a filter such as [toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter): ```html
[{{ item.id }}] {{ item.name }}
``` angular.js-1.7.9/docs/content/guide/000077500000000000000000000000001356472325200173045ustar00rootroot00000000000000angular.js-1.7.9/docs/content/guide/$location.ngdoc000066400000000000000000000733331356472325200222050ustar00rootroot00000000000000@ngdoc overview @name Using $location @sortOrder 500 @description # Using the `$location` service The `$location` service parses the URL in the browser address bar (based on [`window.location`](https://developer.mozilla.org/en/window.location)) and makes the URL available to your application. Changes to the URL in the address bar are reflected into the `$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. - Maintains synchronization between itself and the browser's URL when the user - Changes the address in the browser's address bar. - Clicks the back or forward button in the browser (or clicks a History link). - Clicks on a link in the page. - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). ## Comparing `$location` to `window.location`
window.location $location service
purpose allow read/write access to the current browser location same
API exposes "raw" object with properties that can be directly modified exposes jQuery-style getters and setters
integration with AngularJS application life-cycle none knows about all internal life-cycle phases, integrates with {@link ng.$rootScope.Scope#$watch $watch}, ...
seamless integration with HTML5 API no yes (with a fallback for legacy browsers)
aware of docroot/context from which the application is loaded no - window.location.pathname returns "/docroot/actual/path" yes - $location.path() returns "/actual/path"
## 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? It 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 ng.$locationProvider $locationProvider} and set the parameters as follows: - **html5Mode(mode)**: `{boolean|Object}`
`false` or `{enabled: false}` (default) - see [Hashbang mode](guide/$location#hashbang-mode-default-mode-)
`true` or `{enabled: true}` - see [HTML5 mode](guide/$location#html5-mode)
`{..., requireBase: true/false}` (only affects HTML5 mode) - see [Relative links](guide/$location#relative-links)
`{..., rewriteLinks: true/false/'string'}` (only affects HTML5 mode) - see [HTML link rewriting](guide/$location#html-link-rewriting)
Default: ```j { enabled: false, requireBase: true, rewriteLinks: true } ``` - **hashPrefix(prefix)**: `{string}`
Prefix used for Hashbang URLs (used in Hashbang mode or in legacy browsers in HTML5 mode).
Default: `'!'` #### Example configuration ```js $locationProvider.html5Mode(true).hashPrefix('*'); ``` ### 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: ```js // get the current path $location.path(); // change the path $location.path('/newValue') ``` 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: ```js $location.path('/newValue').search({key: value}); ``` ### Replace method There is a special `replace` method which can be used to tell the $location service that the next time the $location service is synced with the browser, the last history record should be replaced instead of creating a new one. This is useful when you want to implement redirection, which would 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: ```js $location.path('/someNewPath'); $location.replace(); // or you can chain these as: $location.path('/someNewPath').replace(); ``` Note that the setters don't update `window.location` immediately. Instead, the `$location` service is aware of the {@link 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 an 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 [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). 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 History API](https://html.spec.whatwg.org/multipage/browsers.html#the-history-interface). 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.
Hashbang mode HTML5 mode
configuration the default { html5Mode: true }
URL format hashbang URLs in all browsers regular URLs in modern browser, hashbang URLs in old browser
<a href=""> link rewriting no yes
requires server-side configuration no yes
### Hashbang mode (default mode) In this mode, `$location` uses Hashbang URLs in all browsers. AngularJS also does not intercept and rewrite links in this mode. I.e. links work as expected and also perform full page reloads when other parts of the url than the hash fragment was changed. #### Example ```js it('should show example', function() { module(function($locationProvider) { $locationProvider.html5Mode(false); $locationProvider.hashPrefix('!'); }); inject(function($location) { // open http://example.com/base/index.html#!/a expect($location.absUrl()).toBe('http://example.com/base/index.html#!/a'); expect($location.path()).toBe('/a'); $location.path('/foo'); expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo'); expect($location.search()).toEqual({}); $location.search({a: 'b', c: true}); expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo?a=b&c'); $location.path('/new').search('x=y'); expect($location.absUrl()).toBe('http://example.com/base/index.html#!/new?x=y'); }); }); ``` ### HTML5 mode In HTML5 mode, the `$location` service getters and setters interact with the browser URL address through the HTML5 history API. This 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 Note that in this mode, AngularJS intercepts all links (subject to the "Html link rewriting" rules below) and updates the url in a way that never performs a full page reload. #### Example ```js it('should show example', function() { module(function($locationProvider) { $locationProvider.html5Mode(true); $locationProvider.hashPrefix('!'); }); inject(function($location) { // in browser with HTML5 history support: // open http://example.com/#!/a -> rewrite to http://example.com/a // (replacing the http://example.com/#!/a history record) expect($location.path()).toBe('/a'); $location.path('/foo'); expect($location.absUrl()).toBe('http://example.com/foo'); expect($location.search()).toEqual({}); $location.search({a: 'b', c: true}); expect($location.absUrl()).toBe('http://example.com/foo?a=b&c'); $location.path('/new').search('x=y'); expect($location.url()).toBe('/new?x=y'); expect($location.absUrl()).toBe('http://example.com/new?x=y'); }); }); it('should show example (when browser doesn\'t support HTML5 mode', function() { module(function($provide, $locationProvider) { $locationProvider.html5Mode(true); $locationProvider.hashPrefix('!'); $provide.value('$sniffer', {history: false}); }); inject(initBrowser({ url: 'http://example.com/new?x=y', basePath: '/' }), function($location) { // in browser without html5 history support: // open http://example.com/new?x=y -> redirect to http://example.com/#!/new?x=y // (again replacing the http://example.com/new?x=y history item) expect($location.path()).toBe('/new'); expect($location.search()).toEqual({x: 'y'}); $location.path('/foo/bar'); expect($location.path()).toBe('/foo/bar'); expect($location.url()).toBe('/foo/bar?x=y'); expect($location.absUrl()).toBe('http://example.com/#!/foo/bar?x=y'); }); }); ``` #### 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 Hashbang 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 HTML5 history API mode, you will not need special hashbang links. All you have to do is specify regular URL links, such as: `link` 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
Example: `link` - Absolute links that go to a different domain
Example: `link` - Links starting with '/' that lead to a different base path
Example: `link` If `mode.rewriteLinks` is set to `false` in the `mode` configuration object passed to `$locationProvider.html5Mode()`, the browser will perform a full page reload for every link. `mode.rewriteLinks` can also be set to a string, which will enable link rewriting only on anchor elements that have the given attribute. For example, if `mode.rewriteLinks` is set to `'internal-link'`: - `link` will be rewritten - `link` will perform a full page reload Note that [attribute name normalization](guide/directive#normalization) does not apply here, so `'internalLink'` will **not** match `'internal-link'`. #### Relative links Be sure to check all relative links, images, scripts etc. AngularJS requires you to specify the url base in the head of your main html file (``) unless `html5Mode.requireBase` is set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With that, relative urls will always be resolved to this base url, even if the initial url of the document was different. There is one exception: Links that only contain a hash fragment (e.g. ``) will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling to anchors on the same page without needing to know on which page the user currently is. #### 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). Requiring a `` tag is also important for this case, as it allows AngularJS to differentiate between the part of the url that is the application base and the path that should be handled by the application. #### Base href constraints The `$location` service is not able to function properly if the current URL is outside the URL given as the base href. This can have subtle confusing consequences... Consider a base href set as follows: `` (i.e. the application exists in the "folder" called `/base`). The URL `/base` is actually outside the application (it refers to the `base` file found in the root `/` folder). If you wish to be able to navigate to the application via a URL such as `/base` then you should ensure that your server is setup to redirect such requests to `/base/`. See https://github.com/angular/angular.js/issues/14018 for more information. ### 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 that show the difference between **Html5 mode** and **Html5 Fallback mode**. Note that to simulate different levels of browser support, the `$location` instances are connected to a fakeBrowser service, which you don't have to set up in actual projects. Note that when you type hashbang url into the 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 these examples we use ``. The inputs represent the address bar of the browser. ##### Browser in HTML5 mode angular.module('html5-mode', ['fake-browser', 'address-bar']) // Configure the fakeBrowser. Do not set these values in actual projects. .constant('initUrl', 'http://www.example.com/base/path?a=b#h') .constant('baseHref', '/base/index.html') .value('$sniffer', { history: true }) .controller('LocationController', function($scope, $location) { $scope.$location = {}; angular.forEach('protocol host port path search hash'.split(' '), function(method) { $scope.$location[method] = function() { var result = $location[method](); return angular.isObject(result) ? angular.toJson(result) : result; }; }); }) .config(function($locationProvider) { $locationProvider.html5Mode(true).hashPrefix('!'); }) .run(function($rootElement) { $rootElement.on('click', function(e) { e.stopPropagation(); }); }); angular.module('fake-browser', []) .config(function($provide) { $provide.decorator('$browser', function($delegate, baseHref, initUrl) { $delegate.onUrlChange = function(fn) { this.urlChange = fn; }; $delegate.url = function() { return initUrl; }; $delegate.defer = function(fn, delay) { setTimeout(function() { fn(); }, delay || 0); }; $delegate.baseHref = function() { return baseHref; }; return $delegate; }); }); angular.module('address-bar', []) .directive('ngAddressBar', function($browser, $timeout) { return { template: 'Address: ', link: function(scope, element, attrs) { var input = element.children('input'), delay; input.on('keypress keyup keydown', function(event) { delay = (!delay ? $timeout(fireUrlChange, 250) : null); event.stopPropagation(); }) .val($browser.url()); $browser.url = function(url) { return url ? input.val(url) : input.val(); }; function fireUrlChange() { delay = null; $browser.urlChange(input.val()); } } }; }); var addressBar = element(by.css("#addressBar")), url = 'http://www.example.com/base/path?a=b#h'; it("should show fake browser info on load", function() { expect(addressBar.getAttribute('value')).toBe(url); expect(element(by.binding('$location.protocol()')).getText()).toBe('http'); expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com'); expect(element(by.binding('$location.port()')).getText()).toBe('80'); expect(element(by.binding('$location.path()')).getText()).toBe('/path'); expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}'); expect(element(by.binding('$location.hash()')).getText()).toBe('h'); }); it("should change $location accordingly", function() { var navigation = element.all(by.css("#navigation a")); navigation.get(0).click(); expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/first?a=b"); expect(element(by.binding('$location.protocol()')).getText()).toBe('http'); expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com'); expect(element(by.binding('$location.port()')).getText()).toBe('80'); expect(element(by.binding('$location.path()')).getText()).toBe('/first'); expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}'); expect(element(by.binding('$location.hash()')).getText()).toBe(''); navigation.get(1).click(); expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/sec/ond?flag#hash"); expect(element(by.binding('$location.protocol()')).getText()).toBe('http'); expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com'); expect(element(by.binding('$location.port()')).getText()).toBe('80'); expect(element(by.binding('$location.path()')).getText()).toBe('/sec/ond'); expect(element(by.binding('$location.search()')).getText()).toBe('{"flag":true}'); expect(element(by.binding('$location.hash()')).getText()).toBe('hash'); }); ##### Browser in HTML5 Fallback mode (Hashbang mode)


$location.protocol() =
$location.host() =
$location.port() =
$location.path() =
$location.search() =
$location.hash() =
angular.module('hashbang-mode', ['fake-browser', 'address-bar']) // Configure the fakeBrowser. Do not set these values in actual projects. .constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h') .constant('baseHref', '/base/index.html') .value('$sniffer', { history: false }) .config(function($locationProvider) { $locationProvider.html5Mode(true).hashPrefix('!'); }) .controller('LocationController', function($scope, $location) { $scope.$location = {}; angular.forEach('protocol host port path search hash'.split(' '), function(method) { $scope.$location[method] = function() { var result = $location[method](); return angular.isObject(result) ? angular.toJson(result) : result; }; }); }) .run(function($rootElement) { $rootElement.on('click', function(e) { e.stopPropagation(); }); }); angular.module('fake-browser', []) .config(function($provide) { $provide.decorator('$browser', function($delegate, baseHref, initUrl) { $delegate.onUrlChange = function(fn) { this.urlChange = fn; }; $delegate.url = function() { return initUrl; }; $delegate.defer = function(fn, delay) { setTimeout(function() { fn(); }, delay || 0); }; $delegate.baseHref = function() { return baseHref; }; return $delegate; }); }); angular.module('address-bar', []) .directive('ngAddressBar', function($browser, $timeout) { return { template: 'Address: ', link: function(scope, element, attrs) { var input = element.children('input'), delay; input.on('keypress keyup keydown', function(event) { delay = (!delay ? $timeout(fireUrlChange, 250) : null); event.stopPropagation(); }) .val($browser.url()); $browser.url = function(url) { return url ? input.val(url) : input.val(); }; function fireUrlChange() { delay = null; $browser.urlChange(input.val()); } } }; }); var addressBar = element(by.css("#addressBar")), url = 'http://www.example.com/base/index.html#!/path?a=b#h'; it("should show fake browser info on load", function() { expect(addressBar.getAttribute('value')).toBe(url); expect(element(by.binding('$location.protocol()')).getText()).toBe('http'); expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com'); expect(element(by.binding('$location.port()')).getText()).toBe('80'); expect(element(by.binding('$location.path()')).getText()).toBe('/path'); expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}'); expect(element(by.binding('$location.hash()')).getText()).toBe('h'); }); it("should change $location accordingly", function() { var navigation = element.all(by.css("#navigation a")); navigation.get(0).click(); expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/first?a=b"); expect(element(by.binding('$location.protocol()')).getText()).toBe('http'); expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com'); expect(element(by.binding('$location.port()')).getText()).toBe('80'); expect(element(by.binding('$location.path()')).getText()).toBe('/first'); expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}'); expect(element(by.binding('$location.hash()')).getText()).toBe(''); navigation.get(1).click(); expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/sec/ond?flag#hash"); expect(element(by.binding('$location.protocol()')).getText()).toBe('http'); expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com'); expect(element(by.binding('$location.port()')).getText()).toBe('80'); expect(element(by.binding('$location.path()')).getText()).toBe('/sec/ond'); expect(element(by.binding('$location.search()')).getText()).toBe('{"flag":true}'); expect(element(by.binding('$location.hash()')).getText()).toBe('hash'); });
## 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 ng.$window $window.location.href}. ### Using $location outside of the scope life-cycle `$location` knows about AngularJS's {@link ng.$rootScope.Scope scope} life-cycle. When a URL changes in the browser it updates the `$location` and calls `$apply` so that all {@link ng.$rootScope.Scope#$watch $watchers} / {@link ng.$compile.directive.Attributes#$observe $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 {@link ng.$rootScope.Scope#$watch $watchers} / {@link ng.$compile.directive.Attributes#$observe $observers}. When you want to change the `$location` from outside AngularJS (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`. ### Crawling your app Most modern search engines are able to crawl AJAX applications with dynamic content, provided all included resources are available to the crawler bots. There also exists a special [AJAX crawling scheme](http://code.google.com/web/ajaxcrawling/docs/specification.html) developed by Google that allows bots to crawl the static equivalent of a dynamically generated page, but this schema has been deprecated, and support for it may vary by search engine. ## Testing with the $location service When using `$location` service during testing, you are outside of the angular's {@link ng.$rootScope.Scope scope} life-cycle. This means it's your responsibility to call `scope.$apply()`. ```js 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... })); }); ``` ## Two-way binding to $location Because `$location` uses getters/setters, you can use `ng-model-options="{ getterSetter: true }"` to bind it to `ngModel`:
angular.module('locationExample', []) .controller('LocationController', ['$scope', '$location', function($scope, $location) { $scope.locationPath = function(newLocation) { return $location.path(newLocation); }; }]);
## Related API * {@link ng.$location `$location` API} angular.js-1.7.9/docs/content/guide/accessibility.ngdoc000066400000000000000000000404521356472325200231540ustar00rootroot00000000000000@ngdoc overview @name Accessibility @sortOrder 530 @description # Accessibility with ngAria The goal of ngAria is to improve AngularJS's default accessibility by enabling common [ARIA](http://www.w3.org/TR/wai-aria/) attributes that convey state or semantic information for assistive technologies used by persons with disabilities. ## Including ngAria Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into standard AngularJS directives and quietly injects accessibility support into your application at runtime. ```js angular.module('myApp', ['ngAria'])... ``` ### Using ngAria Most of what ngAria does is only visible "under the hood". To see the module in action, once you've added it as a dependency, you can test a few things: * Using your favorite element inspector, look for attributes added by ngAria in your own code. * Test using your keyboard to ensure `tabindex` is used correctly. * Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support. [Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/) ## Supported directives Currently, ngAria interfaces with the following directives: * {@link guide/accessibility#ngmodel ngModel} * {@link guide/accessibility#ngdisabled ngDisabled} * {@link guide/accessibility#ngrequired ngRequired} * {@link guide/accessibility#ngreadonly ngReadonly} * {@link guide/accessibility#ngvaluechecked ngChecked} * {@link guide/accessibility#ngvaluechecked ngValue} * {@link guide/accessibility#ngshow ngShow} * {@link guide/accessibility#nghide ngHide} * {@link guide/accessibility#ngclick ngClick} * {@link guide/accessibility#ngdblclick ngDblClick} * {@link guide/accessibility#ngmessages ngMessages}

ngModel

Much of ngAria's heavy lifting happens in the {@link ng.ngModel ngModel} directive. For elements using ngModel, special attention is paid by ngAria if that element also has a role or type of `checkbox`, `radio`, `range` or `textbox`. For those elements using ngModel, ngAria will dynamically bind and update the following ARIA attributes (if they have not been explicitly specified by the developer): * aria-checked * aria-valuemin * aria-valuemax * aria-valuenow * aria-invalid * aria-required * aria-readonly * aria-disabled ### Example
Custom checkbox

Is checked: {{ !!checked }}
angular. module('ngAria_ngModelExample', ['ngAria']). directive('customCheckbox', customCheckboxDirective). directive('showAttrs', showAttrsDirective); function customCheckboxDirective() { return { restrict: 'E', require: 'ngModel', transclude: true, template: ' ' + '', link: function(scope, elem, attrs, ctrl) { // Overwrite necessary `NgModelController` methods ctrl.$isEmpty = isEmpty; ctrl.$render = render; // Bind to events elem.on('click', function(event) { event.preventDefault(); scope.$apply(toggleCheckbox); }); elem.on('keypress', function(event) { event.preventDefault(); if (event.keyCode === 32 || event.keyCode === 13) { scope.$apply(toggleCheckbox); } }); // Helpers function isEmpty(value) { return !value; } function render() { elem[ctrl.$viewValue ? 'addClass' : 'removeClass']('checked'); } function toggleCheckbox() { ctrl.$setViewValue(!ctrl.$viewValue); ctrl.$render(); } } }; } function showAttrsDirective($timeout) { return function(scope, elem, attrs) { var pre = document.createElement('pre'); elem.after(pre); scope.$watchCollection(function() { return Array.prototype.slice.call(elem[0].attributes).reduce(function(aggr, attr) { if (attr.name !== attrs.$attr.showAttrs) aggr[attr.name] = attr.value; return aggr; }, {}); }, function(newValues) { $timeout(function() { pre.textContent = angular.toJson(newValues, 2); }); }); }; } custom-checkbox { cursor: pointer; display: inline-block; } custom-checkbox .icon:before { content: '\2610'; display: inline-block; font-size: 2em; line-height: 1; speak: none; vertical-align: middle; } custom-checkbox.checked .icon:before { content: '\2611'; } var checkbox = element(by.css('custom-checkbox')); var checkedCheckbox = element(by.css('custom-checkbox.checked')); it('should have the `checked` class only when checked', function() { expect(checkbox.isPresent()).toBe(true); expect(checkedCheckbox.isPresent()).toBe(false); checkbox.click(); expect(checkedCheckbox.isPresent()).toBe(true); checkbox.click(); expect(checkedCheckbox.isPresent()).toBe(false); }); it('should have the `aria-checked` attribute set to the appropriate value', function() { expect(checkedCheckbox.isPresent()).toBe(false); expect(checkbox.getAttribute('aria-checked')).toBe('false'); checkbox.click(); expect(checkedCheckbox.isPresent()).toBe(true); expect(checkbox.getAttribute('aria-checked')).toBe('true'); checkbox.click(); expect(checkedCheckbox.isPresent()).toBe(false); expect(checkbox.getAttribute('aria-checked')).toBe('false'); });
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from the keyboard. It is still up to **you** as a developer to **ensure custom controls will be accessible**. As a rule, any time you create a widget involving user interaction, be sure to test it with your keyboard and at least one mobile and desktop screen reader.

ngValue and ngChecked

To ease the transition between native inputs and custom controls, ngAria now supports {@link ng.ngValue ngValue} and {@link ng.ngChecked ngChecked}. The original directives were created for native inputs only, so ngAria extends support to custom elements by managing `aria-checked` for accessibility. ### Example ```html ``` Becomes: ```html ```

ngDisabled

The `disabled` attribute is only valid for certain elements such as `button`, `input` and `textarea`. To properly disable custom element directives such as `` or ``, using ngAria with {@link ng.ngDisabled ngDisabled} will also add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping custom controls to be more accessible. ### Example ```html ``` Becomes: ```html ```
You can check whether a control is legitimately disabled for a screen reader by visiting [chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).

ngRequired

The boolean `required` attribute is only valid for native form controls such as `input` and `textarea`. To properly indicate custom element directives such as `` or `` as required, using ngAria with {@link ng.ngRequired ngRequired} will also add `aria-required`. This tells accessibility APIs when a custom control is required. ### Example ```html ``` Becomes: ```html ```

ngReadonly

The boolean `readonly` attribute is only valid for native form controls such as `input` and `textarea`. To properly indicate custom element directives such as `` or `` as required, using ngAria with {@link ng.ngReadonly ngReadonly} will also add `aria-readonly`. This tells accessibility APIs when a custom control is read-only. ### Example ```html ``` Becomes: ```html ```

ngShow

The {@link ng.ngShow ngShow} directive shows or hides the given HTML element based on the expression provided to the `ngShow` attribute. The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element. In its default setup, ngAria for `ngShow` is actually redundant. It toggles `aria-hidden` on the directive when it is hidden or shown. However, the default CSS of `display: none !important`, already hides child elements from a screen reader. It becomes more useful when the default CSS is overridden with properties that don’t affect assistive technologies, such as `opacity` or `transform`. By toggling `aria-hidden` dynamically with ngAria, we can ensure content visually hidden with this technique will not be read aloud in a screen reader. One caveat with this combination of CSS and `aria-hidden`: you must also remove links and other interactive child elements from the tab order using `tabIndex=“-1”` on each control. This ensures screen reader users won't accidentally focus on "mystery elements". Managing tab index on every child control can be complex and affect performance, so it’s best to just stick with the default `display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use). ### Example ```css .ng-hide { display: block; opacity: 0; } ``` ```html ``` Becomes: ```html
``` *Note: Child links, buttons or other interactive controls must also be removed from the tab order.*

ngHide

The {@link ng.ngHide ngHide} directive shows or hides the given HTML element based on the expression provided to the `ngHide` attribute. The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element. The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redundant. It toggles `aria-hidden` on the directive when it is hidden or shown, but the content is already hidden with `display: none`. See explanation for {@link guide/accessibility#ngshow ngShow} when overriding the default CSS.

ngClick and ngDblclick

If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in a node blacklist: * Button * Anchor * Input * Textarea * Select * Details/Summary To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will dynamically bind a keypress event by default as long as the element isn't in the node blacklist. You can turn this functionality on or off with the `bindKeypress` configuration option. ngAria will also add the `button` role to communicate to users of assistive technologies. This can be disabled with the `bindRoleForClick` configuration option. For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements such as `div` or `taco-button` to enable keyboard access.

Example

```html
``` Becomes: ```html
```

ngMessages

The ngMessages module makes it easy to display form validation or other messages with priority sequencing and animation. To expose these visual messages to screen readers, ngAria injects `aria-live="assertive"`, causing them to be read aloud any time a message is shown, regardless of the user's focus location. ### Example ```html
You did not enter a field
Your field is too long
``` Becomes: ```html
You did not enter a field
Your field is too long
``` ## Disabling attributes The attribute magic of ngAria may not work for every scenario. To disable individual attributes, you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will tell ngAria to ignore the attribute globally.
<div> with ng-click and bindRoleForClick, tabindex set to false
## Common Accessibility Patterns Accessibility best practices that apply to web apps in general also apply to AngularJS. * **Text alternatives**: Add alternate text content to make visual information accessible using [these W3C guidelines](http://www.w3.org/TR/html-alt-techniques/). The appropriate technique depends on the specific markup but can be accomplished using offscreen spans, `aria-label` or label elements, image `alt` attributes, `figure`/`figcaption` elements and more. * **HTML Semantics**: If you're creating custom element directives, Web Components or HTML in general, use native elements wherever possible to utilize built-in events and properties. Alternatively, use ARIA to communicate semantic meaning. See [notes on ARIA use](http://www.w3.org/TR/aria-in-html/#notes-on-aria-use-in-html). * **Focus management**: Guide the user around the app as views are appended/removed. Focus should *never* be lost, as this causes unexpected behavior and much confusion (referred to as "freak-out mode"). * **Announcing changes**: When filtering or other UI messaging happens away from the user's focus, notify with [ARIA Live Regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). * **Color contrast and scale**: Make sure content is legible and interactive controls are usable at all screen sizes. Consider configurable UI themes for people with color blindness, low vision or other visual impairments. * **Progressive enhancement**: Some users do not browse with JavaScript enabled or do not have the latest browser. An accessible message about site requirements can inform users and improve the experience. ## Additional Resources * [Using ARIA in HTML](http://www.w3.org/TR/aria-in-html/) * [AngularJS Accessibility at ngEurope](https://www.youtube.com/watch?v=dmYDggEgU-s&list=UUEGUP3TJJfMsEM_1y8iviSQ) * [Testing with Screen Readers](http://webaim.org/articles/screenreader_testing/) * [Chrome Accessibility Developer Tools](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en) * [W3C Accessibility Testing](http://www.w3.org/wiki/Accessibility_testing) * [WebAIM](http://webaim.org) * [A11y Project](http://a11yproject.com) angular.js-1.7.9/docs/content/guide/animations.ngdoc000066400000000000000000000512511356472325200224660ustar00rootroot00000000000000@ngdoc overview @name Animations @sortOrder 310 @description # Animations AngularJS provides animation hooks for common directives such as {@link ng.directive:ngRepeat ngRepeat}, {@link ng.directive:ngSwitch ngSwitch}, and {@link ngRoute.directive:ngView ngView}, as well as custom directives via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on whether an animation is placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS or with JavaScript code, defined as a factory.
Note that we have used non-prefixed CSS transition properties in our examples as the major browsers now support non-prefixed properties. If you intend to support older browsers or certain mobile browsers then you will need to include prefixed versions of the transition properties. Take a look at http://caniuse.com/#feat=css-transitions for what browsers require prefixes, and https://github.com/postcss/autoprefixer for a tool that can automatically generate the prefixes for you.
Animations are not available unless you include the {@link ngAnimate `ngAnimate` module} as a dependency of your application. Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
Content...
.content-area { border: 1px solid black; margin-top: 10px; padding: 10px; } .sample-show-hide { transition: all linear 0.5s; } .sample-show-hide.ng-hide { opacity: 0; }
## Installation See the {@link ngAnimate API docs for `ngAnimate`} for instructions on installing the module. You may also want to setup a separate CSS file for defining CSS-based animations. ## How they work Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to an HTML element within your application, you can apply animations to it. Let's say for example that we have an HTML template with a repeater like so: ```html
{{ item.id }}
``` As you can see, the `repeated-item` class is present on the element that will be repeated and this class will be used as a reference within our application's CSS and/or JavaScript animation code to tell AngularJS to perform an animation. As `ngRepeat` does its thing, each time a new item is added into the list, `ngRepeat` will add an `ng-enter` class to the element that is being added. When removed it will apply an `ng-leave` class and when moved around it will apply an `ng-move` class. Taking a look at the following CSS code, we can see some transition and keyframe animation code set up for each of those events that occur when `ngRepeat` triggers them: ```css /* We are using CSS transitions for when the enter and move events are triggered for the element that has the `repeated-item` class */ .repeated-item.ng-enter, .repeated-item.ng-move { transition: all 0.5s linear; opacity: 0; } /* `.ng-enter-active` and `.ng-move-active` are where the transition destination properties are set so that the animation knows what to animate */ .repeated-item.ng-enter.ng-enter-active, .repeated-item.ng-move.ng-move-active { opacity: 1; } /* We are using CSS keyframe animations for when the `leave` event is triggered for the element that has the `repeated-item` class */ .repeated-item.ng-leave { animation: 0.5s my_animation; } @keyframes my_animation { from { opacity: 1; } to { opacity: 0; } } ``` The same approach to animation can be used using JavaScript code (**for simplicity, we rely on jQuery to perform animations here**): ```js myModule.animation('.repeated-item', function() { return { enter: function(element, done) { // Initialize the element's opacity element.css('opacity', 0); // Animate the element's opacity // (`element.animate()` is provided by jQuery) element.animate({opacity: 1}, done); // Optional `onDone`/`onCancel` callback function // to handle any post-animation cleanup operations return function(isCancelled) { if (isCancelled) { // Abort the animation if cancelled // (`element.stop()` is provided by jQuery) element.stop(); } }; }, leave: function(element, done) { // Initialize the element's opacity element.css('opacity', 1); // Animate the element's opacity // (`element.animate()` is provided by jQuery) element.animate({opacity: 0}, done); // Optional `onDone`/`onCancel` callback function // to handle any post-animation cleanup operations return function(isCancelled) { if (isCancelled) { // Abort the animation if cancelled // (`element.stop()` is provided by jQuery) element.stop(); } }; }, // We can also capture the following animation events: move: function(element, done) {}, addClass: function(element, className, done) {}, removeClass: function(element, className, done) {} } }); ``` With these generated CSS class names present on the element at the time, AngularJS automatically figures out whether to perform a CSS and/or JavaScript animation. Note that you can't have both CSS and JavaScript animations based on the same CSS class. See {@link ngAnimate#css-js-animations-together here} for more details. ## Class and `ngClass` animation hooks AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks. This means that if a CSS class is added to or removed from an element then an animation can be executed in between, before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be able to capture class changes if an **interpolated expression** or the **ng-class** directive is used on the element.) The example below shows how to perform animations during class changes:


CSS-Animated Text

.css-class-add, .css-class-remove { transition: all 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940); } .css-class, .css-class-add.css-class-add-active { color: red; font-size: 3em; } .css-class-remove.css-class-remove-active { font-size: 1em; color: black; }
Although the CSS is a little different than what we saw before, the idea is the same. ## Which directives support animations? A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during their life cycle. The table below explains in detail which animation events are triggered: | Directive | Supported Animations | |-------------------------------------------------------------------------------|---------------------------------------------------------------------------| | {@link ng.directive:form#animations form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) | | {@link ngAnimate.directive:ngAnimateSwap#animations ngAnimateSwap} | enter and leave | | {@link ng.directive:ngClass#animations ngClass / {{class}​}} | add and remove | | {@link ng.directive:ngClassEven#animations ngClassEven} | add and remove | | {@link ng.directive:ngClassOdd#animations ngClassOdd} | add and remove | | {@link ng.directive:ngHide#animations ngHide} | add and remove (the `ng-hide` class) | | {@link ng.directive:ngIf#animations ngIf} | enter and leave | | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | | {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave | | {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) | | {@link ng.directive:ngModel#animations ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) | | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move | | {@link ng.directive:ngShow#animations ngShow} | add and remove (the `ng-hide` class) | | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | | {@link ngRoute.directive:ngView#animations ngView} | enter and leave | (More information can be found by visiting the documentation associated with each directive.) For a full breakdown of the steps involved during each animation event, refer to the {@link ng.$animate `$animate` API docs}. ## How do I use animations in my own directives? Animations within custom directives can also be established by injecting `$animate` directly into your directive and making calls to it when needed. ```js myModule.directive('my-directive', ['$animate', function($animate) { return function(scope, element) { element.on('click', function() { if (element.hasClass('clicked')) { $animate.removeClass(element, 'clicked'); } else { $animate.addClass(element, 'clicked'); } }); }; }]); ``` ## Animations on app bootstrap / page load By default, animations are disabled when the AngularJS app {@link guide/bootstrap bootstraps}. If you are using the {@link ngApp} directive, this happens in the `DOMContentLoaded` event, so immediately after the page has been loaded. Animations are disabled, so that UI and content are instantly visible. Otherwise, with many animations on the page, the loading process may become too visually overwhelming, and the performance may suffer. Internally, `ngAnimate` waits until all template downloads that are started right after bootstrap have finished. Then, it waits for the currently running {@link ng.$rootScope.Scope#$digest $digest} and one more after that, to finish. This ensures that the whole app has been compiled fully before animations are attempted. If you do want your animations to play when the app bootstraps, you can enable animations globally in your main module's {@link angular.Module#run run} function: ```js myModule.run(function($animate) { $animate.enabled(true); }); ``` ## How to (selectively) enable, disable and skip animations There are several different ways to disable animations, both globally and for specific animations. Disabling specific animations can help to speed up the render performance, for example for large `ngRepeat` lists that don't actually have animations. Because `ngAnimate` checks at runtime if animations are present, performance will take a hit even if an element has no animation. ### During the config: {@link $animateProvider#customFilter $animateProvider.customFilter()} This function can be called during the {@link angular.Module#config config} phase of an app. It takes a filter function as the only argument, which will then be used to "filter" animations (based on the animated element, the event type, and the animation options). Only when the filter function returns `true`, will the animation be performed. This allows great flexibility - you can easily create complex rules, such as allowing specific events only or enabling animations on specific subtrees of the DOM, and dynamically modify them, for example disabling animations at certain points in time or under certain circumstances. ```js app.config(function($animateProvider) { $animateProvider.customFilter(function(node, event, options) { // Example: Only animate `enter` and `leave` operations. return event === 'enter' || event === 'leave'; }); }); ``` The `customFilter` approach generally gives a big speed boost compared to other strategies, because the matching is done before other animation disabling strategies are checked.
**Best Practice:** Keep the filtering function as lean as possible, because it will be called for each DOM action (e.g. insertion, removal, class change) performed by "animation-aware" directives. See {@link guide/animations#which-directives-support-animations- here} for a list of built-in directives that support animations. Performing computationally expensive or time-consuming operations on each call of the filtering function can make your animations sluggish.
### During the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()} This function too can be called during the {@link angular.Module#config config} phase of an app. It takes a regex as the only argument, which will then be matched against the classes of any element that is about to be animated. The regex allows a lot of flexibility - you can either allow animations for specific classes only (useful when you are working with 3rd party animations), or exclude specific classes from getting animated. ```js app.config(function($animateProvider) { $animateProvider.classNameFilter(/animate-/); }); ``` ```css /* prefixed with `animate-` */ .animate-fade-add.animate-fade-add-active { transition: all 1s linear; opacity: 0; } ``` The `classNameFilter` approach generally gives a big speed boost compared to other strategies, because the matching is done before other animation disabling strategies are checked. However, that also means it is not possible to override class name matching with the two following strategies. It's of course still possible to enable / disable animations by changing an element's class name at runtime. ### At runtime: {@link ng.$animate#enabled $animate.enabled()} This function can be used to enable / disable animations in two different ways: With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)` disables all animations in your app. When the first argument is a native DOM or jqLite/jQuery element, the function enables / disables animations on this element *and all its children*: `$animate.enabled(myElement, false)`. You can still use it to re-enable animations for a child element, even if you have disabled them on a parent element. And compared to the `classNameFilter`, you can change the animation status at runtime instead of during the config phase. Note however that the `$animate.enabled()` state for individual elements does not overwrite disabling rules that have been set in the {@link $animateProvider#classNameFilter classNameFilter}. ### Via CSS styles: overwriting styles in the `ng-animate` CSS class Whenever an animation is started, `ngAnimate` applies the `ng-animate` class to the element for the whole duration of the animation. By applying CSS transition / animation styling to that class, you can skip an animation: ```css .my-class { transition: transform 2s; } .my-class:hover { transform: translateX(50px); } my-class.ng-animate { transition: 0s; } ``` By setting `transition: 0s`, `ngAnimate` will ignore the existing transition styles, and not try to animate them (Javascript animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries issues with existing animations interfering with `ngAnimate`}. ## Preventing flicker before an animation starts When nesting elements with structural animations, such as `ngIf`, into elements that have class-based animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content where the animated element is briefly visible. To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized, but removed before the actual animation starts (after waiting for a `$digest`). This class is only added for *structural* animations (`enter`, `move`, and `leave`). Here's an example where you might see flickering: ```html
``` It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating. In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts: ```css .message.ng-enter-prepare { opacity: 0; } /* Other animation styles ... */ ``` ## Preventing collisions with existing animations and third-party libraries By default, any `ngAnimate`-enabled directives will assume that `transition` / `animation` styles on the element are part of an `ngAnimate` animation. This can lead to problems when the styles are actually for animations that are independent of `ngAnimate`. For example, an element acts as a loading spinner. It has an infinite css animation on it, and also an {@link ngIf `ngIf`} directive, for which no animations are defined: ```css .spinner { animation: rotating 2s linear infinite; } @keyframes rotating { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } ``` Now, when the `ngIf` expression changes, `ngAnimate` will see the spinner animation and use it to animate the `enter`/`leave` event, which doesn't work because the animation is infinite. The element will still be added / removed after a timeout, but there will be a noticeable delay. This might also happen because some third-party frameworks place animation duration defaults across many element or className selectors in order to make their code small and reusable. You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class, that is added for the whole duration of each animation. Simply overwrite the transition / animation duration. In the case of the spinner, this would be: ```css .spinner.ng-animate { animation: 0s none; transition: 0s none; } ``` If you do have CSS transitions / animations defined for the animation events, make sure they have a higher priority than any styles that are not related to `ngAnimate`. You can also use one of the other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}. ## Enable animations outside of the application DOM tree: {@link ng.$animate#pin $animate.pin()} Before animating, `ngAnimate` checks if the animated element is inside the application DOM tree. If not, no animation is run. Usually, this is not a problem since most apps use the `html` or `body` elements as their root. Problems arise when the application is bootstrapped on a different element, and animations are attempted on elements that are outside the application tree, e.g. when libraries append popup or modal elements to the body tag. You can use {@link ng.$animate#pin `$animate.pin(element, parentHost)`} to associate an element with another element that belongs to your application. Simply call it before the element is added to the DOM / before the animation starts, with the element you want to animate, and the element which should be its assumed parent. ## More about animations For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}. To see a complete demo, see the {@link tutorial/step_14 animation step in the phonecat tutorial}. angular.js-1.7.9/docs/content/guide/bootstrap.ngdoc000066400000000000000000000141511356472325200223370ustar00rootroot00000000000000@ngdoc overview @name Bootstrap @sortOrder 350 @description # Bootstrap This page explains the AngularJS initialization process and how you can manually initialize AngularJS if necessary. ## AngularJS ` ``` 1. 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 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. 2. Place `ng-app` to the root of your application, typically on the `` tag if you want AngularJS to auto-bootstrap your application. 3. If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html` when running the page in the XHTML mode. (This is here for historical reasons, and we no longer recommend use of `ng:`.) ## Automatic Initialization AngularJS initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is evaluated if at that time `document.readyState` is set to `'complete'`. At this point AngularJS looks for the {@link ng.directive:ngApp `ngApp`} directive which designates your application root. If the {@link ng.directive:ngApp `ngApp`} directive is found then AngularJS will: * load the {@link guide/module module} associated with the directive. * create the application {@link auto.$injector injector} * compile the DOM treating the {@link ng.directive:ngApp `ngApp`} directive as the root of the compilation. This allows you to tell it to treat only a portion of the DOM as an AngularJS application. ```html I can add: {{ 1+2 }}. ``` As a best practice, consider adding an `ng-strict-di` directive on the same element as `ng-app`: ```html I can add: {{ 1+2 }}. ``` This will ensure that all services in your application are properly annotated. See the {@link guide/di#using-strict-dependency-injection dependency injection strict mode} docs for more. ## 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 AngularJS compiles a page. Here is an example of manually initializing AngularJS: ```html
Hello {{greetMe}}!
``` Note that we provided the name of our application module to be loaded into the injector as the second parameter of the {@link angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter. You should call `angular.bootstrap()` *after* you've loaded or defined your modules. You cannot add controllers, services, directives, etc after an application bootstraps.
**Note:** You should not use the ng-app directive when manually bootstrapping your app.
This is the sequence that your code should follow: 1. After the page and all of the code is loaded, find the root element of your AngularJS application, which is typically the root of the document. 2. Call {@link angular.bootstrap} to {@link compiler compile} the element into an executable, bi-directionally bound application. ## Things to keep in mind There are a few things to keep in mind regardless of automatic or manual bootstrapping: - While it's possible to bootstrap more than one AngularJS application per page, we don't actively test against this scenario. It's possible that you'll run into problems, especially with complex apps, so caution is advised. - Do not bootstrap your app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}. Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, causing animations to stop working and making the injector inaccessible from outside the app. ## Deferred Bootstrap This feature enables tools like [Batarang](https://github.com/angular/angularjs-batarang) and test runners to hook into angular's bootstrap process and sneak in more modules into the DI registry which can replace or augment DI services for the purpose of instrumentation or mocking out heavy dependencies. If `window.name` contains prefix `NG_DEFER_BOOTSTRAP!` when {@link angular.bootstrap} is called, the bootstrap process will be paused until `angular.resumeBootstrap()` is called. `angular.resumeBootstrap()` takes an optional array of modules that should be added to the original list of modules that the app was about to be bootstrapped with. angular.js-1.7.9/docs/content/guide/compiler.ngdoc000066400000000000000000000466101356472325200221410ustar00rootroot00000000000000@ngdoc overview @name HTML Compiler @sortOrder 330 @description # HTML Compiler
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics. If you're just getting started, we recommend the {@link tutorial/ tutorial} first. If you just want to create custom directives, we recommend the {@link guide/directive directives guide}. If you want a deeper look into AngularJS's compilation process, you're in the right place.
## Overview AngularJS's {@link ng.$compile HTML compiler} allows the developer to teach the browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute and even create new HTML elements or attributes with custom behavior. AngularJS calls these behavior extensions {@link ng.$compileProvider#directive directives}. HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion. For example if something needs to be centered, there is no need to provide instructions to the browser how the window size needs to be divided in half so that the center is found, and that this center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any element to achieve the desired behavior. Such is the power of declarative language. However, the declarative language is also limited, as 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 the browser new HTML syntax. AngularJS 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 extensions 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 AngularJS service which traverses the DOM looking for attributes. The compilation process happens in two phases. 1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking 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. This makes the scope model the single source of truth. Some directives such as {@link ng.directive:ngRepeat `ng-repeat`} clone DOM elements once for each item in a 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 A directive is a behavior which should be triggered when specific HTML constructs are encountered during the compilation process. The directives can be placed in element names, attributes, class names, as well as comments. Here are some equivalent examples of invoking the {@link ng.directive:ngBind `ng-bind`} directive. ```html ``` A directive is just a function which executes when the compiler encounters it in the DOM. See {@link 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 `` element. angular.module('drag', []). directive('draggable', function($document) { return function(scope, element, attr) { var startX = 0, startY = 0, x = 0, y = 0; element.css({ position: 'relative', border: '1px solid red', backgroundColor: 'lightgrey', cursor: 'pointer', display: 'block', width: '65px' }); element.on('mousedown', function(event) { // Prevent default dragging of selected content event.preventDefault(); startX = event.screenX - x; startY = event.screenY - y; $document.on('mousemove', mousemove); $document.on('mouseup', mouseup); }); function mousemove(event) { y = event.screenY - startY; x = event.screenX - startX; element.css({ top: y + 'px', left: x + 'px' }); } function mouseup() { $document.off('mousemove', mousemove); $document.off('mouseup', mouseup); } }; }); Drag ME The presence of the `draggable` attribute on any element gives the element new behavior. We extended the vocabulary of the browser in a way which is natural to anyone who is familiar with the principles of HTML. ## Understanding View Most other templating systems consume a static string template and combine it with data, resulting in a new string. The resulting text is then `innerHTML`ed into an element. This means that any changes to the data need to be re-merged with the template and then `innerHTML`ed into the DOM. Some of the issues with this approach are: 1. reading user input and merging it with data 2. clobbering user input by overwriting it 3. managing the whole update process 4. lack of behavior expressiveness AngularJS is different. The AngularJS compiler consumes the DOM, not string templates. The result is a linking function, which when combined with a scope model results in a live view. The view and scope model bindings are transparent. The developer does not need to make any special calls to update the view. And because `innerHTML` is not used, you won't accidentally clobber user input. Furthermore, AngularJS directives can contain not just text bindings, but behavioral constructs as well. The AngularJS approach produces a stable DOM. The DOM element instance bound to a model item instance does not change for the lifetime of the binding. This means that the code can get hold of the elements and register event handlers and know that the reference will not be destroyed by template data merge. ## How directives are compiled It's important to note that AngularJS operates on DOM nodes rather than strings. Usually, you don't notice this restriction because when a page loads, the web browser parses HTML into the DOM automatically. HTML compilation happens in three phases: 1. {@link ng.$compile `$compile`} traverses the DOM and matches directives. If the compiler finds that an element matches a directive, then the directive is added to the list of directives that match the DOM element. A single element may match multiple directives. 2. Once all directives matching a DOM element have been identified, the compiler sorts the directives by their `priority`. Each directive's `compile` functions are executed. Each `compile` function has a chance to modify the DOM. Each `compile` function returns a `link` function. These functions are composed into a "combined" link function, which invokes each directive's returned `link` function. 3. `$compile` links the template with the scope by calling the combined linking function from the previous step. This in turn will call the linking function of the individual directives, registering listeners on the elements and setting up {@link ng.$rootScope.Scope#$watch `$watch`s} with the {@link ng.$rootScope.Scope `scope`} as each directive is configured to do. The result of this is a live binding between the scope and the DOM. So at this point, a change in a model on the compiled scope will be reflected in the DOM. Below is the corresponding code using the `$compile` service. This should help give you an idea of what AngularJS does internally. ```js var $compile = ...; // injected into your code var scope = ...; var parent = ...; // DOM element where the compiled template can be appended var html = '
'; // Step 1: parse HTML into DOM element var template = angular.element(html); // Step 2: compile the template var linkFn = $compile(template); // Step 3: link the compiled template with the scope. var element = linkFn(scope); // Step 4: Append to DOM (optional) parent.appendChild(element); ``` ### The difference between Compile and Link At this point you may wonder why the compile process has separate compile and link phases. The short answer is that compile and link separation is needed any time a change in a model causes a change in the **structure** of the DOM. It's rare for directives to have a **compile function**, since most directives are concerned with working with a specific DOM element instance rather than changing its overall structure. Directives often have a **link function**. A link function allows the directive to register listeners to the specific cloned DOM element instance as well as to copy content into the DOM from the scope.
**Best Practice:** Any operation which can be shared among the instance of directives should be moved to the compile function for performance reasons.
#### An Example of "Compile" Versus "Link" To understand, let's look at a real-world example with `ngRepeat`: ```html Hello {{user.name}}, you have these actions:
  • {{action.description}}
``` When the above example is compiled, the compiler visits every node and looks for directives. `{{user.name}}` matches the {@link ng.$interpolate interpolation directive} and `ng-repeat` matches the {@link ng.directive:ngRepeat `ngRepeat` directive}. But {@link ng.directive:ngRepeat ngRepeat} has a dilemma. It needs to be able to clone new `
  • ` elements for every `action` in `user.actions`. This initially seems trivial, but it becomes more complicated when you consider that `user.actions` might have items added to it later. This means that it needs to save a clean copy of the `
  • ` element for cloning purposes. As new `action`s are inserted, the template `
  • ` element needs to be cloned and inserted into `ul`. But cloning the `
  • ` element is not enough. It also needs to compile the `
  • ` so that its directives, like `{{action.description}}`, evaluate against the right {@link ng.$rootScope.Scope scope}. A naive approach to solving this problem would be to simply insert a copy of the `
  • ` element and then compile it. The problem with this approach is that compiling on every `
  • ` element that we clone would duplicate a lot of the work. Specifically, we'd be traversing `
  • ` each time before cloning it to find the directives. This would cause the compilation process to be slower, in turn making applications less responsive when inserting new nodes. The solution is to break the compilation process into two phases: the **compile phase** where all of the directives are identified and sorted by priority, and a **linking phase** where any work which "links" a specific instance of the {@link ng.$rootScope.Scope scope} and the specific instance of an `
  • ` is performed.
    **Note:** *Link* means setting up listeners on the DOM and setting up `$watch` on the Scope to keep the two in sync.
    {@link ng.directive:ngRepeat `ngRepeat`} works by preventing the compilation process from descending into the `
  • ` element so it can make a clone of the original and handle inserting and removing DOM nodes itself. Instead the {@link ng.directive:ngRepeat `ngRepeat`} directive compiles `
  • ` separately. The result of the `
  • ` element compilation is a linking function which contains all of the directives contained in the `
  • ` element, ready to be attached to a specific clone of the `
  • ` element. At runtime the {@link ng.directive:ngRepeat `ngRepeat`} watches the expression and as items are added to the array it clones the `
  • ` element, creates a new {@link ng.$rootScope.Scope scope} for the cloned `
  • ` element and calls the link function on the cloned `
  • `. ### Understanding How Scopes Work with Transcluded Directives One of the most common use cases for directives is to create reusable components. Below is a pseudo code showing how a simplified dialog component may work. ```html
    Body goes here: {{username}} is {{title}}.
    ``` 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. ```html

    {{title}}

    ``` This will not render properly, unless we do some scope magic. The first issue we have to solve is that the dialog box template expects `title` to be defined. But we would like the template's scope property `title` to be the result of interpolating the `` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the widget. To solve the mapping issue we use the `scope` to create local variables which the template expects as follows: ```js scope: { title: '@', // the title uses the data-binding from the parent scope onOk: '&', // create a delegate onOk function onCancel: '&', // create a delegate onCancel function visible: '=' // set up visible to accept data-binding } ``` Creating local properties on widget scope creates two problems: 1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog template will bind to parent scope property. This is unpredictable and undesirable. 2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the properties which the transclusion needs for data-binding. In our example the `title` property of the widget clobbers the `title` property of the transclusion. To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An isolated scope does not prototypically inherit from the parent scope, and therefore we don't have to worry about accidentally clobbering any properties. However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget isolated scope then it will not be able to bind to anything. For this reason the transcluded scope is a child of the original scope, before the widget created an isolated scope for its local variables. This makes the transcluded and widget isolated scope siblings. This may seem to be unexpected complexity, but it gives the widget user and developer the least surprise. Therefore the final directive definition looks something like this: ```js transclude: true, scope: { title: '@', // the title uses the data-binding from the parent scope onOk: '&', // create a delegate onOk function onCancel: '&', // create a delegate onCancel function visible: '=' // set up visible to accept data-binding }, restrict: 'E', replace: true ``` ### Double Compilation, and how to avoid it Double compilation occurs when an already compiled part of the DOM gets compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues, and memory leaks. A common scenario where this happens is a directive that calls `$compile` in a directive link function on the directive element. In the following **faulty example**, a directive adds a mouseover behavior to a button with `ngClick` on it: ``` angular.module('app').directive('addMouseover', function($compile) { return { link: function(scope, element, attrs) { var newEl = angular.element(' My Hint'); element.on('mouseenter mouseleave', function() { scope.$apply('showHint = !showHint'); }); attrs.$set('addMouseover', null); // To stop infinite compile loop element.append(newEl); $compile(element)(scope); // Double compilation } } }) ``` At first glance, it looks like removing the original `addMouseover` attribute is all there is needed to make this example work. However, if the directive element or its children have other directives attached, they will be compiled and linked again, because the compiler doesn't keep track of which directives have been assigned to which elements. This can cause unpredictable behavior, e.g. `ngClick` or other event handlers will be attached again. It can also degrade performance, as watchers for text interpolation are added twice to the scope. Double compilation should therefore be avoided. In the above example, only the new element should be compiled: ``` angular.module('app').directive('addMouseover', function($compile) { return { link: function(scope, element, attrs) { var newEl = angular.element(' My Hint'); element.on('mouseenter mouseleave', function() { scope.$apply('showHint = !showHint'); }); element.append(newEl); $compile(newEl)(scope); // Only compile the new element } } }) ``` Another scenario is adding a directive programmatically to a compiled element and then executing compile again. See the following **faulty example**: ```html ``` ``` angular.module('app').directive('addOptions', function($compile) { return { link: function(scope, element, attrs) { attrs.$set('addOptions', null) // To stop infinite compile loop attrs.$set('ngModelOptions', '{debounce: 1000}'); $compile(element)(scope); // Double compilation } } }); ``` In that case, it is necessary to intercept the *initial* compilation of the element: 1. Give your directive the `terminal` property and a higher priority than directives that should not be compiled twice. In the example, the compiler will only compile directives which have a priority of 100 or higher. 2. Inside this directive's compile function, add any other directive attributes to the template. 3. Compile the element, but restrict the maximum priority, so that any already compiled directives (including the `addOptions` directive) are not compiled again. 4. In the link function, link the compiled element with the element's scope. ``` angular.module('app').directive('addOptions', function($compile) { return { priority: 100, // ngModel has priority 1 terminal: true, compile: function(templateElement, templateAttributes) { templateAttributes.$set('ngModelOptions', '{debounce: 1000}'); // The third argument is the max priority. Only directives with priority < 100 will be compiled, // therefore we don't need to remove the attribute var compiled = $compile(templateElement, null, 100); return function linkFn(scope) { compiled(scope) // Link compiled element to scope } } } }); ``` angular.js-1.7.9/docs/content/guide/component-router.ngdoc000066400000000000000000001115271356472325200236470ustar00rootroot00000000000000@ngdoc overview @name Component Router @sortOrder 306 @description # Component Router
    **Deprecation Notice:** In an effort to keep synchronized with router changes in the new Angular, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates. We are investigating backporting the new Angular Router to AngularJS, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
    This guide describes the Component Router for AngularJS.
    If you are looking for information about the default router for AngularJS have a look at the {@link ngRoute} module. If you are looking for information about the Component Router for the new Angular then check out the [Angular Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
    ## Overview Here is a table of the main concepts used in the Component Router. | Concept | Description | | ----------------------|-------------------------------------------------------------------------------------- | | Router | Displays the Routing Components for the active Route. Manages navigation from one component to the next. | | RootRouter | The top level Router that interacts with the current URL location | | RouteConfig | Configures a Router with RouteDefinitions, each mapping a URL path to a component. | | Routing Component | An AngularJS component with a RouteConfig and an associated Router. | | RouteDefinition | Defines how the router should navigate to a component based on a URL pattern. | | ngOutlet | The directive (``) that marks where the router should display a view. | | ngLink | The directive (`ng-link="..."`) for binding a clickable HTML element to a route, via a Link Parameters Array. | | Link Parameters Array | An array that the router interprets into a routing instruction. We can bind a RouterLink to that array or pass the array as an argument to the Router.navigate method. | ## Component-based Applications It is recommended to develop AngularJS applications as a hierarchy of Components. Each Component is an isolated part of the application, which is responsible for its own user interface and has a well defined programmatic interface to the Component that contains it. Take a look at the {@link guide/component component guide} for more information. ![Component Based Architecture](img/guide/component-based-architecture.svg) ## URLs and Navigation In most applications, users navigate from one view to the next as they perform application tasks. The browser provides a familiar model of application navigation. We enter a URL in the address bar or click on a link and the browser navigates to a new page. We click the browser's back and forward buttons and the browser navigates backward and forward through the history of pages we've seen. We understand that each view corresponds to a particular URL. In a Component-based application, each of these views is implemented by one or more Components. ## Component Routes **How do we choose which Components to display given a particular URL?** When using the Component Router, each **Component** in the application can have a **Router** associated with it. This **Router** contains a mapping of URL segments to child **Components**. ```js $routeConfig: [ { path: '/a/b/c', component: 'someComponent' }, ... ] ``` This means that for a given URL the **Router** will render an associated child **Component**. ## Outlets **How do we know where to render a child Component?** Each **Routing Component**, needs to have a template that contains one or more **Outlets**, which is where its child **Components** are rendered. We specify the **Outlet** in the template using the {@link ngOutlet ``} directive. ```html ``` *In the future `ng-outlet` will be able to render different child **Components** for a given **Route** by specifying a `name` attribute.* ## Root Router and Component **How does the Component Router know which Component to render first?** All Component Router applications must contain a top level **Routing Component**, which is associated with a top level **Root Router**. The **Root Router** is the starting point for all navigation. You can access this **Router** by injecting the `$rootRouter` service. We define the top level **Root Component** by providing a value for the {@link $routerRootComponent} service. ```js myModule.value('$routerRootComponent', 'myApp'); ``` Here we have specified that the **Root Component** is the component directive with the name `myApp`. Remember to instantiate this **Root Component** in our `index.html` file. ```html ``` ## Route Matching When we navigate to any given URL, the {@link $rootRouter} matches its **Route Config** against the URL. If a **Route Definition** in the **Route Config** recognizes a part of the URL then the **Component** associated with the **Route Definition** is instantiated and rendered in the **Outlet**. If the new **Component** contains routes of its own then a new **Router ({@link ChildRouter})** is created for this **Routing Component**. The {@link ChildRouter} for the new **Routing Component** then attempts to match its **Route Config** against the parts of the URL that have not already been matched by the previous **Router**. This process continues until we run out of **Routing Components** or consume the entire URL. ![Routed Components](img/guide/component-routes.svg) In the previous diagram, we can see that the URL `/heros/4` has been matched against the `App`, `Heroes` and `HeroDetail` **Routing Components**. The **Routers** for each of the **Routing Components** consumed a part of the URL: "/", "/heroes" and "/4" respectively. The result is that we end up with a hierarchy of **Routing Components** rendered in **Outlets**, via the {@link ngOutlet} directive, in each **Routing Component's** template, as you can see in the following diagram. ![Component Hierarchy](img/guide/component-hierarchy.svg) ## Example Heroes App You can see the complete application running below.

    Component Router

    angular.module('app', ['ngComponentRouter', 'heroes', 'crisis-center']) .config(function($locationProvider) { $locationProvider.html5Mode(true); }) .value('$routerRootComponent', 'app') .component('app', { template: '\n' + '\n', $routeConfig: [ {path: '/crisis-center/...', name: 'CrisisCenter', component: 'crisisCenter', useAsDefault: true}, {path: '/heroes/...', name: 'Heroes', component: 'heroes' } ] }); angular.module('heroes', []) .service('heroService', HeroService) .component('heroes', { template: '

    Heroes

    ', $routeConfig: [ {path: '/', name: 'HeroList', component: 'heroList', useAsDefault: true}, {path: '/:id', name: 'HeroDetail', component: 'heroDetail'} ] }) .component('heroList', { template: '
    \n' + '{{hero.name}}\n' + '
    ', controller: HeroListComponent }) .component('heroDetail', { template: '
    \n' + '

    "{{$ctrl.hero.name}}"

    \n' + '
    \n' + ' {{$ctrl.hero.id}}
    \n' + '
    \n' + ' \n' + ' \n' + '
    \n' + ' \n' + '
    \n', bindings: { $router: '<' }, controller: HeroDetailComponent }); function HeroService($q) { var heroesPromise = $q.resolve([ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' } ]); this.getHeroes = function() { return heroesPromise; }; this.getHero = function(id) { return heroesPromise.then(function(heroes) { for (var i = 0; i < heroes.length; i++) { if (heroes[i].id === id) return heroes[i]; } }); }; } function HeroListComponent(heroService) { var selectedId = null; var $ctrl = this; this.$routerOnActivate = function(next) { // Load up the heroes for this view heroService.getHeroes().then(function(heroes) { $ctrl.heroes = heroes; selectedId = next.params.id; }); }; this.isSelected = function(hero) { return (hero.id === selectedId); }; } function HeroDetailComponent(heroService) { var $ctrl = this; this.$routerOnActivate = function(next) { // Get the hero identified by the route parameter var id = next.params.id; heroService.getHero(id).then(function(hero) { $ctrl.hero = hero; }); }; this.gotoHeroes = function() { var heroId = this.hero && this.hero.id; this.$router.navigate(['HeroList', {id: heroId}]); }; }
    angular.module('crisis-center', ['dialog']) .service('crisisService', CrisisService) .component('crisisCenter', { template: '

    Crisis Center

    ', $routeConfig: [ {path:'/', name: 'CrisisList', component: 'crisisList', useAsDefault: true}, {path:'/:id', name: 'CrisisDetail', component: 'crisisDetail'} ] }) .component('crisisList', { template: '
      \n' + '
    • \n' + ' {{crisis.id}} {{crisis.name}}\n' + '
    • \n' + '
    \n', bindings: { $router: '<' }, controller: CrisisListComponent, $canActivate: function($nextInstruction, $prevInstruction) { console.log('$canActivate', arguments); } }) .component('crisisDetail', { templateUrl: 'crisisDetail.html', bindings: { $router: '<' }, controller: CrisisDetailComponent }); function CrisisService($q) { var crisesPromise = $q.resolve([ {id: 1, name: 'Princess Held Captive'}, {id: 2, name: 'Dragon Burning Cities'}, {id: 3, name: 'Giant Asteroid Heading For Earth'}, {id: 4, name: 'Release Deadline Looms'} ]); this.getCrises = function() { return crisesPromise; }; this.getCrisis = function(id) { return crisesPromise.then(function(crises) { for (var i = 0; i < crises.length; i++) { if (crises[i].id === id) return crises[i]; } }); }; } function CrisisListComponent(crisisService) { var selectedId = null; var ctrl = this; this.$routerOnActivate = function(next) { console.log('$routerOnActivate', this, arguments); // Load up the crises for this view crisisService.getCrises().then(function(crises) { ctrl.crises = crises; selectedId = next.params.id; }); }; this.isSelected = function(crisis) { return (crisis.id === selectedId); }; this.onSelect = function(crisis) { this.$router.navigate(['CrisisDetail', { id: crisis.id }]); }; } function CrisisDetailComponent(crisisService, dialogService) { var ctrl = this; this.$routerOnActivate = function(next) { // Get the crisis identified by the route parameter var id = next.params.id; crisisService.getCrisis(id).then(function(crisis) { if (crisis) { ctrl.editName = crisis.name; ctrl.crisis = crisis; } else { // id not found ctrl.gotoCrises(); } }); }; this.$routerCanDeactivate = function() { // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged. if (!this.crisis || this.crisis.name === this.editName) { return true; } // Otherwise ask the user with the dialog service and return its // promise which resolves to true or false when the user decides return dialogService.confirm('Discard changes?'); }; this.cancel = function() { ctrl.editName = ctrl.crisis.name; ctrl.gotoCrises(); }; this.save = function() { ctrl.crisis.name = ctrl.editName; ctrl.gotoCrises(); }; this.gotoCrises = function() { var crisisId = ctrl.crisis && ctrl.crisis.id; // Pass along the hero id if available // so that the CrisisListComponent can select that hero. this.$router.navigate(['CrisisList', {id: crisisId}]); }; }

    "{{$ctrl.editName}}"

    {{$ctrl.crisis.id}}
    angular.module('dialog', []) .service('dialogService', DialogService); function DialogService($q) { this.confirm = function(message) { return $q.resolve(window.confirm(message || 'Is it OK?')); }; } h1 {color: #369; font-family: Arial, Helvetica, sans-serif; font-size: 250%;} h2 { color: #369; font-family: Arial, Helvetica, sans-serif; } h3 { color: #444; font-weight: lighter; } body { margin: 2em; } body, input[text], button { color: #888; font-family: Cambria, Georgia; } button {padding: 0.2em; font-size: 14px} ul {list-style-type: none; margin-left: 1em; padding: 0; width: 20em;} li { cursor: pointer; position: relative; left: 0; transition: all 0.2s ease; } li:hover {color: #369; background-color: #EEE; left: .2em;} /* route-link anchor tags */ a {padding: 5px; text-decoration: none; font-family: Arial, Helvetica, sans-serif; } a:visited, a:link {color: #444;} a:hover {color: white; background-color: #1171a3; } a.router-link-active {color: white; background-color: #52b9e9; } .selected { background-color: #EEE; color: #369; } .badge { font-size: small; color: white; padding: 0.1em 0.7em; background-color: #369; line-height: 1em; position: relative; left: -1px; top: -1px; } crisis-detail input { width: 20em; }
    ### Getting Started In the following sections we will step through building this application. The finished application has views to display list and detail views of Heroes and Crises. #### Install the libraries It is easier to use [Yarn](https://yarnpkg.com) or [npm](https://www.npmjs.com) to install the **Component Router** module. For this guide we will also install AngularJS itself via Yarn: ```bash yarn init yarn add angular@1.5.x @angular/router@0.2.0 ``` #### Load the scripts Just like any AngularJS application, we load the JavaScript files into our `index.html`: ```html ``` You also need to include ES6 shims for browsers that do not support ES6 code (Internet Explorer, iOs < 8, Android < 5.0, Windows Mobile < 10): ```html ``` #### Create the `app` module In the app.js file, create the main application module `app` which depends on the `ngComponentRouter` module, which is provided by the **Component Router** script. ```js angular.module('app', ['ngComponentRouter']) ``` We must choose what **Location Mode** the **Router** should use. We are going to use HTML5 mode locations, so that we will not have hash-based paths. We must rely on the browser to provide `pushState` support, which is true for most modern browsers. See {@link $locationProvider#html5Mode} for more information.
    Using HTML5 mode means that we can have clean URLs for our application routes. However, HTML5 mode does require that our web server, which hosts the application, understands that it must respond with the index.html file for requests to URLs that represent all our application routes. We are going to use the `lite-server` web server to do this for us.
    ```js .config(function($locationProvider) { $locationProvider.html5Mode(true); }) ``` Configure the top level routed `App` Component. ```js .value('$routerRootComponent', 'app') ``` Create a very simple App Component to test that the application is working. We are using the AngularJS {@link $compileProvider#component `.component()`} helper method to create all the **Components** in our application. It is perfectly suited to this task. ```js .component('app', { template: 'It worked!' }); ``` Add a `` element to the head of our index.html. Remember that we have chosen to use HTML5 mode for the `$location` service. This means that our HTML must have a base URL. ```html ... ``` #### Bootstrap AngularJS Bootstrap the AngularJS application and add the top level App Component. ```html

    Component Router

    ``` ### Implementing the AppComponent In the previous section we have created a single top level **App Component**. Let's now create some more **Routing Components** and wire up **Route Config** for those. We start with a Heroes Feature, which will display one of two views. * A list of Heroes that are available: ![Heroes List View](img/guide/heroes-list.png) * A detailed view of a single Hero: ![Heroes List View](img/guide/hero-detail.png) We are going to have a `Heroes` Component for the Heroes feature of our application, and then `HeroList` and `HeroDetail` **Components** that will actually display the two different views. #### App Component Configure the **App Component** with a template and **Route Config**: ```js .component('app', { template: '\n' + '\n', $routeConfig: [ {path: '/heroes/...', name: 'Heroes', component: 'heroes'}, ] }); ``` The **App Component** has an `` directive in its template. This is where the child **Components** of this view will be rendered. #### ngLink We have used the `ng-link` directive to create a link to navigate to the Heroes Component. By using this directive we don't need to know what the actual URL will be. We can let the Router generate that for us. We have included a link to the Crisis Center but have not included the `ng-link` directive as we have not yet implemented the CrisisCenter component. #### Non-terminal Routes We need to tell the **Router** that the `Heroes` **Route Definition** is **non-terminal**, that it should continue to match **Routes** in its child **Components**. We do this by adding a **continuation ellipsis (`...`)** to the path of the Heroes Route, `/heroes/...`. Without the **continuation ellipsis** the `HeroList` **Route** will never be matched because the Router will stop at the `Heroes` **Routing Component** and not try to match the rest of the URL. ### Heroes Feature Now we can implement our Heroes Feature which consists of three **Components**: `Heroes`, `HeroList` and `HeroDetail`. The `Heroes` **Routing Component** simply provides a template containing the {@link ngOutlet} directive and a **Route Config** that defines a set of child **Routes** which delegate through to the `HeroList` and `HeroDetail` **Components**. ### HeroesComponent Create a new file `heroes.js`, which defines a new AngularJS module for the **Components** of this feature and registers the Heroes **Component**. ```js angular.module('heroes', []) .component('heroes', { template: '

    Heroes

    ', $routeConfig: [ {path: '/', name: 'HeroList', component: 'heroList', useAsDefault: true}, {path: '/:id', name: 'HeroDetail', component: 'heroDetail'} ] }) ``` Remember to load this file in the index.html: ```html ``` and also to add the module as a dependency of the `app` module: ```js angular.module('app', ['ngComponentRouter', 'heroes']) ``` #### Use As Default The `useAsDefault` property on the `HeroList` **Route Definition**, indicates that if no other **Route Definition** matches the URL, then this **Route Definition** should be used by default. #### Route Parameters The `HeroDetail` Route has a named parameter (`id`), indicated by prefixing the URL segment with a colon, as part of its `path` property. The **Router** will match anything in this segment and make that value available to the HeroDetail **Component**. #### Terminal Routes Both the Routes in the `HeroesComponent` are terminal, i.e. their routes do not end with `...`. This is because the `HeroList` and `HeroDetail` will not contain any child routes. #### Route Names **What is the difference between the `name` and `component` properties on a Route Definition?** The `component` property in a **Route Definition** defines the **Component** directive that will be rendered into the DOM via the **Outlet**. For example the `heroDetail` **Component** will be rendered into the page where the `` lives as ``. The `name` property is used to reference the **Route Definition** when generating URLs or navigating to **Routes**. For example this link will `Heroes` navigate the **Route Definition** that has the `name` property of `"Heroes"`. ### HeroList Component The HeroList **Component** is the first component in the application that actually contains significant functionality. It loads up a list of heroes from a `heroService` and displays them using `ng-repeat`. Add it to the `heroes.js` file: ```js .component('heroList', { template: '
    \n' + '{{hero.name}}\n' + '
    ', controller: HeroListComponent }) ``` The `ng-link` directive creates links to a more detailed view of each hero, via the expression `['HeroDetail', {id: hero.id}]`. This expression is an array describing what Routes to use to generate the link. The first item is the name of the HeroDetail **Route Definition** and the second is a parameter object that will be available to the HeroDetail **Component**. *The HeroDetail section below explains how to get hold of the `id` parameter of the HeroDetail Route.* The template iterates through each `hero` object of the array in the `$ctrl.heroes` property. *Remember that the `module.component()` helper automatically provides the **Component's Controller** as the `$ctrl` property on the scope of the template.* ### HeroService Our HeroService simulates requesting a list of heroes from a server. In a real application this would be making an actual server request, perhaps over HTTP. ```js function HeroService($q) { var heroesPromise = $q.resolve([ { id: 11, name: 'Mr. Nice' }, ... ]); this.getHeroes = function() { return heroesPromise; }; this.getHero = function(id) { return heroesPromise.then(function(heroes) { for (var i = 0; i < heroes.length; i++) { if (heroes[i].id === id) return heroes[i]; } }); }; } ``` Note that both the `getHeroes()` and `getHero(id)` methods return a promise for the data. This is because in real-life we would have to wait for the server to respond with the data. ### Router Lifecycle Hooks **How do I know when my Component is active?** To deal with initialization and tidy up of **Components** that are rendered by a **Router**, we can implement one or more **Lifecycle Hooks** on the **Component**. These will be called at well defined points in the lifecycle of the **Component**. The **Lifecycle Hooks** that can be implemented as instance methods on the **Component** are as follows: * `$routerCanReuse` : called to to determine whether a **Component** can be reused across **Route Definitions** that match the same type of **Component**, or whether to destroy and instantiate a new **Component** every time. * `$routerOnActivate` / `$routerOnReuse` : called by the **Router** at the end of a successful navigation. Only one of `$routerOnActivate` and `$routerOnReuse` will be called depending upon the result of a call to `$routerCanReuse`. * `$routerCanDeactivate` : called by the **Router** to determine if a **Component** can be removed as part of a navigation. * `$routerOnDeactivate` : called by the **Router** before destroying a **Component** as part of a navigation. We can also provide an **Injectable function** (`$routerCanActivate`) on the **Component Definition Object**, or as a static method on the **Component**, that will determine whether this **Component** is allowed to be activated. If any of the `$routerCan...` methods return false or a promise that resolves to false, the navigation will be cancelled. For our HeroList **Component** we want to load up the list of heroes when the **Component** is activated. So we implement the `$routerOnActivate()` instance method. ```js function HeroListComponent(heroService) { var $ctrl = this; this.$routerOnActivate = function() { return heroService.getHeroes().then(function(heroes) { $ctrl.heroes = heroes; }); } } ``` Running the application should update the browser's location to `/heroes` and display the list of heroes returned from the `heroService`. By returning a promise for the list of heroes from `$routerOnActivate()` we can delay the activation of the Route until the heroes have arrived successfully. This is similar to how a `resolve` works in {@link ngRoute}. ### Route Parameters **How do I access parameters for the current route?** The HeroDetailComponent displays details of an individual hero. The `id` of the hero to display is passed as part of the URL, for example **/heroes/12**. The **Router** parses the id from the URL when it recognizes the **Route Definition** and provides it to the **Component** as part of the parameters of the `$routerOnActivate()` hook. ```js function HeroDetailComponent(heroService) { var $ctrl = this; this.$routerOnActivate = function(next, previous) { // Get the hero identified by the route parameter var id = next.params.id; return heroService.getHero(id).then(function(hero) { $ctrl.hero = hero; }); }; ``` The `$routerOnActivate(next, previous)` hook receives two parameters, which hold the `next` and `previous` **Instruction** objects for the **Route** that is being activated. These parameters have a property called `params` which will hold the `id` parameter extracted from the URL by the **Router**. In this code it is used to identify a specific Hero to retrieve from the `heroService`. This hero is then attached to the **Component** so that it can be accessed in the template. ### Access to the Current Router **How do I get hold of the current router for my component?** Each component has its own Router. Unlike in the new Angular, we cannot use the dependency injector to get hold of a component's Router. We can only inject the `$rootRouter`. Instead we use the fact that the `ng-outlet` directive binds the current router to a `$router` attribute on our component. ```html ``` We can then specify a `bindings` property on our component definition to bind the current router to our component: ```js bindings: { $router: '<' } ``` This sets up a one-way binding of the current Router to the `$router` property of our Component. The binding is available once the component has been activated, and the `$routerOnActivate` hook is called. As you might know from reading the {@link guide/component component guide}, the binding is actually available by the time the `$onInit` hook is called, which is before the call to `$routerOnActivate`. ### HeroDetailComponent The `HeroDetailComponent` displays a form that allows the Hero to be modified. ```js .component('heroDetail', { template: '
    \n' + '

    "{{$ctrl.hero.name}}"

    \n' + '
    \n' + ' {{$ctrl.hero.id}}
    \n' + '
    \n' + ' \n' + ' \n' + '
    \n' + ' \n' + '
    \n', bindings: { $router: '<' }, controller: HeroDetailComponent }); ``` The template contains a button to navigate back to the HeroList. We could have styled an anchor to look like a button and used `ng-link="['HeroList']" but here we demonstrate programmatic navigation via the Router itself, which was made available by the binding in the **Component Definition Object**. ```js function HeroDetailComponent(heroService) { ... this.gotoHeroes = function() { this.$router.navigate(['HeroList']); }; ``` Here we are asking the Router to navigate to a route defined by `['HeroList']`. This is the same kind of array used by the `ng-link` directive. Other options for generating this navigation are: * manually create the URL and call `this.$router.navigateByUrl(url)` - this is discouraged because it couples the code of your component to the router URLs. * generate an Instruction for a route and navigate directly with this instruction. ```js var instruction = this.$router.generate(['HeroList']); this.$router.navigateByInstruction(instruction); ``` this form gives you the possibility of caching the instruction, but is more verbose. #### Absolute vs Relative Navigation **Why not use `$rootRouter` to do the navigation?** Instead of binding to the current **Router**, we can inject the `$rootRouter` into our **Component** and use that: `$rootRouter.navigate(...)`. The trouble with doing this is that navigation is always relative to the **Router**. So in order to navigate to the `HeroListComponent` with the `$rootRouter`, we would have to provide a complete path of Routes: `['App','Heroes','HeroList']`. ### Extra Parameters We can also pass additional optional parameters to routes, which get encoded into the URL and are again available to the `$routerOnActivate(next, previous)` hook. If we pass the current `id` from the HeroDetailComponent back to the HeroListComponent we can use it to highlight the previously selected hero. ```js this.gotoHeroes = function() { var heroId = this.hero && this.hero.id; this.$router.navigate(['HeroList', {id: heroId}]); }; ``` Then in the HeroList component we can extract this `id` in the `$routerOnActivate()` hook. ```js function HeroListComponent(heroService) { var selectedId = null; var $ctrl = this; this.$routerOnActivate = function(next) { heroService.getHeroes().then(function(heroes) { $ctrl.heroes = heroes; selectedId = next.params.id; }); }; this.isSelected = function(hero) { return (hero.id === selectedId); }; } ``` Finally, we can use this information to highlight the current hero in the template. ```html ``` ### Crisis Center Let's implement the Crisis Center feature, which displays a list if crises that need to be dealt with by a hero. The detailed crisis view has an additional feature where it blocks you from navigating if you have not saved changes to the crisis being edited. * A list of Crises that are happening: ![Crisis List View](img/guide/crisis-list.png) * A detailed view of a single Crisis: ![Crisis Detail View](img/guide/crisis-detail.png) ### Crisis Feature This feature is very similar to the Heroes feature. It contains the following **Components**: * CrisisService: contains method for getting a list of crises and an individual crisis. * CrisisListComponent: displays the list of crises, similar to HeroListComponent. * CrisisDetailComponent: displays a specific crisis CrisisService and CrisisListComponent are basically the same as HeroService and HeroListComponent respectively. ### Navigation Control Hooks **How do I prevent navigation from occurring?** Each **Component** can provide the `$canActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The `$routerCanDeactivate` hook is an instance method on the **Component**. The `$canActivate` hook is used as a static method defined on the **Component Definition Object**. The **Router** will call these hooks to control navigation from one **Route** to another. Each of these hooks can return a `boolean` or a Promise that will resolve to a `boolean`. During a navigation, some **Components** will become inactive and some will become active. Before the navigation can complete, all the **Components** must agree that they can be deactivated or activated, respectively. The **Router** will call the `$routerCanDeactivate` and `$canActivate` hooks, if they are provided. If any of the hooks resolve to `false` then the navigation is cancelled. #### Dialog Box Service We can implement a very simple dialog box that will prompt the user whether they are happy to lose changes they have made. The result of the prompt is a promise that can be used in a `$routerCanDeactivate` hook. ```js .service('dialogService', DialogService); function DialogService($q) { this.confirm = function(message) { return $q.resolve(window.confirm(message || 'Is it OK?')); }; } ``` ### CrisisDetailComponent We put the template into its own file by using a `templateUrl` property in the **Component Definition Object**: ```js .component('crisisDetail', { templateUrl: 'app/crisisDetail.html', bindings: { $router: '<' }, controller: CrisisDetailComponent }); ``` In the `$routerOnActivate` hook, we make a local copy of the `crisis.name` property to compare with the original value so that we can determine whether the name has changed. ```js this.$routerOnActivate = function(next) { // Get the crisis identified by the route parameter var id = next.params.id; crisisService.getCrisis(id).then(function(crisis) { if (crisis) { ctrl.editName = crisis.name; // Make a copy of the crisis name for editing ctrl.crisis = crisis; } else { // id not found ctrl.gotoCrises(); } }); }; ``` In the `$routerCanDeactivate` we check whether the name has been modified and ask whether the user wishes to discard the changes. ```js this.$routerCanDeactivate = function() { // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged. if (!this.crisis || this.crisis.name === this.editName) { return true; } // Otherwise ask the user with the dialog service and return its // promise which resolves to true or false when the user decides return dialogService.confirm('Discard changes?'); }; ``` You can test this check by navigating to a crisis detail page, modifying the name and then either pressing the browser's back button to navigate back to the previous page, or by clicking on one of the links to the Crisis Center or Heroes features. The Save and Cancel buttons update the `editName` and/or `crisis.name` properties before navigating to prevent the `$routerCanDeactivate` hook from displaying the dialog box. ## Summary This guide has given an overview of the features of the Component Router and how to implement a simple application. angular.js-1.7.9/docs/content/guide/component.ngdoc000066400000000000000000000446111356472325200223300ustar00rootroot00000000000000@ngdoc overview @name Components @sortOrder 305 @description # Understanding Components In AngularJS, a Component is a special kind of {@link guide/directive directive} that uses a simpler configuration which is suitable for a component-based application structure. This makes it easier to write an app in a way that's similar to using Web Components or using the new Angular's style of application architecture. Advantages of Components: - simpler configuration than plain directives - promote sane defaults and best practices - optimized for component-based architecture - writing component directives will make it easier to upgrade to Angular When not to use Components: - for directives that need to perform actions in compile and pre-link functions, because they aren't available - when you need advanced directive definition options like priority, terminal, multi-element - when you want a directive that is triggered by an attribute or CSS class, rather than an element ## Creating and configuring a Component Components can be registered using the {@link ng.$compileProvider#component `.component()`} method of an AngularJS module (returned by {@link module `angular.module()`}). The method takes two arguments: * The name of the Component (as string). * The Component config object. (Note that, unlike the `.directive()` method, this method does **not** take a factory function.) angular.module('heroApp', []).controller('MainCtrl', function MainCtrl() { this.hero = { name: 'Spawn' }; }); angular.module('heroApp').component('heroDetail', { templateUrl: 'heroDetail.html', bindings: { hero: '=' } });
    Hero
    Name: {{$ctrl.hero.name}}
    It's also possible to add components via {@link $compileProvider#component} in a module's config phase. ### Comparison between Directive definition and Component definition | | Directive | Component | |-------------------|----------------------|-----------------| | bindings | No | Yes (binds to controller) | | bindToController | Yes (default: false) | No (use bindings instead) | | compile function | Yes | No | | controller | Yes | Yes (default `function() {}`) | | controllerAs | Yes (default: false) | Yes (default: `$ctrl`) | | link functions | Yes | No | | multiElement | Yes | No | | priority | Yes | No | | replace | Yes (deprecated) | No | | require | Yes | Yes | | restrict | Yes | No (restricted to elements only) | | scope | Yes (default: false) | No (scope is always isolate) | | template | Yes | Yes, injectable | | templateNamespace | Yes | No | | templateUrl | Yes | Yes, injectable | | terminal | Yes | No | | transclude | Yes (default: false) | Yes (default: false) | ## Component-based application architecture As already mentioned, the component helper makes it easier to structure your application with a component-based architecture. But what makes a component beyond the options that the component helper has? - **Components only control their own View and Data:** Components should never modify any data or DOM that is out of their own scope. Normally, in AngularJS it is possible to modify data anywhere in the application through scope inheritance and watches. This is practical, but can also lead to problems when it is not clear which part of the application is responsible for modifying the data. That is why component directives use an isolate scope, so a whole class of scope manipulation is not possible. - **Components have a well-defined public API - Inputs and Outputs:** However, scope isolation only goes so far, because AngularJS uses two-way binding. So if you pass an object to a component like this - `bindings: {item: '='}`, and modify one of its properties, the change will be reflected in the parent component. For components however, only the component that owns the data should modify it, to make it easy to reason about what data is changed, and when. For that reason, components should follow a few simple conventions: - Inputs should be using `<` and `@` bindings. The `<` symbol denotes {@link $compile#-scope- one-way bindings} which are available since 1.5. The difference to `=` is that the bound properties in the component scope are not watched, which means if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent and component scope reference the same object, so if you are changing object properties or array elements in the component, the parent will still reflect that change. The general rule should therefore be to never change an object or array property in the component scope. `@` bindings can be used when the input is a string, especially when the value of the binding doesn't change. ```js bindings: { hero: '<', comment: '@' } ``` - Outputs are realized with `&` bindings, which function as callbacks to component events. ```js bindings: { onDelete: '&', onUpdate: '&' } ``` - Instead of manipulating Input Data, the component calls the correct Output Event with the changed data. For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to the owner component via the correct event. ```html
    ``` - That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties) ```js ctrl.deleteHero(hero) { $http.delete(...).then(function() { var idx = ctrl.list.indexOf(hero); if (idx >= 0) { ctrl.list.splice(idx, 1); } }); } ``` - **Components have a well-defined lifecycle:** Each component can implement "lifecycle hooks". These are methods that will be called at certain points in the life of the component. The following hook methods can be implemented: * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and had their bindings initialized (and before the pre & post linking functions for the directives on this element). This is a good place to put initialization code for your controller. * `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a component such as cloning the bound value to prevent accidental mutation of the outer value. * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on changes. Any actions that you wish to take in response to the changes that you detect must be invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not be detected by AngularJS's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments; if detecting changes, you must store the previous value(s) for comparison to the current values. * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing external resources, watches and event handlers. * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain `templateUrl` directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs. This hook can be considered analogous to the `ngAfterViewInit` and `ngAfterContentInit` hooks in Angular. Since the compilation process is rather different in AngularJS there is no direct mapping and care should be taken when upgrading. By implementing these methods, your component can hook into its lifecycle. - **An application is a tree of components:** Ideally, the whole application should be a tree of components that implement clearly defined inputs and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state of a component is. ## Example of a component tree The following example expands on the simple component example and incorporates the concepts we introduced above: Instead of an ngController, we now have a heroList component that holds the data of different heroes, and creates a heroDetail for each of them. The heroDetail component now contains new functionality: - a delete button that calls the bound `onDelete` function of the heroList component - an input to change the hero location, in the form of a reusable editableField component. Instead of manipulating the hero object itself, it sends a changeset upwards to the heroDetail, which sends it upwards to the heroList component, which updates the original data. angular.module('heroApp', []); function HeroListController($scope, $element, $attrs) { var ctrl = this; // This would be loaded by $http etc. ctrl.list = [ { name: 'Superman', location: '' }, { name: 'Batman', location: 'Wayne Manor' } ]; ctrl.updateHero = function(hero, prop, value) { hero[prop] = value; }; ctrl.deleteHero = function(hero) { var idx = ctrl.list.indexOf(hero); if (idx >= 0) { ctrl.list.splice(idx, 1); } }; } angular.module('heroApp').component('heroList', { templateUrl: 'heroList.html', controller: HeroListController }); function HeroDetailController() { var ctrl = this; ctrl.delete = function() { ctrl.onDelete({hero: ctrl.hero}); }; ctrl.update = function(prop, value) { ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value}); }; } angular.module('heroApp').component('heroDetail', { templateUrl: 'heroDetail.html', controller: HeroDetailController, bindings: { hero: '<', onDelete: '&', onUpdate: '&' } }); function EditableFieldController($scope, $element, $attrs) { var ctrl = this; ctrl.editMode = false; ctrl.handleModeChange = function() { if (ctrl.editMode) { ctrl.onUpdate({value: ctrl.fieldValue}); ctrl.fieldValueCopy = ctrl.fieldValue; } ctrl.editMode = !ctrl.editMode; }; ctrl.reset = function() { ctrl.fieldValue = ctrl.fieldValueCopy; }; ctrl.$onInit = function() { // Make a copy of the initial value to be able to reset it later ctrl.fieldValueCopy = ctrl.fieldValue; // Set a default fieldType if (!ctrl.fieldType) { ctrl.fieldType = 'text'; } }; } angular.module('heroApp').component('editableField', { templateUrl: 'editableField.html', controller: EditableFieldController, bindings: { fieldValue: '<', fieldType: '@?', onUpdate: '&' } }); Heroes

    Name: {{$ctrl.hero.name}}
    Location:
    {{$ctrl.fieldValue}}
    ## Components as route templates Components are also useful as route templates (e.g. when using {@link ngRoute ngRoute}). In a component-based application, every view is a component: ```js var myMod = angular.module('myMod', ['ngRoute']); myMod.component('home', { template: '

    Home

    Hello, {{ $ctrl.user.name }} !

    ', controller: function() { this.user = {name: 'world'}; } }); myMod.config(function($routeProvider) { $routeProvider.when('/', { template: '' }); }); ```
    When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some boilerplate, by passing the resolved route dependencies directly to the component. Since 1.5, ngRoute automatically assigns the resolves to the route scope property `$resolve` (you can also configure the property name via `resolveAs`). When using components, you can take advantage of this and pass resolves directly into your component without creating an extra route controller: ```js var myMod = angular.module('myMod', ['ngRoute']); myMod.component('home', { template: '

    Home

    Hello, {{ $ctrl.user.name }} !

    ', bindings: { user: '<' } }); myMod.config(function($routeProvider) { $routeProvider.when('/', { template: '', resolve: { user: function($http) { return $http.get('...'); } } }); }); ``` ## Intercomponent Communication Directives can require the controllers of other directives to enable communication between each other. This can be achieved in a component by providing an object mapping for the `require` property. The object keys specify the property names under which the required controllers (object values) will be bound to the requiring component's controller.
    Note that the required controllers will not be available during the instantiation of the controller, but they are guaranteed to be available just before the `$onInit` method is executed!
    Here is a tab pane example built from components: angular.module('docsTabsExample', []) .component('myTabs', { transclude: true, controller: function MyTabsController() { var panes = this.panes = []; this.select = function(pane) { angular.forEach(panes, function(pane) { pane.selected = false; }); pane.selected = true; }; this.addPane = function(pane) { if (panes.length === 0) { this.select(pane); } panes.push(pane); }; }, templateUrl: 'my-tabs.html' }) .component('myPane', { transclude: true, require: { tabsCtrl: '^myTabs' }, bindings: { title: '@' }, controller: function() { this.$onInit = function() { this.tabsCtrl.addPane(this); console.log(this); }; }, templateUrl: 'my-pane.html' });

    Hello

    Lorem ipsum dolor sit amet

    World

    Mauris elementum elementum enim at suscipit.

    counter: {{i || 0}}

    ## Unit-testing Component Controllers The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController} that is included in {@link ngMock}. The advantage of this method is that you do not have to create any DOM elements. The following example shows how to do this for the `heroDetail` component from above. The examples use the [Jasmine](http://jasmine.github.io/) testing framework. **Controller Test:** ```js describe('HeroDetailController', function() { var $componentController; beforeEach(module('heroApp')); beforeEach(inject(function(_$componentController_) { $componentController = _$componentController_; })); it('should call the `onDelete` binding, when deleting the hero', function() { var onDeleteSpy = jasmine.createSpy('onDelete'); var bindings = {hero: {}, onDelete: onDeleteSpy}; var ctrl = $componentController('heroDetail', null, bindings); ctrl.delete(); expect(onDeleteSpy).toHaveBeenCalledWith({hero: ctrl.hero}); }); it('should call the `onUpdate` binding, when updating a property', function() { var onUpdateSpy = jasmine.createSpy('onUpdate'); var bindings = {hero: {}, onUpdate: onUpdateSpy}; var ctrl = $componentController('heroDetail', null, bindings); ctrl.update('foo', 'bar'); expect(onUpdateSpy).toHaveBeenCalledWith({ hero: ctrl.hero, prop: 'foo', value: 'bar' }); }); }); ``` angular.js-1.7.9/docs/content/guide/concepts.ngdoc000066400000000000000000000433541356472325200221470ustar00rootroot00000000000000@ngdoc overview @name Conceptual Overview @sortOrder 200 @description # Conceptual Overview This section briefly touches on all of the important parts of AngularJS using a simple example. For a more in-depth explanation, see the {@link tutorial/ tutorial}. | Concept | Description | |--------------------------------------------|--------------------------------------------------------------------------| |{@link concepts#template Template} | HTML with additional markup | |{@link concepts#directive Directives} | extend HTML with custom attributes and elements | |{@link concepts#model Model} | the data shown to the user in the view and with which the user interacts | |{@link concepts#scope Scope} | context where the model is stored so that controllers, directives and expressions can access it | |{@link concepts#expression Expressions} | access variables and functions from the scope | |{@link concepts#compiler Compiler} | parses the template and instantiates directives and expressions | |{@link concepts#filter Filter} | formats the value of an expression for display to the user | |{@link concepts#view View} | what the user sees (the DOM) | |{@link concepts#databinding Data Binding} | sync data between the model and the view | |{@link concepts#controller Controller} | the business logic behind views | |{@link concepts#di Dependency Injection} | Creates and wires objects and functions | |{@link concepts#injector Injector} | dependency injection container | |{@link concepts#module Module} | a container for the different parts of an app including controllers, services, filters, directives which configures the Injector | |{@link concepts#service Service} | reusable business logic independent of views | ## A first example: Data binding In the following example we will build a form to calculate the costs of an invoice in different currencies. Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:
    Invoice:
    Quantity:
    Costs:
    Total: {{qty * cost | currency}}
    Try out the Live Preview above, and then let's walk through the example and describe what's going on. This looks like normal HTML, with some new markup. In AngularJS, a file like this is called a {@link templates template}. When AngularJS starts your application, it parses and processes this new markup from the template using the {@link compiler compiler}. The loaded, transformed and rendered DOM is then called the *view*. The first kind of new markup are the {@link directive directives}. They apply special behavior to attributes or elements in the HTML. In the example above we use the {@link ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically initializes our application. AngularJS also defines a directive for the {@link ng.directive:input `input`} element that adds extra behavior to the element. The {@link ng.directive:ngModel `ng-model`} directive stores/updates the value of the input field into/from a variable.
    **Custom directives to access the DOM**: In AngularJS, the only place where an application should access the DOM is within directives. This is important because artifacts that access the DOM are hard to test. If you need to access the DOM directly you should write a custom directive for this. The {@link directive directives guide} explains how to do this.
    The second kind of new markup are the double curly braces `{{ expression | filter }}`: When the compiler encounters this markup, it will replace it with the evaluated value of the markup. An {@link expression expression} in a template is a JavaScript-like code snippet that allows AngularJS to read and write variables. Note that those variables are not global variables. Just like variables in a JavaScript function live in a scope, AngularJS provides a {@link scope scope} for the variables accessible to expressions. The values that are stored in variables on the scope are referred to as the *model* in the rest of the documentation. Applied to the example above, the markup directs AngularJS to "take the data we got from the input widgets and multiply them together". The example above also contains a {@link guide/filter filter}. A filter formats the value of an expression for display to the user. In the example above, the filter {@link ng.filter:currency `currency`} formats a number into an output that looks like money. The important thing in the example is that AngularJS provides _live_ bindings: Whenever the input values change, the value of the expressions are automatically recalculated and the DOM is updated with their values. The concept behind this is {@link databinding two-way data binding}. ## Adding UI logic: Controllers Let's add some more logic to the example that allows us to enter and calculate the costs in different currencies and also pay the invoice. angular.module('invoice1', []) .controller('InvoiceController', function InvoiceController() { this.qty = 1; this.cost = 2; this.inCurr = 'EUR'; this.currencies = ['USD', 'EUR', 'CNY']; this.usdToForeignRates = { USD: 1, EUR: 0.74, CNY: 6.09 }; this.total = function total(outCurr) { return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr); }; this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) { return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr]; }; this.pay = function pay() { window.alert('Thanks!'); }; });
    Invoice:
    Quantity:
    Costs:
    Total: {{invoice.total(c) | currency:c}}
    What changed? First, there is a new JavaScript file that contains a {@link controller controller}. More accurately, the file specifies a constructor function that will be used to create the actual controller instance. The purpose of controllers is to expose variables and functionality to expressions and directives. Besides the new file that contains the controller code, we also added an {@link ng.directive:ngController `ng-controller`} directive to the HTML. This directive tells AngularJS that the new `InvoiceController` is responsible for the element with the directive and all of the element's children. The syntax `InvoiceController as invoice` tells AngularJS to instantiate the controller and save it in the variable `invoice` in the current scope. We also changed all expressions in the page to read and write variables within that controller instance by prefixing them with `invoice.` . The possible currencies are defined in the controller and added to the template using {@link ng.directive:ngRepeat `ng-repeat`}. As the controller contains a `total` function we are also able to bind the result of that function to the DOM using `{{ invoice.total(...) }}`. Again, this binding is live, i.e. the DOM will be automatically updated whenever the result of the function changes. The button to pay the invoice uses the directive {@link ng.directive:ngClick `ngClick`}. This will evaluate the corresponding expression whenever the button is clicked. In the new JavaScript file we are also creating a {@link concepts#module module} at which we register the controller. We will talk about modules in the next section. The following graphic shows how everything works together after we introduced the controller: ## View-independent business logic: Services Right now, the `InvoiceController` contains all logic of our example. When the application grows it is a good practice to move view-independent logic from the controller into a {@link services service}, so it can be reused by other parts of the application as well. Later on, we could also change that service to load the exchange rates from the web, e.g. by calling the [exchangeratesapi.io](https://exchangeratesapi.io) exchange rate API, without changing the controller. Let's refactor our example and move the currency conversion into a service in another file: angular.module('finance2', []) .factory('currencyConverter', function() { var currencies = ['USD', 'EUR', 'CNY']; var usdToForeignRates = { USD: 1, EUR: 0.74, CNY: 6.09 }; var convert = function(amount, inCurr, outCurr) { return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr]; }; return { currencies: currencies, convert: convert }; }); angular.module('invoice2', ['finance2']) .controller('InvoiceController', ['currencyConverter', function InvoiceController(currencyConverter) { this.qty = 1; this.cost = 2; this.inCurr = 'EUR'; this.currencies = currencyConverter.currencies; this.total = function total(outCurr) { return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr); }; this.pay = function pay() { window.alert('Thanks!'); }; }]);
    Invoice:
    Quantity:
    Costs:
    Total: {{invoice.total(c) | currency:c}}
    What changed? We moved the `convertCurrency` function and the definition of the existing currencies into the new file `finance2.js`. But how does the controller get a hold of the now separated function? This is where {@link di Dependency Injection} comes into play. Dependency Injection (DI) is a software design pattern that deals with how objects and functions get created and how they get a hold of their dependencies. Everything within AngularJS (directives, filters, controllers, services, ...) is created and wired using dependency injection. Within AngularJS, the DI container is called the {@link di injector}. To use DI, there needs to be a place where all the things that should work together are registered. In AngularJS, this is the purpose of the {@link module modules}. When AngularJS starts, it will use the configuration of the module with the name defined by the `ng-app` directive, including the configuration of all modules that this module depends on. In the example above: The template contains the directive `ng-app="invoice2"`. This tells AngularJS to use the `invoice2` module as the main module for the application. The code snippet `angular.module('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the `finance2` module. By this, AngularJS uses the `InvoiceController` as well as the `currencyConverter` service. Now that AngularJS knows of all the parts of the application, it needs to create them. In the previous section we saw that controllers are created using a constructor function. For services, there are multiple ways to specify how they are created (see the {@link services service guide}). In the example above, we are using an anonymous function as the factory function for the `currencyConverter` service. This function should return the `currencyConverter` service instance. Back to the initial question: How does the `InvoiceController` get a reference to the `currencyConverter` function? In AngularJS, this is done by simply defining arguments on the constructor function. With this, the injector is able to create the objects in the right order and pass the previously created objects into the factories of the objects that depend on them. In our example, the `InvoiceController` has an argument named `currencyConverter`. By this, AngularJS knows about the dependency between the controller and the service and calls the controller with the service instance as argument. The last thing that changed in the example between the previous section and this section is that we now pass an array to the `module.controller` function, instead of a plain function. The array first contains the names of the service dependencies that the controller needs. The last entry in the array is the controller constructor function. AngularJS uses this array syntax to define the dependencies so that the DI also works after minifying the code, which will most probably rename the argument name of the controller constructor function to something shorter like `a`. ## Accessing the backend Let's finish our example by fetching the exchange rates from the [exchangeratesapi.io](https://exchangeratesapi.io) exchange rate API. The following example shows how this is done with AngularJS: angular.module('invoice3', ['finance3']) .controller('InvoiceController', ['currencyConverter', function InvoiceController(currencyConverter) { this.qty = 1; this.cost = 2; this.inCurr = 'EUR'; this.currencies = currencyConverter.currencies; this.total = function total(outCurr) { return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr); }; this.pay = function pay() { window.alert('Thanks!'); }; }]); angular.module('finance3', []) .factory('currencyConverter', ['$http', function($http) { var currencies = ['USD', 'EUR', 'CNY']; var usdToForeignRates = {}; var convert = function(amount, inCurr, outCurr) { return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr]; }; var refresh = function() { var url = 'https://api.exchangeratesapi.io/latest?base=USD&symbols=' + currencies.join(","); return $http.get(url).then(function(response) { usdToForeignRates = response.data.rates; usdToForeignRates['USD'] = 1; }); }; refresh(); return { currencies: currencies, convert: convert }; }]);
    Invoice:
    Quantity:
    Costs:
    Total: {{invoice.total(c) | currency:c}}
    What changed? Our `currencyConverter` service of the `finance` module now uses the {@link ng.$http `$http`}, a built-in service provided by AngularJS for accessing a server backend. `$http` is a wrapper around [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports. angular.js-1.7.9/docs/content/guide/controller.ngdoc000066400000000000000000000303001356472325200224770ustar00rootroot00000000000000@ngdoc overview @name Controllers @sortOrder 220 @description # Understanding Controllers In AngularJS, a Controller is defined by a JavaScript **constructor function** that is used to augment the {@link scope AngularJS Scope}. Controllers can be attached to the DOM in different ways. For each of them, AngularJS will instantiate a new Controller object, using the specified Controller's **constructor function**: - the {@link ng.directive:ngController ngController} directive. A new **child scope** will be created and made available as an injectable parameter to the Controller's constructor function as `$scope`. - a route controller in a {@link ngRoute.$routeProvider $route definition}. - the controller of a {@link guide/directive regular directive}, or a {@link guide/component component directive}. If the controller has been attached using the `controller as` syntax then the controller instance will be assigned to a property on the scope. Use controllers to: - Set up the initial state of the `$scope` object. - Add behavior to the `$scope` object. Do not use controllers to: - Manipulate DOM — Controllers should contain only business logic. Putting any presentation logic into Controllers significantly affects its testability. AngularJS has {@link databinding databinding} for most cases and {@link guide/directive directives} to encapsulate manual DOM manipulation. - Format input — Use {@link forms AngularJS form controls} instead. - Filter output — Use {@link guide/filter AngularJS filters} instead. - Share code or state across controllers — Use {@link services AngularJS services} instead. - Manage the life-cycle of other components (for example, to create service instances). In general, a Controller shouldn't try to do too much. It should contain only the business logic needed for a single view. The most common way to keep Controllers slim is by encapsulating work that doesn't belong to controllers into services and then using these services in Controllers via dependency injection. This is discussed in the {@link di Dependency Injection} and {@link services Services} sections of this guide. ## Setting up the initial state of a `$scope` object Typically, when you create an application you need to set up the initial state for the AngularJS `$scope`. You set up the initial state of a scope by attaching properties to the `$scope` object. The properties contain the **view model** (the model that will be presented by the view). All the `$scope` properties will be available to the {@link templates template} at the point in the DOM where the Controller is registered. The following example demonstrates creating a `GreetingController`, which attaches a `greeting` property containing the string `'Hola!'` to the `$scope`: ```js var myApp = angular.module('myApp',[]); myApp.controller('GreetingController', ['$scope', function($scope) { $scope.greeting = 'Hola!'; }]); ``` We create an {@link module AngularJS Module}, `myApp`, for our application. Then we add the controller's constructor function to the module using the `.controller()` method. This keeps the controller's constructor function out of the global scope.
    We have used an **inline injection annotation** to explicitly specify the dependency of the Controller on the `$scope` service provided by AngularJS. See the guide on {@link guide/di Dependency Injection} for more information.
    We attach our controller to the DOM using the `ng-controller` directive. The `greeting` property can now be data-bound to the template: ```js
    {{ greeting }}
    ``` ## Adding Behavior to a Scope Object In order to react to events or execute computation in the view we must provide behavior to the scope. We add behavior to the scope by attaching methods to the `$scope` object. These methods are then available to be called from the template/view. The following example uses a Controller to add a method, which doubles a number, to the scope: ```js var myApp = angular.module('myApp',[]); myApp.controller('DoubleController', ['$scope', function($scope) { $scope.double = function(value) { return value * 2; }; }]); ``` Once the Controller has been attached to the DOM, the `double` method can be invoked in an AngularJS expression in the template: ```js
    Two times equals {{ double(num) }}
    ``` As discussed in the {@link concepts Concepts} section of this guide, any objects (or primitives) assigned to the scope become model properties. Any methods assigned to the scope are available in the template/view, and can be invoked via AngularJS expressions and `ng` event handler directives (e.g. {@link ng.directive:ngClick ngClick}). ## Simple Spicy Controller Example To illustrate further how Controller components work in AngularJS, let's create a little app with the following components: - A {@link templates template} with two buttons and a simple message - A model consisting of a string named `spice` - A Controller with two functions that set the value of `spice` The message in our template contains a binding to the `spice` model which, by default, is set to the string "very". Depending on which button is clicked, the `spice` model is set to `chili` or `jalapeño`, and the message is automatically updated by data-binding.

    The food is {{spice}} spicy!

    var myApp = angular.module('spicyApp1', []); myApp.controller('SpicyController', ['$scope', function($scope) { $scope.spice = 'very'; $scope.chiliSpicy = function() { $scope.spice = 'chili'; }; $scope.jalapenoSpicy = function() { $scope.spice = 'jalapeño'; }; }]);
    Things to notice in the example above: - The `ng-controller` directive is used to (implicitly) create a scope for our template, and the scope is augmented (managed) by the `SpicyController` Controller. - `SpicyController` is just a plain JavaScript function. As an (optional) naming convention the name starts with capital letter and ends with "Controller". - Assigning a property to `$scope` creates or updates the model. - Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method) - The Controller methods and properties are available in the template (for both the `