Compare commits

...

217 Commits

Author SHA1 Message Date
Peter Bacon Darwin 19a0c9324c chore(jenkins): do not publish to code.angularjs.org snapshot
While the firewall continues to block the update ports
we will not try to publish there. This will be fixed when we move to hosting
the sites on Firebase.

This means that successful builds on master will not automatically update
code.angularjs.org, this will affect:

* https://code.angularjs.org/snapshot, which people often use to check latest features
* https://docs.angularjs.org, which is supposed to display the docs for the latest master

As it turns out we can manually partially trigger an update by browsing to
https://code.angularjs.org/gitFetchSite.php but we just can’t guarantee that we will update
both the round robin servers.
2017-07-03 22:34:52 +03:00
Georgios Kalpakas 9ef02e72ab docs(changelog): add release notes for 1.6.5 2017-07-03 15:19:52 +03:00
ksvitkovsky 2101126ce7 feat($compile): overload .component() to accept object map of components
Register multiple components with single call as it is possible with other module units.

Closes #14579 
Closes #16062
2017-07-03 11:10:07 +02:00
Jason Bedard a222d0b452 fix($timeout/$interval): do not trigger a digest on cancel
Previously, `.catch(noop)` was used on a rejected timeout/interval to prevent an unhandled rejection error (introduced in #c9dffde1cb). However this would schedule a deferred task to run the `noop`. If the cancelling was outside a digest this could cause a new digest such as with the ng-model `debounce` option.

For unit testing, this means that it's no longer necessary to use `$timeout.flush()` when a `$timeout` has been cancelled outside of a digest. Previously, this was necessary to execute the deferred task added by `.catch(noop).
There's an example of such a change in this commit's changeset in the file `/test/ngAnimate/animateCssSpec.js`.

Fixes #16057
Closes #16064
2017-07-03 11:10:00 +02:00
davesidious 1b8eb231c9 docs($log): add note about blackboxing
Add browser-agnostic hint about blackboxing and the benefits it brings developers when using $log.

Closes #15592
2017-06-30 12:04:21 +02:00
Martin Staffa 06baf1869b docs($rootScope.Scope): clarify $watchGroup "oldValues" argument
This should help to prevent issues such as #8671, #12452, #16004.

Note that the behavior will change in 1.7 (see https://github.com/angular/angular.js/pull/15854)

Closes #12643
Closes #16005
2017-06-29 18:18:32 +02:00
Martin Staffa 82597fc12b test(ngOptions): ensure options are only painted once on compile 2017-06-29 10:54:57 +02:00
Pol Bonastre ff52b188a7 perf(ngOptions): prevent initial options repainting
Avoid double execution of `updateOptions()` method,
which causes a complete repainting of all `<option>` elements.

Fixes #15801
Closes #15812
Close #16071
2017-06-29 10:54:53 +02:00
Martin Staffa dc41f465ba fix(Angular): deprecate angular.merge
This function has problems with special object types but since it's not used in core,
it is not worth implementing fixes for these cases.
A general purpose library like lodash (provides `merge`) should be used instead.

Closes #12653
Closes #14941
Closes #15180
Closes #15992
Closes #16036
2017-06-29 10:53:40 +02:00
Jason Bedard 6e3b5a57cd fix($parse): do not shallow-watch inputs to one-time intercepted expressions 2017-06-19 21:27:31 -07:00
Jason Bedard f003d93a3d Revert "fix($parse): standardize one-time literal vs non-literal and interceptors"
This reverts commit 60394a9d91.
2017-06-19 19:20:43 -07:00
Jason Bedard aac5623247 fix($parse): do not shallow-watch inputs when wrapped in an interceptor fn
Fixes #15905
2017-06-12 21:32:14 -07:00
Saurav Bhattacharya aa03812fd0 docs(external-resources.ngdoc): fix broken link
Closes #16042
2017-06-12 15:04:15 +03:00
Georgios Kalpakas bcb6a494de test(ngMock): fix Firefox craches on Travis
This test keeps causing Firefox 47 (currently used on Travis) to crash and fail
the build. The test passes locally (on Firefox 53). Lowering the loop count from
1000 to 100 seems to fix the issue.
(Note: The crach only affects the mocked implementation of `$interval` and does
not happen locally.)

Closes #16040
2017-06-12 14:45:27 +03:00
Lucio Martinez a1e3f8728e fix(ngMock/$interval): add support for zero-delay intervals in tests
Previously, trying to test code thaat contained zero-delay intervals (e.g.
`$interval(fn, 0)` or `$interval(fn)`) would result in an infinite loop.
This commit avoids the infinite loop, by treating zero-delay intervals as one
second intervals (except for the initial trigger, where they can also be
executed with `$interval.flush(0)`).

Fixes #15952

Closes #15953
2017-06-06 13:36:08 +03:00
Georgios Kalpakas 9e8e3e187d chore(i18n): update CLDR to v30.0.1
Fixes #15976

Closes #15997
2017-06-06 13:17:08 +03:00
Georgios Kalpakas c6554433cf chore(i18n): fix parser for currency patterns without fraction digits
Previously, it was assumed that all currency pattern would have fraction digits.
However, in [closure-library@b9155d5][1] the `agq_CM` locale was modified to
have such a pattern (namely `#,##0\u00A4`).
This commit modifies the parser implementation to account for pattern without a
decimal point (and thus no fraction digits).

[1]: https://github.com/google/closure-library/commit/b9155d5966a228cb33f367c30c275c833b30e3ff#diff-02793124214ad0470ccea6f86b90d786R711
2017-06-06 13:17:07 +03:00
Georgios Kalpakas 43d2a75f4e chore(i18n): fix relative paths in scripts 2017-06-06 13:17:07 +03:00
Georgios Kalpakas 37d2b50812 test(i18n): remove bad assertion 2017-06-06 13:17:06 +03:00
Peter Bacon Darwin 8f31f1ff43 fix($sanitize): use appropriate inert document strategy for Firefox and Safari
Both Firefox and Safari are vulnerable to XSS if we use an inert document
created via `document.implementation.createHTMLDocument()`.

Now we check for those vulnerabilities and then use a DOMParser or XHR
strategy if needed.

Thanks to @cure53 for the heads up on this issue.
2017-06-05 21:03:55 +01:00
Michał Gołębiowski cf84fcf544 chore(package.json): backport package.json/yarn.lock changes from master 2017-06-05 11:33:28 +02:00
Michał Gołębiowski f63bc3cfde test(support): verify support tests results in all tested browsers (#16008)
Closes #16008
2017-05-30 20:21:15 +02:00
Jason Bedard a77943110e test($parse): reorganize $parse tests 2017-05-24 21:49:21 -07:00
Jason Bedard ec97686f2f fix($parse): always re-evaluate filters within literals when an input is an object
Fixes #15964
Closes #15990
2017-05-24 21:49:12 -07:00
Michał Gołębiowski 4ff7b7aa48 chore(testabilityPatch): fix a typo 2017-05-24 10:25:37 +02:00
Michał Gołębiowski 57b837bd5c test($log): run all $log tests in IE9 & non-IE9 logging mode (#15995)
In IE 9 console methods don't inherit from Function.prototype and, hence, don't
have apply. Until recently IE 9 logging in AngularJS was restricted to the
first 2 parameters but that changed as we could just reuse
Function.prototype.apply everywhere, creating one code path for all browsers.
Therefore, we can now run all tests in modes where apply exists on logging
methods and where it doesn't.

Ref #15911
Ref b277e3ead7
Closes #15995
2017-05-24 10:15:21 +02:00
Thomas Grainger 6daca023e4 fix(*): correctly detect Error instances from different contexts
Previously, errors thrown from different contexts (such as an iframe or
webworker) were not detected as `Error` instances and handled accordingly.
This commit fixes it by introducing an `isError()` helper, that is able to
correctly detect such instances.

Fixes #15868

Closes #15872
2017-05-22 14:09:52 +03:00
Sercan Eraslan 91b4eb0f69 docs(guide/component): remove redundant empty controller from example
There is no need for empty controller functions on components, since this is the
default.

Closes #16003
2017-05-22 11:51:40 +03:00
BobChao87 e50ed4da9e fix(orderBy): guarantee stable sort
If a user-provided comparator fails to differentiate between two items, fall
back to the built-in comparator (using the tie-breaker predicate).

Fixes #14881

Closes #15914
2017-05-19 14:41:41 +03:00
Georgios Kalpakas fe5dd1da8f docs(*): fix dangling links
Closes #15984
2017-05-12 18:13:10 +03:00
Georgios Kalpakas df4c03fa33 docs(form): improve the docs for FormController.$setValidity()
Fixes #15963
2017-05-12 18:13:10 +03:00
tsclaus 8d4e626326 docs(ngRepeat): fix argument name in comment to match actual argument (element --> clone)
Closes #15975
2017-05-11 11:41:28 +03:00
Jake Danforth 92aef5d456 docs(error/badrestrict): fix typo (of --> or)
Closes #15979
2017-05-11 11:35:51 +03:00
Michał Gołębiowski eed13cf732 test(*): run class-related tests everywhere; fix eval syntax
1. Wrap an evaled class definition in parens; previously they weren't; the test
   wasn't failing only because it was disabled everywhere outside of Chrome
   and Chrome <59 incorrectly accepted such input.
2. There's no reason to restrict class-related tests just to Chrome; now they
   run in every browser that supports ES6 classes. The classes support test
   was modified to check not only if a class definition parses but also if
   it stringifies correctly which is required by AngularJS. This restriction
   disables class-related tests in current Firefox (53) but will work in v55
   or newer.

Closes #15967
2017-05-09 15:12:00 +02:00
musclor e90200b4de docs(guide/component): remove redundant unit test
Fixes #15968
Closes #15974
2017-05-09 10:20:07 +03:00
Michał Gołębiowski 7f36ba77a0 chore(*): update all Karma-related packages except Karma
The updated karma-chrome-launcher adds support for ChromeHeadless &
ChromeCanaryHeadless launchers; test with:

    karma start karma-jqlite.conf.js --browsers=ChromeCanaryHeadless

The updated karma-firefox-launcher disables multi-process which may increase
stability on Jenkins.

Closes #15966
2017-05-06 17:52:19 +02:00
Valentin 457c58827b docs(guide/templates): add missing closing <script> tag
Closes #15961
2017-05-04 14:20:34 +03:00
Leonardo Souza aae768611f docs(guide/interpolation): fix typo in markdown (code highlight)
Closes #15935
2017-05-02 15:42:32 +03:00
Georgios Kalpakas ce5ffbf667 perf(animate): avoid unnecessary computations if animations are globally disabled
Closes #14914
2017-05-02 15:39:44 +03:00
Georgios Kalpakas ab114af850 feat($animate): add support for customFilter
This commit adds a new `customFilter()` function on `$animateProvider` (similar
to `classNameFilter()`), which can be used to filter animations (i.e. decide
whether they are allowed or not), based on the return value of a custom filter
function.
This allows to easily create arbitrarily complex rules for filtering animations,
such as allowing specific events only, or enabling animations on specific
subtrees of the DOM, etc.

Fixes #14891
2017-05-02 15:39:11 +03:00
Georgios Kalpakas 2759788737 perf($animate): do not retrieve className unless classNameFilter is used 2017-05-02 15:38:27 +03:00
Georgios Kalpakas c643323c17 docs(guide/animations): list missing animated directives (and other improvements)
- List missing animation-aware directives.
- Fix/Improve wording/formatting.
- Fix typos.
- Limit lines to 100 chars.
2017-05-02 15:38:04 +03:00
Dimitri Tarasiuk 9b97a033b0 docs($q): fix typo in qFactory documentation
Closes #15946
2017-04-28 12:22:00 +03:00
Georgios Kalpakas a3226d01fa fix(angular-loader): do not depend on "closure" globals that may not be available
Code that is distributed as part of both `angular.js` and `angular-loader.js`
should not depend on "closure" globals that may not be available in
`angular-loader`.

Fixes #15880

Closes #15881
2017-04-27 22:47:11 +03:00
Martin Staffa 510d0f946f fix(ngOptions): re-render after empty option has been removed 2017-04-27 21:40:59 +02:00
Martin Staffa 0b962d4881 feat(select): expose info about selection state in controller
This allows custom directives to manipulate the select's and
ngModel's behavior based on the state of the unknown and
the empty option.

Closes #13172
Closes #10127
2017-04-27 21:40:59 +02:00
Pol Bonastre 71b4daa4e1 fix(ngOptions): allow empty option to be removed and re-added
This bug was reported as part of angular/angular.js#15801
2017-04-27 21:40:58 +02:00
Martin Staffa e0b02a5040 docs(select): add known issue about Firefox selection behavior
Related to #9134
2017-04-27 21:40:58 +02:00
Martin Staffa a0dd9b0fdd refactor(select, ngOptions): extract common methods; make consistent 2017-04-27 21:40:32 +02:00
Martin Staffa 498bef199a chore(matchers): improve output for toBeMarkedAsSelected 2017-04-27 21:40:32 +02:00
Martin Staffa 17d34b7a98 fix(ngOptions): select unknown option if unmatched model does not match empty option
When a regular / ngOptions select has an explicit *empty* option, this option can be selected
by the user and will set the model to `null`. It is also selected when the model is set to
`null` or `undefined`.

When the model is set to a value that does not match any option value, and is also not
`null` or `undefined`, the *unknown* option is inserted and selected - this is an explicit marker
that the select is in an invalid / unknown state, which is different from an allowed empty state.

Previously, regular selects followed this logic, whereas ngOptions selects selected the empty
option in the case described above.

This patch makes the behavior consistent between regular / ngOptions select - the latter will now
insert and select the unknown option. The order of the options has been fixed to unknown -> empty
-> actual options.
2017-04-27 21:40:32 +02:00
Martin Staffa 72359fd097 test(select, ngOptions): add more tests for "required" with "empty" or "unknown" option 2017-04-27 21:40:32 +02:00
Georgios Kalpakas 72882190f2 chore(docs-app): fix vertical scrolling offset after recent re-design
Previously, the `yOffset` pointed to the `<header>` element, which had a height
of 0 (since all its children have fixed positions). This caused scrolled items
to have 0 vertical offset, essentially hiding the top part behind the static
`<nav>` items.
This commit fixes the vertical scrolling offset, by setting `yOffset` to the
last (lowest) `<nav>` item.

Closes #15945
2017-04-27 19:40:08 +03:00
Michał Gołębiowski fd2d8a5755 chore(*): make package.json & yarn.lock identical to master 2017-04-25 22:11:37 +02:00
Michał Gołębiowski fbe84f95a1 chore(browserstack): Update browserstacktunnel-wrapper, fix options
Only the latest version of the package works correctly (the backend for it at
BrowserStack is not versioned) and the options have changed in the new version
of the package.

Also, iOS 8.0 is no longer available on BrowserStack, only 8.3 is. Instead,
this commit changes it to 9.3 as we shouldn't be testing on 8 anymore anyway.

(a late cherry-pick of ad0bb83819)

Closes #15892
2017-04-25 21:46:01 +02:00
Michał Gołębiowski 3671a43be4 feat($log): log all parameters in IE 9, not just the first two.
IE 9 lacks apply on console methods but it's possible to borrow the apply
method from Function.prototype.
2017-04-25 21:38:30 +02:00
Michał Gołębiowski f6a1ad528d refactor(*): remove workarounds for IE <9, update IE/Edge-related comments 2017-04-25 21:34:26 +02:00
Jacob Hansson d9128e7b23 feat(ngMock): describe unflushed http requests
The current implementation of $httpBackend.verifyNoOutstandingRequest
gives an integer number describing how many requests are unflushed.

While it's superficially easy to solve test errors from that message
by simply adding an additional $httpBackend.flush(), if a developer
is truly not expecting the code to make further requests this is
not ideal.

This change explicitly prints out which additional requests remain
unflushed in the error message, helping her determine if the code
needs changing, or if an additional flush is appropriate.

Before this change:

    Unflushed requests: 1

After this change:

    Unflushed requests: 1
      GET /some

Closes #10596
Closes #15928
2017-04-21 13:49:05 +02:00
TheHalcyonSavant 0b54c1d4a9 docs(guide/migration): remove duplicate entry for commit 13c252
Closes #15919
2017-04-21 13:48:49 +02:00
Martin Staffa 608d623b55 docs($http): correct and clarify default transforms
- baddata error described incorrect http behavior, and workarounds
- httpProvider defaults were missing transformResponse / transformRequest
- http was not clear about JSON detection strategy

Closes #15897 
Closes #15906
2017-04-21 13:48:49 +02:00
Eli Sadoff 9f30bb5475 docs(filter/uppercase): add an example
I saw that the uppercase filter had no example so I decided to add a minimal example to explain how the uppercase filter works.

Thank you very much to @narretz for helping me through this process.

Closes #15885
2017-04-21 13:48:08 +02:00
Martin Staffa 182fb18f00 docs(filter/filter): remove duplicate 'the'
Closes #15893
2017-04-21 13:48:02 +02:00
Atef Ben Ali 3a9fdceeee docs(guide/directive): delete redundant 'the'
Closes #15891
2017-04-21 13:47:55 +02:00
Atul Shimpi ee4ac72170 docs(README): improve vocabulary and orthography
Closes #15876
Closes #15875
2017-04-21 13:47:48 +02:00
michaelb958 eb9fc571a0 docs(guide/i18n): fix links to CLDR
The old link target is dead, deceased, pushing up daisies. I quote:
> The cldr-tmp repository is no longer available.
> For access to CLDR sources and data, please see the [CLDR pages](link to new one).

Closes #15879
2017-04-02 16:13:59 +03:00
Martin Staffa b4d1e5e492 docs(changelog): add release notes for 1.6.4 2017-03-31 10:48:25 +02:00
Jason Bedard 60394a9d91 fix($parse): standardize one-time literal vs non-literal and interceptors
Previously literal one-time bindings did not use the expression `inputs`, causing infinite digest issues with literal values. This often forces the use of deepEquals when watching one-time literals.

`ng-class` is one example of deepEquals which is no longer required.

This one-time/literal behavior is now also consistently propogated through interceptors.

Closes #15858
2017-03-31 09:36:49 +02:00
Jason Bedard f5ddb10b56 fix($parse): fix infinite digest errors when watching objects with .valueOf in literals
Closes #15867
2017-03-31 09:36:46 +02:00
Martin Staffa 6a448d3459 docs($animate): remove obsolete error doc 2017-03-30 23:20:35 +02:00
Martin Staffa 6a8c0f5f4a docs(faq): clarify the versioning strategy
- When do breaking changes appear
- Relationship with Semver
- Compatibility of modules

Closes #15845
2017-03-30 23:19:05 +02:00
Jason Bedard 584308fc06 refactor($parse): move duplicate $parse interpreter/compiler logic into Parser
- the construction of the AST is now in the Parser
- the assigning of the literal and constant flags is now in the Parser
- remove unused references to the lexer, $filter and options on the Parser
2017-03-30 23:09:51 +02:00
Jason Bedard 48ad2c44bf refactor($compile): reuse shared simpleCompare method 2017-03-30 23:09:39 +02:00
Jason Bedard 100998330a refactor($parse): make use of local variable instead of refetching property 2017-03-30 23:09:32 +02:00
Michał Gołębiowski b9e85c62be docs($compile): fix a typo in a deprecation warning 2017-03-29 14:15:02 +02:00
Michał Gołębiowski 77fad099d2 docs($compile): update a mention of preassigning bindings in controllers
The deprecation warning claimed the bindings are preassigned in controllers
by default which is not the case in 1.6.

Ref #15870
2017-03-29 14:01:43 +02:00
Jason Bedard e1f8a6e82b fix(ngModel): prevent internal scope reference from being copied
Fixes #15833
2017-03-27 22:45:46 -07:00
Jason Bedard 132d767647 refactor(ngModel): use local scope param in watcher 2017-03-27 22:45:46 -07:00
Michał Gołębiowski 9cde98cbc7 fix(jqLite): make jqLite invoke jqLite.cleanData as a method
The previous implementation of jqLite didn't use cleanData from the jqLite
object but instead used a cached version which maede it impossible to
monkey-patch jqLite.cleanData similarly to how you can do it in jQuery.

The cleanData method is not meant to be called directly by userland code;
its purpose is mainly to be able to be monkey-patched; therefore, the previous
implementation didn't make a lot of sense.

This commit enables one of the tests so far run only with jQuery to run with
jqLite as well.

(cherry-picked from bf5c2eef34)

Ref #8486
Ref #8695
Closes #15846
2017-03-27 14:05:43 +02:00
Michał Gołębiowski 1ddbb3ec3e chore(*): make ESLint happy 2017-03-27 13:58:38 +02:00
Michał Gołębiowski 34f40266b2 test(jqLite): test not firing $destroy on jqLite.cleanData with jQuery UI
So far it wasn't tested that Angular's logic for skipping it triggering
the $destroy event on jQuery.cleanData in the replaceWith internal function
works correctly when Angular is not the last one to patch the cleanData method
(e.g. if jQuery UI does the patching later). This commits adds the relevant
test.

(cherry-picked from bf7685abbd)

Ref #8486
2017-03-27 13:17:45 +02:00
Michał Gołębiowski b8c06e3f1b test(jQuery): run tests with jQuery 2.1, 2.2 & 3.2
Also, update jQuery 2.2.x mentions in the tutorial to 3.2.x.

Closes #15843
2017-03-27 11:25:29 +02:00
Peter Mertz a6cf648b3c docs($interval): Update interval promise docs
It's currently not clear when or why the promise returned by `$interval` resolves. This updates the docs to be more specific.

Closes #15862
2017-03-25 18:12:51 +02:00
Rodrigo aebde27f1b docs(filterFilter): clarify the comparator parameter
Closes #15827
2017-03-23 16:51:50 +02:00
Michał Gołębiowski c9be327d53 chore(yarn): rely on Travis built-in Yarn support, update Yarn in Jenkins
On Travis we now rely on built-in Yarn support and we only cache the Yarn cache,
not node_modules. This creates a more stable environment as we don't install
over previous node_modules state but we still won't download packages from the
internet in the second run for the same yarn.lock as Yarn takes packages from
its local cache if they exist there.

We install a new Yarn verison manually on Jenkins; the location of the install
script changed.

Closes #15851
2017-03-22 22:51:02 +01:00
Michał Gołębiowski 3dc7d22d90 chore($parse): make sure no one changes .toEqual(-0) to .toBe(-0) 2017-03-22 12:09:00 +01:00
Michał Gołębiowski c10c6cac74 docs(faq): document the AngularJS/jqLite deprecation strategy
Fixes #15282
2017-03-22 11:59:10 +01:00
Joshua J Wilborn cee2c4c569 docs ($compile): add error documentation for noslot error in $compile
Fixes #15790
Closes  #15828
2017-03-22 11:59:05 +01:00
Martin Staffa a5160c82dc docs($compile): add deprecation notice for preAssignBindingsEnabled 2017-03-22 11:56:33 +01:00
Martin Staffa 8853312197 docs(component-router): give deprecation notice red color 2017-03-22 11:56:33 +01:00
Raphael Jamet 049b24de21 docs($sce): overhaul the $sce service documentation
A big docs update around `$sce`:
There is a lot of content in there that is often misunderstood, and some of the
documentation starts to get really old too. Also fixed capitalization,
formatting, indentation and uniformized `@param` descriptions.

Closes #15735
2017-03-21 17:56:38 +02:00
Richard Kaufhold a9f987a0c9 feat($resource): add hasBody action configuration option
By default, only `PUT`, `POST` and `PATCH` requests have a body, but you can use
`hasBody` to configure any action to either have or not have a body, regardless
of its HTTP method.

Fixes #10128

Closes #12181
2017-03-21 12:45:46 +02:00
Georgios Kalpakas ad4a20d3d2 docs($sanitize): fix incorrect test description 2017-03-20 16:26:56 +02:00
BobChao87 b3972d1b65 docs($resource): encode ) in link
JSDoc to HTML converter was treating the close parenthesis in
`[MDN](...#toJson()_behavior)` as the final close parenthesis, thus resulting in
a broken link.
This commit fixes it by percent-encoding the parentesis in the link address.

Closes #15825
2017-03-20 12:53:38 +02:00
xfg c3e0d58b3c docs(ngMock/$httpBackend): add catch() block to example
Make the `it should fail authentication` test pass.

Closes #15822
2017-03-20 12:46:50 +02:00
TheRealMaxion f08156ea9b docs(tutorial/step_09) fix typo
Closes #15829
2017-03-20 12:33:53 +02:00
TheRealMaxion 4bacf5a5da docs(tutorial/step_04): fix typo (each --> its)
Closes #15826
2017-03-20 12:29:20 +02:00
Chirag Bhatia df88873bb7 fix($http): throw more informative error on invalid JSON response
Fixes #15695

Closes #15724
2017-03-14 14:49:24 +02:00
eeeqxxtg c4b1c5e8f1 docs(changelog): fix typo (resourceUrlWhiteList --> resourceUrlWhitelist)
Closes #15809
2017-03-14 11:30:53 +02:00
Ash Searle 9822711ad2 fix(dateFilter): correctly handle newlines in format string
Fixes #15794

Closes #15792
2017-03-13 20:23:02 +02:00
Martin Staffa 30cd764b6d docs(changelog): move bootstrap fixes to Bug Fix section 2017-03-08 12:44:24 +01:00
Martin Staffa 38b75cdb2d docs(changelog): add release notes for 1.6.3 and 1.6.2 2017-03-08 12:27:00 +01:00
Pablo Targa 6cb8b39af8 docs(ngAnimate): update staggering config for use with css animations
Closes #15743
2017-03-07 20:25:09 +01:00
diegomrsantos ebaa336614 docs(guide/migration): add info for 1.4 (ng)Pattern BC
Breaking change was introduced in commit 0e001084ff.
This content being included in the migration guide is taken from the commit message of commit 0e001084ff.

Closes #15758
Closes #15765
2017-03-07 20:24:59 +01:00
mohamed amr ef5f567f91 test(errorHandlingConfig): add tests for errorHandlingConfig() (independent of minErr)
Closes #15770
2017-03-05 00:46:14 +02:00
Michał Gołębiowski 64e5afc478 fix($log): don't parse error stacks manually outside of IE/Edge
IE/Edge display errors in such a way that it requires the user to click in
4 places to see the stack trace. There is no way to feature-detect it so
there's a chance of the user agent sniffing to go wrong but since it's only
about logging, this shouldn't break apps. Other browsers display errors in
a sensible way and some of them map stack traces along source maps if available
so it makes sense to let browsers display it as they want.

Fixes #15590
Closes #15767
2017-03-03 11:41:02 +01:00
Peter Bacon Darwin 1e582e4fa4 feat(info): add angularVersion info to each module
You can now check what version of AngularJS a core module is designed for:

```
var angularVersion = $injector.modules['myModule'].info().angularVersion;
```
2017-03-02 08:25:16 +00:00
Peter Bacon Darwin 7421235f24 feat($injector): add new modules property
The `modules` property is a hash of the modules loaded into the injector
at bootstrap time. This can be used to access the module's info.
2017-03-02 08:25:09 +00:00
Peter Bacon Darwin 09ba69078d feat(Module): add info() method
The new `info()` method lets developers store arbitrary information about
their module for consumption later.

Closes #15225
2017-03-02 08:25:01 +00:00
Peter Bacon Darwin 3536e83d8a fix(Angular): do not autobootstrap if the src exists but is empty
In Chrome an empty `src` attribute will be ignored, but in Firefox it seems
happy to prepend the `base[href]` and try to load whatever that is.
2017-02-27 20:47:04 +00:00
Georgios Kalpakas 3bb1dd5d7f fix($sanitize): prevent clobbered elements from freezing the browser
Closes #15699
2017-02-24 17:42:14 +00:00
Peter Bacon Darwin 95f964b827 fix(Angular): do not auto bootstrap if the currentScript has been clobbered 2017-02-24 15:04:10 +00:00
Peter Bacon Darwin c8f78a8ca9 fix(Angular): do not auto bootstrap if the script source is bad and inside SVG 2017-02-24 15:04:03 +00:00
Peter Bacon Darwin f34d48087b test(Angular): refactor auto bootstrap tests 2017-02-24 15:03:56 +00:00
Martin Staffa 2c9ecd01b1 docs($compile): clarify to which element scope isolation applies
Closes #13556
2017-02-22 21:08:52 +01:00
Georgios Kalpakas a584fb6e15 fix($animate): reset classNameFilter to null when a disallowed RegExp is used
Closes #14913
2017-02-17 11:33:13 +02:00
Georgios Kalpakas 1f13313f40 fix($animate): improve detection on ng-animate in classNameFilter RegExp
Fixes #14806
2017-02-17 11:33:12 +02:00
Martin Staffa 5b60303781 chore(docs-app): add debounce to search input
This fixes issues where the search results do not correctly reflect
the search query. This happens in Firefox when you enter a search query
very rapidly.
There is probably an issue with the async behavior of the search / webworker,
so this is just a workaround.
2017-02-16 14:28:08 +01:00
Martin Staffa 10e2552a7d chore(docs-app): update links in header menu
They are now in the same order as angularjs.org

Closes #14351
2017-02-16 14:28:08 +01:00
Martin Staffa ef48b0aa55 chore(docs-app): update the header style
Also adds a new fixed strip / bar about the AngularJS version

Closes #14963
Closes #15670
2017-02-16 14:28:08 +01:00
Keith Walsh f57872bca0 docs($resource): add minor clarification
Closes #15711
2017-02-16 12:00:45 +02:00
Martin Staffa 2deaf2877e docs(select, ngOptions): add ngAttrSize as argument
Closes #1619
2017-02-15 15:28:08 +01:00
Martin Staffa 7a146c9cd5 docs(ngModelController): improve $formatters and $parsers info
Closes #11714
Closes #8194
2017-02-15 13:05:44 +01:00
Martin Staffa 2796ec172b docs(filterFilter): add note about self-referencing objects in array
Relate #6655, #6319
2017-02-15 12:21:58 +01:00
Martin Staffa 6997c1bf0c docs(guide/directive): clarify which type of matching directives support
Closes #15710
2017-02-15 12:21:22 +01:00
Georgii Dolzhykov 4a030f3834 refactor($rootScope): remove extraneous call to $parse in $evalAsync
Closes #15682
2017-02-09 23:39:56 +02:00
Michał Gołębiowski f78d8b8ff3 chore(jenkins): get rid of Opera from the Jenkins build script
The Opera launcher hasn't been installed for ages, but until Karma 1.4.0 the
error of Opera not being able to start was ignored. Karma has fixed the bug and
now Jenkins is failing.

This commit also removes Opera/Opera launcher mentions from the docs. We don't
support Opera officially anymore (it's sort-of supported via being based on
Blink).

Closes #15691
2017-02-09 12:59:43 +02:00
PRIJCK Frederik (FPRJ) f27d19ed60 fix(filterFilter): don't throw if key.charAt is not a function
Previously, when an object has keys which are not of type string, `filterFilter`
would throw an exception for trying to call `key.charAt()`, which is a string
method.

This commit checks whether `charAt` is defined before calling it.

Fixes #15644

Closes #15660
2017-02-08 23:18:01 +02:00
mohamed amr 4a5eaf7bec feat(errorHandlingConfig): make the depth for object stringification in errors configurable
Closes #15402
Closes #15433
2017-02-08 19:06:17 +02:00
Martin Staffa 8513674911 fix(select): add attribute "selected" for select[multiple]
This helps screen readers identify the selected options,
see #14419
2017-02-08 17:46:24 +01:00
Martin Staffa 97b74ad6fb fix(select): keep original selection when using shift to add options in IE/Edge
In IE9-11 + Edge, the selected options were previously incorrect under the following
circumstances:
- at least two options are selected
- shift+click or shift+down/up is used to add to the selection (any number of options)

In these cases, only the last of the previously selected options and the newly selected
options would be selected.

The problems seems to be that the render engine gets confused when an option that
already has selected = true gets selected = true set again.

Note that this is not testable via unit test because it's not possible to simulate
click / keyboard events on option elements (the events are delegated to the select element
change event), and the problem also doesn't appear when modifying the option elements directly
and then using the selectController API. It seems that this only happens when you manipulate the
select directly in the user interface.

Fixes #15675
Closes #15676
2017-02-08 17:46:21 +01:00
Martin Staffa a47ea79023 test($http): ensure json deserialization errors are forwarded to error handler
Since https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9,
errors thrown from onFulfilled and onRejected handlers are passed to the regular http
error handlers. Before this, JSON deserialization errors lead to hard application errors, and could
not be handled by application code. This behavior was introduced in https://github.com/angular/angular.js/commit/7b6c1d08aceba6704a40302f373400aed9ed0e0b, and originally, a malformed JSON string was forwarded
as the data to the http success response handler.

This commit adds a specifc test case, even though the behavior is unlikely to break in the future without
a change in the $q rejection handling.

Related #11433
Closes #15689
2017-02-08 17:46:17 +01:00
Matt Lewis 5ca0de6487 fix($jsonpCallbacks): allow $window to be mocked in unit tests
Fixes #15685

Closes #15686
2017-02-08 18:35:55 +02:00
Georgios Kalpakas 50a449f053 chore(*): update changez-angular 2017-02-08 18:35:12 +02:00
Georgios Kalpakas d7422da7d7 test($resource): fix broken test
(Introduced while "cleaning up" the tests for in edfb691.)
2017-02-05 17:58:25 +02:00
Georgios Kalpakas c7cbc978c6 refactor(*): remove ignored expensiveChecks argument passed to $parse()
This is a follow-up to #15094.

Closes #15680
2017-02-05 15:39:10 +02:00
Kyle Wuolle 27146e8a7f fix($resource): do not swallow errors in success callback
Previously, errors thrown inside the `success` callback would be swallowed by a
noop `catch()` handler. The `catch()` handler was added in order to avoid an
unnecessary "Possibly Unhandled Rejection" error, in case the user provided an
`error` callback (which would handle request errors).

The handler was added too "eagrly" and as a result would swallow errors thrown
in the `success` callback, despite the fact that those errors would _not_ be
handled by the `error` callback.

This commit fixes this, by adding the `catch()` handler "lazily", only when it
is certain that a rejection will be handled by the `error` callback.

Fixes #15624

Closes #15628
2017-02-04 21:01:22 +02:00
Kindy Lin 5e418b1145 fix($parse): make sure ES6 object computed properties are watched
Add the missing watches for ES6 object computed properties which were
implemented in #14407.

Closes #15678
2017-02-04 16:18:21 +02:00
Dimitris Vardoulakis f4bb973eb7 refactor(*): avoid non-existent property warnings from Closure Compiler
Closes #15672
2017-02-02 22:38:27 +02:00
Chris 848857aa5b docs(misc/started): update Twitter handle (@angularjs --> @angular)
Closes #15671
2017-02-02 19:30:48 +02:00
Jessica Soltero ee8a05d3f1 docs(guide/expression): typo in one-time-binding
Closes #15668
2017-02-02 12:45:58 +02:00
Michał Gołębiowski 275ebbf0ec refactor($injector): remove the Chrome stringification hack
The Chrome stringification hack added in afcedff34c
is no longer needed. I verified that both of the commented out tests pass
on Chrome 56.
2017-02-01 15:02:52 +00:00
Michał Gołębiowski 0f23df4c06 chore(anchorScroll): remove a Jasmine toHaveBeenCalled workaround
The Jasmine fix landed long time ago and we've updated Jasmine since that
happened.
2017-02-01 13:36:20 +00:00
Michał Gołębiowski 11f700f7bd docs($location): fix examples
The examples contained tests with assertions in form of regular equality
comparisons which would be noops and in case of an error nothing would get
reported. Also, one of the test mixed a HTML5 browser scenario with a non-HTML5
one.
2017-02-01 13:35:15 +00:00
Michał Gołębiowski 5785f2a991 docs($animation): fix weird spaces around colons 2017-02-01 13:29:28 +00:00
Peter Bacon Darwin 2546c29f81 feat(ngModel): add $overrideModelOptions support
This change allows developers to modify the model options for an `ngModel`
directive programmatically.

Closes #15415
2017-02-01 12:20:41 +00:00
Patrick McElhaney 19ea708c9d docs(guide/component): add replace option
Add `replace` to the table comparing components to directives options. The
`replace` option is deprecated, but it is still documented for directives, so
it is worth pointing it out as a difference between directives and components.

Closes #15658
2017-01-31 19:37:47 +02:00
Frederik Prijck 5cf05d67f2 chore(doc-gen): show arguments as a subsection of the usage section
Previously, on the docs of directives which include the `animation` section, `arguments` are shown as an `h3` element below the `animation` `h2` element, making it look like it's a subsection of `animations`.

This commit ensures that the àrgument` `h3`element is rendered correctly after the `usage` `h2` element.

Fixes #15645
Closes #15646
2017-01-30 19:26:27 +02:00
Georgios Kalpakas 0377c6f0e8 fix($compile): do not swallow thrown errors in test
In e13eeab, errors/rejections produced during fetching the template or compiling
an asynchronous directive, where overzealously silenced. This doesn't make any
difference in (most) production apps, where `$exceptionHandler` does not rethrow
the errors. In tests though (where `$exceptionHandler` rethrows by default), it
can unexpectedly "swallow" thrown errors.

This commit fixes it by removing the extraneous `.catch(noop)`, thus letting
errors thrown by `$exceptionHandler` to surface.

The changes in 'compileSpec.js' essentially revert the modifications that were
unnecessarily (and incorrectly) done in e13eeab (and also one incorrect
modification from [c22615c][1]).

[1]: https://github.com/angular/angular.js/commit/c22615cbfbaa7d1712e79b6bf2ace6eb41313bac#diff-348c2f3781ed66a24894c2046a52c628L2084

Fixes #15629

Closes #15631
2017-01-30 19:25:06 +02:00
Peter Bacon Darwin 9c13866824 chore(docs): don't use bower for docs dependencies 2017-01-30 14:10:25 +00:00
vteremasov 419a4813e3 fix($resource): correctly unescape /\. even if \. comes from a param value
Closes #15627
2017-01-27 18:20:11 +02:00
Martin Staffa 131af8272d fix(select): keep ngModel when selected option is recreated by ngRepeat
Fixes #15630 
Closes #15632
2017-01-27 16:14:18 +01:00
Martin Staffa c219a46f59 docs(ngDisabled): list some elements that natively support
Closes #15473
2017-01-27 16:14:06 +01:00
Jason Bedard 25f008f541 feat($parse): allow watching array/object of inputs to literal values
The inputs of array/object literals are now watched for changes.
If the an input changes then a new instance of the literal will be
provided when the parsed expression is executed.

Closes #15301
2017-01-27 10:06:31 +00:00
frederikprijck 4a593db79b fix($sniffer): allow history for NW.js apps
Previously `$sniffer` incorrectly detected NW.js apps as Chrome Packaged Apps,
disallowing them to use the history API.

This commit correctly detects NW.js apps and allows them to use the History API.

Fixes #15474

Closes #15633
2017-01-27 00:16:54 +02:00
Georgios Kalpakas ad4fef0431 refactor(*): replace HashMap with NgMap
For the time being, we will be using `NgMap`, which is an API-compatible
implementation of native `Map` (for the features required in Angular). This will
make it easy to switch to using the native implementations, once they become
more stable.

Note:
At the moment some native implementations are still buggy (often in subtle ways)
and can cause hard-to-debug failures.)

Closes #15483
2017-01-25 23:57:16 +02:00
Georgios Kalpakas 8a15fcc1f5 test(hashKey): add tests for hashKey() 2017-01-25 23:57:15 +02:00
Georgios Kalpakas f01212ab52 fix(ngAnimate): correctly animate transcluded clones with templateUrl
Previously, `$animate` would decide whether an animation should be cancelled
based on some assumption that didn't hold in specific cases (e.g. when animating
transcluded clones with `templateUrl` directives on them for the first time). As
a result, the entering elements would not be animated in such cases. This
affected commonly used, structural built-in directives (`ngIf`, `ngRepeat`,
`ngSwitch` etc).
This commit fixes it by avoiding invalid assumptions (i.e. by taking into
account the transformations that take place while compiling such elements).

Partly addresses #14074 and #14124.

Fixes #15510

Closes #15514
2017-01-24 23:56:04 +02:00
Georgios Kalpakas 28693a1a67 test(ngAnimate): make expectations more specific 2017-01-24 23:56:04 +02:00
Georgios Kalpakas 29fd499552 refactor(ngAnimate): simplify functions and remove redundant args/calls
Simplifies/Optimizes the following functions:

- `areAnimationsAllowed()`
- `cleanupEventListeners()`
- `closeChildAnimations()`
- `clearElementAnimationState()`
- `markElementAnimationState()`
- `findCallbacks()`

Although not its primary aim, this commit also offers a small performance boost
to animations (~5% as measured with the `animation-bp` benchmark).
2017-01-24 23:56:03 +02:00
Georgios Kalpakas 2f97d9d647 chore(benchmarks): add basic animation benchmark 2017-01-24 23:56:03 +02:00
Georgios Kalpakas 4146b38459 docs(*): document the breaking change in 7ceb5f6 2017-01-20 22:51:21 +02:00
frederikprijck 05aab660ce fix(ngValue): correctly update the value property when value is undefined
Previously, when the expression evaluated to `undefined` the `value` property
was not updated. This happened because jqLite/jQuery's `prop(_, undefined)` is
treated as a getter, thus not apdating the property.

This commit fixes it by setting the property to `null` instead.
(On IE9 we use `''` - see inline comments for more info.)

Fixes #15603

Closes #15605
2017-01-19 14:45:28 +02:00
Peter Bacon Darwin 5ecb64849e chore(package): relax yarn version constraint 2017-01-19 09:26:25 +00:00
Martin Brown 59dfe1b5a0 docs(guide/i18n): fix typos
Closes #15616
2017-01-17 18:18:48 +02:00
Georgios Kalpakas 50ebfb735c docs(changelog): update with changes for 1.5.11 2017-01-13 01:13:18 +02:00
Georgios Kalpakas 0bdbfe5069 style($compile): remove trailing whitespace 2017-01-12 23:12:44 +02:00
Grace Benz 3fc4d6028c docs($compile): add some detail about $onChanges
Closes #15604
2017-01-12 22:42:50 +02:00
Georgios Kalpakas 2eb12a052b test(e2e): make test less flakey-prone 2017-01-12 22:25:47 +02:00
Peter Bacon Darwin bd63b2235c fix(ngMockE2E): ensure that mocked $httpBackend uses correct $browser
The fix from #13124 enabled ngMock and ngMockE2E to work together but
did it in a way that meant that the "real" `$httpBackend` service that
was used in pass-through depended upon a different `$browser` service
to the rest of the app.

This broke Protractor since it watches the `$browser` for outstanding
requests and the pass through requests were being tracked by the wrong
`$browser` instance.

Closes #15593
2017-01-12 10:59:26 +00:00
Georgios Kalpakas f418ffd083 revert: fix($sce): consider document base URL in 'self' URL policy
This reverts commit cce98ff53a.
Reverting while investigating security implications of cce98ff without #15597
(which is possibly a breaking change, thus not suitable for this branch).
2017-01-12 11:24:10 +02:00
Georgios Kalpakas 6ab5f8ce4b chore(*): fix yarn.lock
(It turns out cherry-picking it from master wasn't a great idea :/)
2017-01-12 01:08:55 +02:00
Georgios Kalpakas becfeb5aa3 chore(*): update dgeni-packages (and other devDependencies)
`dgeni-packages` prior to version 0.16.3 specified `engine.yarn: '^0.17.9'`,
which was unnecessarily strict and would cause any task to fail if someone had a
yarn version >=0.18.0.
Other devDependencies were also updated (because why not).

Closes #15600
2017-01-12 01:02:08 +02:00
Sammy Jelin eb968c4a68 fix($route): make asynchronous tasks count as pending requests
Protractor users were having a problem where if they had asynchonous code in a
`route.resolve` or `route.resolveRedirectTo` variable, Protractor was not
waiting for that code to complete before continuing. See
https://github.com/angular/protractor/issues/789#issuecomment-190983200 for
details.

This commit fixes it by ensuring that `$browser#outstandingRequestCount` is
properly increased/decreased while `$route` (asynchronously) processes a route.

Also, enhanced `ngMock` to wait for pending requests, before calling callbacks
from `$browser.notifyWhenNoOutstandingRequests()`.

Related to angular/protractor#789.

Closes #14159
2017-01-12 00:15:20 +02:00
frederikprijck 7f2af3f923 fix($compile): allow the usage of "$" in isolate scope property alias
Previously, when using an alias for an isolate scope or `bindings` property
(e.g. `alias: '<attrName'` instead of `attrName: '<'`), a `$compile:iscp` error
was thrown if the attribute name contained a "$".
This commit removes the error by changing the regex to allow "$" characters in
the attribute name when using a property alias.

Fixes: #15586

Closes #15594
2017-01-11 11:46:31 +02:00
Alex Dobkin cce98ff53a fix($sce): consider document base URL in 'self' URL policy
Page authors can use the `<base>` tag in HTML to specify URL to use as a base
when resovling relative URLs. This can cause SCE to reject relative URLs on the
page, because they fail the same-origin test.

To improve compatibility with the `<base>` tag, this commit changes the logic
for matching URLs to the 'self' policy to allow URLs that match the protocol and
domain of the base URL in addition to URLs that match the loading origin.

**Security Note:**
If an attacker can inject a `<base>` tag into the page, they can circumvent SCE
protections. However, injecting a `<base>` tag typically requires the ability to
inject arbitrary HTML into the page, which is a more serious vulnerabilty than
bypassing SCE.

Fixes #15144

Closes #15145
2017-01-10 14:51:09 +02:00
Georgios Kalpakas b607618342 fix($location): correctly handle external URL change during $digest
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This was an issue when calling `history.pushState()/replaceState()` in all
browsers, since these methods do not emit any event. This was also an issue when
setting `location.href` in IE11, where (unlike other browsers) no `popstate`
event is fired at all for hash-only changes ([known bug][1]) and the
`hashchange` event is fired asynchronously (which is too late).

This commit fixes both usecases by:

1. Keeping track of `$location` setter methods being called and only processing
   a URL change if it originated from such a call. If there is a URL difference
   but no setter method has been called, this means that the browser URL/history
   has been updated directly and the change hasn't yet been propagated to
   `$location` (e.g. due to no event being fired synchronously or at all).
2. Checking for URL/state changes at the end of the `$digest`, in order to
   detect changes via `history` methods (that took place during the `$digest`).

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes #11075
Fixes #12571
Fixes #15556

Closes #15561
2017-01-10 00:03:10 +02:00
Georgios Kalpakas fa50fbaf57 fix(*): detect external changes in history.state
Previously, `$browser.$$checkUrlChange()` (which was run before each `$digest`)
would only detect an external change (i.e. not via `$location`) to the browser
URL. External changes to `history.state` would not be detected and propagated to
`$location`.

This would not be a problem if changes were followed by a `popstate` or
`hashchange` event (which would call `cacheStateAndFireUrlChange()`). But since
`history.pushState()/replaceState()` do not fire any events, calling these
methods manually would result in `$location` getting out-of-sync with the actual
history state.

This was not detected in tests, because the mocked `window.history` would
incorrectly trigger `popstate` when calling `pushState()/replaceState()`, which
"covered" the bug.

This commit fixes it by always calling `cacheState()`, before looking for and
propagating a URL/state change.
2017-01-10 00:03:09 +02:00
David Jöch f135e2dc05 style($log): fix indentation
Closes #15579
2017-01-05 13:02:59 +02:00
Florian Berger 780351db5e docs(*): fix typos
Closes #15577
2017-01-05 11:10:52 +02:00
Nic Mitchell a50bb0bfec chore(*): update copyright year
Closes #15573
2017-01-04 12:21:08 +02:00
Georgios Kalpakas 4d86df6f48 docs(guide/$location): correctly format heading 2017-01-03 22:37:17 +02:00
Georgios Kalpakas bb464d16b4 fix(angularInit): allow auto-bootstraping from inline script
Some browsers (e.g. Safari 9.x, PhantomJS) do not set `link.origin/protocol`
correctly, when setting `link.href` to `null`, which prevented auto-bootstraping
Angular from scripts without a `src` attribute (i.e. inline scripts).
Inline scripts are on the same origin as the loading page, so auto-bootstraping
should be allowed.

Fixes #15567

Closes #15571
2017-01-03 19:23:43 +02:00
Soumya Ranjan Mohanty 85b2eb1472 docs(guide/services): fix syntax for Jasmine v2.x
Closes #15570
2017-01-03 16:14:32 +02:00
Georgios Kalpakas 7608f92c6a docs(ngShow/ngHide): improve docs and fix inconsistencies between ngShow/ngHide
Closes #15471
2017-01-03 12:55:38 +02:00
Deco c0bf8db63c docs(tutorial/step_04): fix typo
Closes #15562
2016-12-31 12:32:22 +02:00
Georgios Kalpakas c95a6737fb fix(input): fix step validation for input[type=number/range]
Previously, the validation would incorrectly fail in certain cases (e.g.
`step: 0.01`, `value: 1.16 or 20.1`), due to Floating Point Arithmetic
limitations. The previous fix for FPA limitations (081d06ff) tried to solve the
issue by converting the numbers to integers, before doing the actual
calculation, but it failed to account for cases where the conversion itself
returned non-integer values (again due to FPA limitations).
This commit fixes it by ensuring that the values used in the final calculation
are always integers.

Fixes #15504

Closes #15506
2016-12-29 10:18:32 +02:00
Georgios Kalpakas cd43d24402 docs(CHANGELOG.md): add missing commas 2016-12-29 10:09:04 +02:00
supasak 086c5d0354 fix($resource): delete $cancelRequest() in toJSON()
Closes #15244
2016-12-29 10:01:11 +02:00
Peter Bacon Darwin 2a2ac5f53a docs(CHANGELOG): add 1.6.1 release info 2016-12-23 10:38:58 +00:00
Peter Bacon Darwin 21deaf637a chore(deps): update changez-angular version 2016-12-23 10:38:48 +00:00
Naomi Black 72e15a3a83 docs(guide/forms): remove implicit bias from example
Closes #15543
2016-12-23 11:43:47 +02:00
Thomas Grainger 174cb4a8c8 fix($q): Add traceback to unhandled promise rejections
Fixes #14631
Closes #15527
2016-12-21 20:12:29 +01:00
sp00m 33f769b0a1 fix($$cookieReader): correctly handle forbidden access to document.cookie
In certain cases (e.g. on LG webOS using the `file:` protocol), access to
`document.cookie` may not be allowed and throw an error. This could break
`$http` which relies on `$$cookieReader()` for retrieving the XSRF token.
This commit fixes it by treating `document.cookie` as empty, when access to it
is fordibben.

Fixes  #15523

Closes #15532
2016-12-20 23:34:07 +02:00
Simon Legner c8abf20558 docs(CHANGELOG): fix typo
Closes #15529
2016-12-20 10:33:14 +02:00
Thomas Grainger 6dbb183e75 docs(guide/migration): improve grammar
Closes #15526
2016-12-20 01:17:19 +02:00
Georgios Kalpakas bc4844d3b2 fix(ngOptions): do not unset the selected property unless necessary
Previously, when updating the value of a `select[multiple]` element, all options
were first set to `selected = false` and then the selected ones were set to
`true`. By setting an already selected option to `selected = false` and then
`true` again - essentially unselecting and reselecting it - caused some browsers
(including Firefox, IE and under some circumstances Chrome) to unexpectedly
scroll to the last selected option.

This commit fixes it by ensuring that the `selected` property is only set if its
current value is different than the new one and even then it is set to its final
value at once (i.e. without first setting it to `false`), thus avoiding the
undesirable behavior.

Fixes #15477

Closes #15478
2016-12-19 22:52:18 +02:00
Peter Neave 708f8b47de docs(tutorial/step_13): add missing dependency phoneDetails module
Closes #15521
2016-12-19 21:09:21 +02:00
Georgios Kalpakas 5518126d42 docs(CHANGELOG.md): remove reverted bug fix
Related to 02f045b.
2016-12-16 11:16:08 +02:00
Peter Bacon Darwin 5fe73fdc3a docs(CHANGELOG): add note to 1.5.0-beta.1 2016-12-16 11:16:08 +02:00
Georgios Kalpakas 394c496bf2 docs(CHANGELOG.md): add changes for v1.5.10 2016-12-15 19:29:37 +02:00
Georgios Kalpakas 8e1aeba715 docs(CHANGELOG.md): add missing entries for v1.6.0-rc.0/v1.6.0 2016-12-15 19:29:06 +02:00
Georgios Kalpakas 0e6e7eb477 docs($q): document the default value for errorOnUnhandledRejections 2016-12-14 00:44:36 +02:00
Jannick Fahlbusch 183f636816 docs($interval): improve fn description
If no additional arguments are passed, the function is called with the current iteration count.

Closes #15503
2016-12-13 14:16:20 +02:00
Georgios Kalpakas 5f8ed63f2a fix(ngModelOptions): work correctly when on the template of replace directives
Previously, in order for `ngModel` and `ngModelOptions` to work correctly
together, the latter's pre-linking function should be run before the former's
pre-linking function. This was typically what happened, except when `ngModel`
was used on an element which also had a `replace` directive, whose template
included `ngModelOptions`. In that case, the order was reversed.

This commit fixes it by moving the initialization logic of `ngModelOptions` from
its pre-linking function to its controller's `$onInit()` lifecycle hook.

Fixes #15492

Closes #15493
2016-12-13 00:12:34 +02:00
Georgios Kalpakas e4f3c94e31 refactor(testabilityPatch): remove code duplication 2016-12-13 00:11:18 +02:00
Aaron Brewer 1e5cbcbd93 docs(ngMessageExp): improve description
Closes #15486
2016-12-11 21:01:57 +02:00
idhindsight dcf3ec160f docs(tutorial/step_09): fix typo (it's --> its)
Closes #15485
2016-12-10 22:27:59 +02:00
David Rodenas Pico a7beb5b6d3 chore(benchpress): add an ngClass benchmark
Closes #15243
2016-12-09 12:21:21 +02:00
Georgios Kalpakas 1d3b65adc2 perf(ngClass): avoid unnecessary .data() accesses, deep-watching and copies
Includes the following commits (see #15246 for details):

- **perf(ngClass): only access the element's `data` once**

- **refactor(ngClass): simplify conditions**

- **refactor(ngClass): move helper functions outside the closure**

- **refactor(ngClass): exit `arrayDifference()` early if an input is empty**

- **perf(ngClass): avoid deep-watching (if possible) and unnecessary copies**

  The cases that should benefit most are:

  1. When using large objects as values (e.g.: `{loaded: $ctrl.data}`).
  2. When using objects/arrays and there are frequent changes.
  3. When there are many `$index` changes (e.g. addition/deletion/reordering in large `ngRepeat`s).

  The differences in operations per digest include:

  1. `Regular expression (when not changed)`
     **Before:** `equals()`
     **After:**  `toClassString()`

  2. `Regular expression (when changed)`
     **Before:** `copy()` + 2 x `arrayClasses()` + `shallowCopy()`
     **After:**  2 x `split()`

  3. `One-time expression (when not changed)`
     **Before:** `equals()`
     **After:**  `toFlatValue()` + `equals()`*

  4. `One-time expression (when changed)`
     **Before:** `copy()` + 2 x `arrayClasses()` + `shallowCopy()`
     **After:**  `copy()`* + `toClassString()`* + 2 x `split()`

  5. `$index modulo changed`
     **Before:** `arrayClasses()`
     **After:**  -

  (*): on flatter structure

  In large based on #14404. Kudos to @drpicox for the initial idea and a big part
  of the implementation.

Closes #14404

Closes #15246
2016-12-09 12:04:47 +02:00
Georgios Kalpakas d528644fe3 fix(ngClassOdd/Even): add/remove the correct classes when expression/$index change simultaneously 2016-12-09 12:03:38 +02:00
David Rodenas Pico 6f1bcfc14e test(ngClass): add some tests about one-time bindings and objects inside arrays 2016-12-09 12:01:37 +02:00
Georgios Kalpakas 996914c6b0 refactor(ngClass): remove redundant $observer and dependency on $animate
Includes the following commits (see #15246 for details):

- **refactor(ngClass): remove unnecessary dependency on `$animate`**

- **refactor(ngClass): remove redundant `$observe`r**

  The code was added in b41fe9f in order to support having both `ngClass` and
  interpolation in `class` work together. `ngClass` has changed considerably since
  b41fe9f and for simple cases to work the `$observe`r is no longer necessary (as
  indicated by the expanded test still passing).

  That said, it is a [documented known issue][1] that `ngClass` should not be used
  together with interpolation in `class` and more complicated cases do not work
  anyway.

[1]: https://docs.angularjs.org/api/ng/directive/ngClass#known-issues
2016-12-09 12:00:41 +02:00
Aman Mittal fff048d099 docs(guide/external-resources): add "AngularJS in Action" book
Closes #15480
2016-12-09 11:36:26 +02:00
Peter Bacon Darwin dcb0da8225 chore(docs): fix plnkrOpener to use $onInit
Since 1.6.0 does not preassign bindings before running the controller
constructor function, we must move initialisation into the `$onInit`
method.
2016-12-09 11:28:23 +02:00
Peter Bacon Darwin b664e20d12 chore(package): update docs app to run on 1.6.0 2016-12-09 11:27:20 +02:00
Georgios Kalpakas 3d68b95028 fix(jqLite): silently ignore after() if element has no parent
Previously, the element was always assumed to have a parent and an error was
thrown when that was not the case.
This commit makes jqLite consistent with jQuery, which silently ignores a call
on elements that do not have a parent.

Fixes #15331
Closes #15367

Closes #15475
2016-12-09 10:56:14 +02:00
Georgios Kalpakas 163aca336d fix($rootScope): when adding/removing watchers during $digest
Previously, adding a watcher during a `$digest` (i.e. from within a watcher),
would result in the next watcher getting skipped. Similarly, removing a watcher
during a `$digest` could result in the current watcher being run twice (if the
removed watcher had not run yet in the current `$digest`).

This commit fixes both cases by keeping track of the current watcher index
during a digest and properly updating it when adding/removing watchers.

Fixes #15422

Closes #15424
2016-12-09 10:44:24 +02:00
608 changed files with 24316 additions and 25375 deletions
+1 -5
View File
@@ -4,8 +4,8 @@ node_js:
- '6'
cache:
yarn: true
directories:
- node_modules
- bower_components
- docs/bower_components
@@ -36,10 +36,6 @@ addons:
packages:
- g++-4.8
before_install:
- curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9
- export PATH=$HOME/.yarn/bin:$PATH
before_script:
- du -sh ./node_modules ./bower_components/ ./docs/bower_components/ || true
- ./scripts/travis/before_build.sh
+625 -28
View File
@@ -1,3 +1,415 @@
<a name="1.6.5"></a>
# 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))<br>
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))
<a name="1.6.4"></a>
# 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))
<a name="1.6.3"></a>
# 1.6.3 scriptalicious-bootstrapping (2017-03-08)
## Bug Fixes
- **Angular:**
- 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))
<a name="1.6.2"></a>
# 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))
<a name="1.5.11"></a>
# 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-bootstraping 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))
<a name="1.6.1"></a>
# 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))
<a name="1.5.10"></a>
# 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))
<a name="1.6.0"></a>
# 1.6.0 rainbow-tsunami (2016-12-08)
@@ -8,14 +420,23 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- **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:** set `preAssignBindingsEnabled` to false by default
([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858),
[#15352](https://github.com/angular/angular.js/issues/15352))
- **$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)`
@@ -38,6 +459,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- JSONP requests now require a trusted resource URL
([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4),
[#11352](https://github.com/angular/angular.js/issues/11352))
- **$anchorScroll:** convert numeric hash targets to string
([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6)
[#14680](https://github.com/angular/angular.js/issues/14680))
- **select:** support values of any type added with `ngValue`
([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622),
[#9842](https://github.com/angular/angular.js/issues/9842))
@@ -51,6 +475,10 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
[#10597](https://github.com/angular/angular.js/issues/10597))
- allow `ngTrim` to work for `input[type=radio]`
([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b))
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79)
[#3410](https://github.com/angular/angular.js/issues/3410)
[#3516](https://github.com/angular/angular.js/issues/3516))
- **$interpolate:** use custom `toString()` function if present
([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe),
[#7317](https://github.com/angular/angular.js/issues/7317),
@@ -66,18 +494,30 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0),
[#13653](https://github.com/angular/angular.js/issues/13653),
[#7992](https://github.com/angular/angular.js/issues/7992))
- **$location:** default hashPrefix to `'!'`
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52),
[#13812](https://github.com/angular/angular.js/issues/13812))
- **$resource:** pass `status`/`statusText` to success callbacks
([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056)
[#8341](https://github.com/angular/angular.js/issues/8841)
[#8841](https://github.com/angular/angular.js/issues/8841))
- **$location:**
- default hashPrefix to `'!'`
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)
[#13812](https://github.com/angular/angular.js/issues/13812))
- add support for selectively rewriting links based on attribute
([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4))
- **$controller:** throw when requested controller is not registered
([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64)
[#14980](https://github.com/angular/angular.js/issues/14980))
## Security Related
- Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/09/angular-16-expression-sandbox-removal.html).
- **bootstrap:** explicitly whitelist URL schemes for bootstrap. (#15427)
([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53))
- **bootstrap:**
- explicitly whitelist URL schemes for bootstrap.
([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53))
- do not bootstrap from unknown schemes with a different origin
([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1))
([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1)
[#15428](https://github.com/angular/angular.js/issues/15428))
- **$compile:**
- secure `link[href]` as a `RESOURCE_URL`s in `$sce`
([04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa),
@@ -87,14 +527,18 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
## Bug Fixes
- **$sce:** fix `adjustMatcher` to replace multiple '*' and '**' (#7897)
- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**`
([991a2b](https://github.com/angular/angular.js/commit/991a2b30e00aed1d312e29555e356a795f9e3d62))
- **ngModelOptions:** handle debounce of `updateOn` triggers that are not in debounce list
([789790](https://github.com/angular/angular.js/commit/789790feee4d6c5b1f5d5b18ecb0ccf6edd36fb3))
- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()`
([7d9a79](https://github.com/angular/angular.js/commit/7d9a791c6a8c80d29d6c84afa287c81f2a307439))
- **$location:** throw if the path starts with double (back)slashes
([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004))
- **$location:**
- prevent infinite digest with IDN URLs in Edge
([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7)
[#15217](https://github.com/angular/angular.js/issues/15217))
- throw if the path starts with double (back)slashes
([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004))
- **core:** do not auto-bootstrap when loaded from an extension.
([0ff10e](https://github.com/angular/angular.js/commit/0ff10e1b56c6b7c4ac465e35c96a5886e294bac5))
- **input[radio]:** use strict comparison when evaluating checked-ness
@@ -124,6 +568,20 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- don't throw tplrt error when there is a whitespace around a top-level comment
([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284),
[#15108](https://github.com/angular/angular.js/issues/15108))
- clean up `@`-binding observers when re-assigning bindings
([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0)
[#15268](https://github.com/angular/angular.js/issues/15268))
- set attribute value even if `ngAttr*` contains no interpolation
([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842)
[#15133](https://github.com/angular/angular.js/issues/15133))
- `bindToController` should work without `controllerAs`
([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e)
[#15088](https://github.com/angular/angular.js/issues/15088))
- do not overwrite values set in `$onInit()` for `<`-bound literals
([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9)
[#15118](https://github.com/angular/angular.js/issues/15118))
- avoid calling `$onChanges()` twice for `NaN` initial values
([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445))
- disallow linking the same element more than once
([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f))
- correctly merge consecutive text nodes on IE11
@@ -136,14 +594,14 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
[#5513](https://github.com/angular/angular.js/issues/5513),
[#5597](https://github.com/angular/angular.js/issues/5597))
- move check for interpolation of on-event attributes to compile time
([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4),
[#13267](https://github.com/angular/angular.js/issues/13267))
([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4),
[#13267](https://github.com/angular/angular.js/issues/13267))
- **select, ngOptions, ngValue:**
- don't add comment nodes as empty options
([245b27](https://github.com/angular/angular.js/commit/245b27101aad129061585252b73652054319ca82),
[#15454](https://github.com/angular/angular.js/issues/15454))
- do not throw when removing the element (e.g. via `ngIf`)
([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f))
([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f))
- add/remove selected attribute for selected/unselected options
([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e))
- don't register options when select has no ngModel
@@ -158,7 +616,7 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
([e6afca](https://github.com/angular/angular.js/commit/e6afca00c9061a3e13b570796ca3ab428c1723a1),
[#14031](https://github.com/angular/angular.js/issues/14031))
- **$resource:**
- **$resource:** allow params in `hostname` (except for IPv6 addresses)
- allow params in `hostname` (except for IPv6 addresses)
([752b1e](https://github.com/angular/angular.js/commit/752b1e69b7a8e9c0b908f1980e9c738888f3647c),
[#14542](https://github.com/angular/angular.js/issues/14542))
- fulfill promise with the correct value on error
@@ -202,6 +660,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- **loader:** `module.decorator` order of operations is now irrelevant
([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e),
[#12382](https://github.com/angular/angular.js/issues/12382))
- **$sanitize:** reduce stack height in IE <= 11
([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76)
[#14928](https://github.com/angular/angular.js/issues/14928))
- **ngAnimate:** make svg elements work with `classNameFilter`
([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3))
@@ -221,8 +682,11 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597))
- **injector:** cache the results of the native class detection check
([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948))
- **$compile:** use strict comparison for `controller === '@'`
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- **$compile:**
- use strict comparison for `controller === '@'`
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- validate `directive.restrict` property on directive init
([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7))
- **$parse:**
- Inline constants
([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4))
@@ -452,6 +916,48 @@ 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, '')`
@@ -778,7 +1284,7 @@ You configure this list in a module configuration block:
```js
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhiteList([
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads.
'self',
// Allow JSONP calls that match this pattern
@@ -875,7 +1381,7 @@ previous behaviour simply add a comment:
**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 unexpectd side effects in the following cases:
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:
@@ -1029,7 +1535,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie
might also have been instantiated asynchronously. After this change, `$route` (and its dependencies)
will - by default - be instantiated early on.
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
Although this is not expected to have unwanted side-effects in normal application behavior, it may
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
request will be made for the default route's template. If not properly "trained", `$httpBackend`
will complain about this unexpected request.
@@ -1595,6 +2101,7 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **ngModel:** treat synchronous validators as boolean always ([7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b) [#14734](https://github.com/angular/angular.js/issues/14734))
- **$q:** treat thrown errors as regular rejections ([e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9) [#3174](https://github.com/angular/angular.js/issues/3174) [#15213](https://github.com/angular/angular.js/issues/15213))
- **ngTransclude:** use fallback content if only whitespace is provided ([32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301) [#15077](https://github.com/angular/angular.js/issues/15077))
- **$location:** prevent infinite digest with IDN URLs in Edge ([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7) [#15217](https://github.com/angular/angular.js/issues/15217))
- **$compile:**
- don't throw tplrt error when there is a whitespace around a top-level comment ([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284) [#15108](https://github.com/angular/angular.js/issues/15108))
- disallow linking the same element more than once ([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f))
@@ -1604,6 +2111,20 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- don't add leading white-space in attributes for a specific merge case ([305ba1](https://github.com/angular/angular.js/commit/305ba1a3fb3529cb3fdf04c12ac03fbb4f634456))
- don't trim white-space in attributes ([97bbf8](https://github.com/angular/angular.js/commit/97bbf86a1979d099802f0d631c17c54b87563b40) [#5513](https://github.com/angular/angular.js/issues/5513) [#5597](https://github.com/angular/angular.js/issues/5597))
- move check for interpolation of on-event attributes to compile time ([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4) [#13267](https://github.com/angular/angular.js/issues/13267))
- clean up `@`-binding observers when re-assigning bindings
([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0)
[#15268](https://github.com/angular/angular.js/issues/15268))
- set attribute value even if `ngAttr*` contains no interpolation
([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842)
[#15133](https://github.com/angular/angular.js/issues/15133))
- `bindToController` should work without `controllerAs`
([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e)
[#15088](https://github.com/angular/angular.js/issues/15088))
- do not overwrite values set in `$onInit()` for `<`-bound literals
([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9)
[#15118](https://github.com/angular/angular.js/issues/15118))
- avoid calling `$onChanges()` twice for `NaN` initial values
([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445))
- **select:**
- add/remove selected attribute for selected/unselected options ([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e))
- don't register options when select has no ngModel ([e8c2e1](https://github.com/angular/angular.js/commit/e8c2e119758e58e18fe43932d09a8ff9f506aa9d))
@@ -1627,6 +2148,9 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **ngMock/$httpBackend:** fail if a url is provided but is `undefined` ([7551b8](https://github.com/angular/angular.js/commit/7551b8975a91ee286cc2cf4af5e78f924533575e) [#8442](https://github.com/angular/angular.js/issues/8442) [#10934](https://github.com/angular/angular.js/issues/10934))
- **$route:** don't process route change controllers and templates for `redirectTo` routes ([7f4b35](https://github.com/angular/angular.js/commit/7f4b356c2bebb87f0c26b57a20415b004b20bcd1) [#3332](https://github.com/angular/angular.js/issues/3332))
- **loader:** `module.decorator` order of operations is now irrelevant ([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e) [#12382](https://github.com/angular/angular.js/issues/12382))
- **$sanitize:** reduce stack height in IE <= 11
([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76)
[#14928](https://github.com/angular/angular.js/issues/14928))
- **ngAnimate:** make svg elements work with `classNameFilter` ([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3))
@@ -1634,24 +2158,50 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **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 `<select multiple>` with no selection ([d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8))
- **$compile:**
- add `preAssignBindingsEnabled` option
([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef))
- 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))
- **$http:**
- remove deprecated callback methods: `success()/error()` ([b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014))
- JSONP callback must be specified by `jsonpCallbackParam` config ([fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb) [#15161](https://github.com/angular/angular.js/issues/15161) [#11352](https://github.com/angular/angular.js/issues/11352))
- JSONP requests now require a trusted resource URL ([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4) [#11352](https://github.com/angular/angular.js/issues/11352))
- **$anchorScroll:** convert numeric hash targets to string
([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6)
[#14680](https://github.com/angular/angular.js/issues/14680))
- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` ([87a2ff](https://github.com/angular/angular.js/commit/87a2ff76af5d0a9268d8eb84db5755077d27c84c) [#10922](https://github.com/angular/angular.js/issues/10922))
- **input:**
- add support for binding to `input[type=range]` ([913016](https://github.com/angular/angular.js/commit/9130166767c4792c5d32d08a918fc7becf32c9a6) [#5892](https://github.com/angular/angular.js/issues/5892) [#14870](https://github.com/angular/angular.js/issues/14870))
- add support for `step` to `input[type=number]` ([e1da4be](https://github.com/angular/angular.js/commit/e1da4bed8e291003d485a8ad346ab80bed8ae2e3) [#10597](https://github.com/angular/angular.js/issues/10597))
- allow `ngTrim` to work for `input[type=radio]` ([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b))
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79)
[#3410](https://github.com/angular/angular.js/issues/3410)
[#3516](https://github.com/angular/angular.js/issues/3516))
- **ngRoute:** allow `ngView` to be included in an asynchronously loaded template ([c13c66](https://github.com/angular/angular.js/commit/c13c666728c1a1485ef18e92d7cb35118ce39609) [#1213](https://github.com/angular/angular.js/issues/1213))
- **select:** support values of any type added with `ngValue` ([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622) [#9842](https://github.com/angular/angular.js/issues/9842))
- **$interpolate:** use custom `toString()` function if present ([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe) [#7317](https://github.com/angular/angular.js/issues/7317) [#11406](https://github.com/angular/angular.js/issues/11406))
- **$route:** implement `resolveRedirectTo` ([e98656](https://github.com/angular/angular.js/commit/e9865654b39c71be71034c38581a8c7bd16bc716) [#5150](https://github.com/angular/angular.js/issues/5150))
- **$q:** report promises with non rejection callback ([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0) [#13653](https://github.com/angular/angular.js/issues/13653) [#7992](https://github.com/angular/angular.js/issues/7992))
- **$location:** default hashPrefix to `'!'` ([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52) [#13812](https://github.com/angular/angular.js/issues/13812))
- **$resource:** pass `status`/`statusText` to success callbacks
([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056)
[#8341](https://github.com/angular/angular.js/issues/8841)
[#8841](https://github.com/angular/angular.js/issues/8841))
- **$location:**
- default hashPrefix to `'!'`
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)
[#13812](https://github.com/angular/angular.js/issues/13812))
- add support for selectively rewriting links based on attribute
([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4))
- **$controller:** throw when requested controller is not registered
([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64)
[#14980](https://github.com/angular/angular.js/issues/14980))
## Performance Improvements
@@ -1660,7 +2210,11 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **$animate:** listen for document visibility changes ([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597))
- **injector:** cache the results of the native class detection check ([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948))
- **$parse:** Inline constants ([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4))
- **$compile:** use strict comparison for `controller === '@'` ([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- **$compile:**
- use strict comparison for `controller === '@'`
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- validate `directive.restrict` property on directive init
([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7))
- **$parse:** remove Angular expression sandbox ([1547c7](https://github.com/angular/angular.js/commit/1547c751aa48efe7dbefef701c3df5983b04aa2e) [#15094](https://github.com/angular/angular.js/issues/15094))
@@ -1782,6 +2336,48 @@ 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
@@ -2099,7 +2695,7 @@ You configure this list in a module configuration block:
```js
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhiteList([
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads.
'self',
// Allow JSONP calls that match this pattern
@@ -2215,7 +2811,7 @@ affects custom directives that might have been reading options for their own pur
**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 unexpectd side effects in the following cases:
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:
@@ -2365,7 +2961,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie
might also have been instantiated asynchronously. After this change, `$route` (and its dependencies)
will - by default - be instantiated early on.
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
Although this is not expected to have unwanted side-effects in normal application behavior, it may
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
request will be made for the default route's template. If not properly "trained", `$httpBackend`
will complain about this unexpected request.
@@ -4273,6 +4869,7 @@ support asynchronous loading of resources.)
- **$compile:** Lazily compile the `transclude` function
([652b83eb](https://github.com/angular/angular.js/commit/652b83eb226131d131a44453520a569202aa4aac))
See https://github.com/angular/angular.js/issues/14343#issuecomment-229037252 for more information.
## Breaking Changes
@@ -12079,7 +12676,7 @@ Contains only these fixes cherry-picked from [v1.2.0rc1](#1.2.0rc1).
- due to [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56),
Interpolations inside DOM event handlers are disallowed.
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
To migrate the code follow the example below:
@@ -14710,7 +15307,7 @@ with the `$route` service
### Docs
- rewrite of several major portions of angular.service.*, angular.Array.*, angular.Object.* docs
- added support for [sitemap]((http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
- added support for [sitemap](http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
search crawlers
- transition of Developer Guide docs from the wiki into docs.angularjs.org
- lots of improvements related to formatting of the content of docs.anguarjs.org
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
Copyright (c) 2010-2017 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+2 -2
View File
@@ -8,8 +8,8 @@ synchronizes data from your UI (view) with your JavaScript objects (model) throu
binding. To help you structure your application better and make it easy to test, AngularJS teaches
the browser how to do dependency injection and inversion of control.
It also helps with server-side communication, taming async callbacks with promises and deferreds,
and it makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
It also helps with server-side communication, taming async callbacks with promises and deferred objects,
and it makes client-side navigation and deep linking with hashbang urls or HTML5 pushState a
piece of cake. Best of all? It makes development fun!
* Web site: https://angularjs.org
+44
View File
@@ -0,0 +1,44 @@
'use strict';
angular
.module('animationBenchmark', ['ngAnimate'], config)
.controller('BenchmarkController', BenchmarkController);
// Functions - Definitions
function config($compileProvider) {
$compileProvider
.commentDirectivesEnabled(false)
.cssClassDirectivesEnabled(false)
.debugInfoEnabled(false);
}
function BenchmarkController($scope) {
var self = this;
var itemCount = 1000;
var items = (new Array(itemCount + 1)).join('.').split('');
benchmarkSteps.push({
name: 'create',
fn: function() {
$scope.$apply(function() {
self.items = items;
});
}
});
benchmarkSteps.push({
name: '$digest',
fn: function() {
$scope.$root.$digest();
}
});
benchmarkSteps.push({
name: 'destroy',
fn: function() {
$scope.$apply(function() {
self.items = [];
});
}
});
}
+22
View File
@@ -0,0 +1,22 @@
/* eslint-env node */
'use strict';
module.exports = function(config) {
config.set({
scripts: [
{
id: 'jquery',
src: 'jquery-noop.js'
}, {
id: 'angular',
src: '/build/angular.js'
}, {
id: 'angular-animate',
src: '/build/angular-animate.js'
}, {
src: 'app.js'
}
]
});
};
+1
View File
@@ -0,0 +1 @@
// Override me with ?jquery=/bower_components/jquery/dist/jquery.js
+28
View File
@@ -0,0 +1,28 @@
<style>
[ng-cloak] { display: none !important; }
.animation-container .ng-enter,
.animation-container .ng-leave {
transition: all 0.1s;
}
.animation-container .ng-enter,
.animation-container .ng-leave.ng-leave-active {
opacity: 0;
}
.animation-container .ng-enter.ng-enter-active,
.animation-container .ng-leave {
opacity: 1;
}
</style>
<div ng-app="animationBenchmark" ng-cloak ng-controller="BenchmarkController as bm">
<div class="container-fluid">
<h2>Large collection of elements animated in and out with ngAnimate</h2>
<div class="animation-container">
<div ng-repeat="i in bm.items track by $index">
Just a plain ol' element
</div>
</div>
</div>
</div>
+108
View File
@@ -0,0 +1,108 @@
'use strict';
var app = angular.module('ngClassBenchmark', []);
app.controller('DataController', function DataController($scope) {
this.init = function() {
this.numberOfTodos = 1000;
this.implementation = 'tableOptimized';
this.completedPeriodicity = 3;
this.importantPeriodicity = 13;
this.urgentPeriodicity = 29;
this.createTodos(100);
this.setTodosValuesWithSeed(0);
};
this.clearTodos = function() {
this.todos = null;
};
this.createTodos = function(count) {
var i;
this.todos = [];
for (i = 0; i < count; i++) {
this.todos.push({
id: i + 1,
completed: false,
important: false,
urgent: false
});
}
};
this.setTodosValuesWithSeed = function(offset) {
var i, todo;
for (i = 0; i < this.todos.length; i++) {
todo = this.todos[i];
todo.completed = 0 === (i + offset) % this.completedPeriodicity;
todo.important = 0 === (i + offset) % this.importantPeriodicity;
todo.urgent = 0 === (i + offset) % this.urgentPeriodicity;
}
};
this.init();
benchmarkSteps.push({
name: 'setup',
fn: function() {
$scope.$apply();
this.clearTodos();
this.createTodos(this.numberOfTodos);
}.bind(this)
});
benchmarkSteps.push({
name: 'create',
fn: function() {
// initialize data for first time that will construct the DOM
this.setTodosValuesWithSeed(0);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: '$apply',
fn: function() {
$scope.$apply();
}
});
benchmarkSteps.push({
name: 'update',
fn: function() {
// move everything but completed
this.setTodosValuesWithSeed(3);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: 'unclass',
fn: function() {
// remove all classes
this.setTodosValuesWithSeed(NaN);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: 'class',
fn: function() {
// add all classes as the initial state
this.setTodosValuesWithSeed(0);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: 'destroy',
fn: function() {
this.clearTodos();
$scope.$apply();
}.bind(this)
});
});
+15
View File
@@ -0,0 +1,15 @@
/* eslint-env node */
'use strict';
module.exports = function(config) {
config.set({
scripts: [{
id: 'angular',
src: '/build/angular.js'
},
{
src: 'app.js'
}]
});
};
+177
View File
@@ -0,0 +1,177 @@
<style>
.gold {
background: gold;
}
.silver {
background: silver;
}
.table tbody tr > td.success {
background-color: #dff0d8;
}
.table tbody tr > td.error {
background-color: #f2dede;
}
.table tbody tr > td.warning {
background-color: #fcf8e3;
}
.table tbody tr > td.info {
background-color: #d9edf7;
}
.completed {
text-decoration: line-through;
}
.important {
font-weight: bold;
}
.urgent {
color: red;
}
</style>
<div ng-app="ngClassBenchmark" ng-cloak class="container-fluid">
<div ng-controller="DataController as benchmark" class="row">
<div class="col-lg-12">
<div class="well">
<h3>Parameters</h3>
<br>
<p>
<label>Number of todos</label><br>
<input type="number" ng-model="benchmark.numberOfTodos">
</p>
<br>
<p>
<label>Implementation</label><br>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="tableOptimized"
type="radio" name="implementation">
Table optimized <br>
<code>ng-class="todo.completed && 'success'"</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="table"
type="radio" name="implementation">
Table <br>
<code>ng-class="{success: todo.completed}"</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="list"
type="radio" name="implementation">
List <br>
<code>ng-class="{completed: todo.completed, urgent: todo.urgent, important: todo.important"}</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="singleOptimized"
type="radio" name="implementation">
Single ngClass optimized <br>
<code>
ng-class="{'panel-success': !!benchmark.todos, 'panel-danger': !benchmark.todos}"
</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="single"
type="radio" name="implementation">
Single ngClass <br>
<code>
ng-class="{'panel-success': benchmark.todos, 'panel-danger': !benchmark.todos}"
</code>
</label>
</div>
</p>
</div>
<br>
<h3>Example</h3>
<div ng-switch="benchmark.implementation">
<table ng-switch-when="tableOptimized" class="table">
<thead>
<tr>
<th>todo #id</th>
<th>completed?</th>
<th>urgent?</th>
<th>important?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in benchmark.todos track by todo.id"
ng-class="todo.completed && 'active'"
ng-class-even="todo.completed && todo.important && 'gold'"
ng-class-odd="todo.completed && todo.important && 'silver'"
>
<td>#{{todo.id}}</td>
<td>{{todo.completed}}</td>
<td ng-class="todo.urgent && 'danger'">{{todo.urgent}}</td>
<td ng-class="todo.important && 'success'">{{todo.important}}</td>
</tr>
</tbody>
</table>
<table ng-switch-when="table" class="table">
<thead>
<tr>
<th>todo #id</th>
<th>completed?</th>
<th>urgent?</th>
<th>important?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in benchmark.todos track by todo.id"
ng-class="{active: todo.completed}"
ng-class-even="{gold: todo.completed && todo.important}"
ng-class-odd="{silver: todo.completed && todo.important}"
>
<td>#{{todo.id}}</td>
<td>{{todo.completed}}</td>
<td ng-class="{danger: todo.urgent}">{{todo.urgent}}</td>
<td ng-class="{success: todo.important}">{{todo.important}}</td>
</tr>
</tbody>
</table>
<ul ng-switch-when="list">
<li ng-repeat="todo in benchmark.todos track by todo.id"
ng-class="{
completed: todo.completed,
urgent: todo.urgent,
important: todo.important
}">#{{todo.id}}</li>
</ul>
<div ng-switch-when="singleOptimized"
class="panel"
ng-class="{'panel-success': !!benchmark.todos, 'panel-danger': !benchmark.todos}">
<div class="panel-heading">
<h3 class="panel-title">Information</h3>
</div>
<div class="panel-body"> The title is green because there are todos... </div>
</div>
<div ng-switch-when="single"
class="panel"
ng-class="{'panel-success': benchmark.todos, 'panel-danger': !benchmark.todos}">
<div class="panel-heading">
<h3 class="panel-title">Information</h3>
</div>
<div class="panel-body"> The title is green because there are todos... </div>
</div>
</div>
</div>
</div>
</div>
<br><br><br>
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "angularjs",
"license": "MIT",
"devDependencies": {
"jquery": "3.1.0",
"jquery": "3.2.1",
"jquery-2.2": "jquery#2.2.4",
"jquery-2.1": "jquery#2.1.4",
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
+1
View File
@@ -0,0 +1 @@
.visible-phone{display:none}.visible-desktop{display:block}.navbar{display:block}.navbar .container{padding:0 16px;width:auto}.navbar .brand{float:left;margin:8px 80px 0 8px;padding:0}.navbar .brand a{display:block;height:30px;margin:6px 0 5px 0;overflow:hidden;padding:0;width:117px}.navbar .nav{float:right}.navbar .nav .dropdown-toggle{color:rgba(255,255,255,0.87);font-size:16px;font-weight:300;line-height:56px;padding:0 24px;text-transform:uppercase;transition:all .3s}.navbar .nav .dropdown-toggle:hover,.navbar .nav .dropdown-toggle:active,.navbar .nav .dropdown-toggle:focus{background:#37474F;color:#fff}.navbar .nav .dropdown-menu{background:#37474F;border:none;border-radius:0;box-shadow:0 0 16px rgba(0,0,0,0.12),0 16px 16px rgba(0,0,0,0.24);color:#fff;left:auto;margin:0;padding:0;right:0}.navbar .nav .dropdown-menu:after,.navbar .nav .dropdown-menu:before{display:none}.navbar .nav .dropdown-menu li{border-bottom:1px solid rgba(38,50,56,0.56);box-sizing:border-box;line-height:48px}.navbar .nav .dropdown-menu li:last-child{border:none}.navbar .nav .dropdown-menu a{background:#37474F;color:#fff;font-weight:300;line-height:48px;padding:0 16px;transition:all .2s}.navbar .nav .dropdown-menu a:hover,.navbar .nav .dropdown-menu a:focus{background:#455A64}.navbar .navbar-search{left:200px;margin:0;position:absolute;right:440px;top:8px;width:auto}.navbar .navbar-search i{color:#546E7A;font-size:16px;left:12px;position:absolute;top:11px}.navbar .navbar-search .search-query{background:#37474F;border:none;border-radius:2px;box-shadow:none;box-sizing:border-box;color:#546E7A;font-size:14px;height:40px;width:100%;padding:0 16px 0 32px;text-shadow:none;transition:all .3s}.navbar .navbar-search .search-query:-webkit-autofill,.navbar .navbar-search .search-query:-webkit-autofill:hover,.navbar .navbar-search .search-query:-webkit-autofill:focus{background-color:#fff;transition:background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#455A64}.navbar .navbar-search .search-query:hover,.navbar .navbar-search .search-query:active,.navbar .navbar-search .search-query:focus{background:#fff;box-shadow:inset 0 2px 4px rgba(0,0,0,0.24);color:#2196F3}.navbar .navbar-search .search-query::-webkit-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query::-moz-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-ms-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-moz-placeholder{color:#546E7A}#navbar-main .navbar-inner{background:#263238;height:56px}#navbar-notice{z-index:1029;top:56px}#navbar-notice .navbar-inner{background:#ECEFF1;box-shadow:0 0 3px rgba(0,0,0,0.12),0 3px 3px rgba(0,0,0,0.24);height:auto}.site-notice{padding:4px 0;text-align:center;font-size:13px;margin:0}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.visible-phone{display:block}.visible-desktop{display:none}}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.homepage .container{padding:16px;width:auto}.homepage .span1{width:auto}.homepage .span2{width:auto}.homepage .span3{width:auto}.homepage .span4{width:auto}.homepage .span5{width:auto}.homepage .span6{width:auto}.homepage .span7{width:auto}.homepage .span8{width:auto}.homepage .span9{width:auto}.homepage .span10{width:auto}.homepage .navbar .container{padding:0 8px}.homepage #navbar-main .navbar-inner{height:40px}.homepage #navbar-main .brand{margin:6px 0 0 0}.homepage #navbar-main .brand a{margin:0}.homepage #navbar-main .nav{margin:0}.homepage #navbar-main .nav .dropdown-toggle{font-size:12px;line-height:40px;padding:0 8px}.homepage #navbar-main .dropdown-menu a{padding:0 8px}.homepage #navbar-main .navbar-search{background:#263238;border-bottom:1px solid #263238;left:0;right:0;top:100%}.homepage #navbar-main .navbar-search i{left:12px;top:7px}.homepage #navbar-main .navbar-search .search-query{border-radius:0;height:32px}.homepage #navbar-notice{top:40px}.homepage #navbar-notice .site-notice{font-size:11px}.homepage .hero{padding:80px 32px 32px 32px}.homepage .hero h2{background-size:230px 60px;height:60px;width:230px}}
+97 -6
View File
@@ -53,13 +53,13 @@ h1,h2,h3,h4,h5,h6 {
}
.header .brand {
padding-top: 6px;
padding-bottom: 0px;
}
.header .brand img {
margin-top: 5px;
height: 30px;
margin-top: 0;
height: auto;
vertical-align: top;
}
.docs-search {
@@ -82,6 +82,11 @@ h1,h2,h3,h4,h5,h6 {
margin-right: 10px;
}
.navbar .navbar-search i {
top: 13px;
font-size: 12px;
}
.docs-search > .search-query:focus {
outline: 0;
}
@@ -297,6 +302,7 @@ iframe.example {
}
.search-results-container {
position: relative;
padding-bottom: 1em;
border-top: 1px solid #111;
background: #181818;
@@ -435,15 +441,17 @@ iframe.example {
background: #f1f1f1;
}
.sup-header {
#navbar-sub {
padding-top: 10px;
padding-bottom: 5px;
background: rgba(245,245,245,0.88);
box-shadow: 0 0 2px #999;
z-index: 1028;
top: 83px;
}
.main-body-grid {
margin-top: 120px;
margin-top: 144px;
position: relative;
}
@@ -454,7 +462,7 @@ iframe.example {
.main-body-grid > .grid-left {
position: fixed;
top: 120px;
top: 144px;
bottom: 0;
overflow: auto;
}
@@ -827,3 +835,86 @@ ul.events > li {
iframe[name="example-anchoringExample"] {
height: 400px;
}
/*
angular-topnav.css and bootstrap overrides
*/
.navbar .navbar-inner .container {
padding: 0 16px;
width: auto;
height: auto;
}
.navbar .nav > li {
float: left;
}
.navbar-nav .open .dropdown-menu {
position: absolute;
float: left;
}
.navbar-nav .open .dropdown-menu > li > a {
line-height: 48px;
}
#navbar-main .navbar-inner, #navbar-notice .navbar-inner {
box-shadow: none;
}
#navbar-sub .container {
max-width: 970px;
}
.nav .open > a, .nav .open > a:hover, .nav .open > a:focus {
background-color: inherit;
}
@media handheld and (max-width:800px), screen and (max-device-width:800px), screen and (max-width:800px) {
.navbar {
min-height: auto;
}
.search-results-container {
top: 32px;
overflow: auto;
max-height: 85vh;
padding-bottom: 0;
position: static;
}
.search-close {
right: 1px;
margin-left: 0;
top: 41px;
padding: 5px 10px;
border-top-right-radius: 0;
border-top-left-radius: 0;
box-shadow: none;
width: auto;
bottom: auto;
left: auto;
}
.navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header {
padding: 0 8px;
}
.homepage #navbar-notice {
top: 72px;
}
#navbar-notice .navbar-inner {
box-shadow: 0 0 3px rgba(0, 0, 0, .12), 0 3px 3px rgba(0, 0, 0, .24)
}
#navbar-sub {
position: relative;
top: 17px;
margin-top: 80px;
padding-bottom: 0;
margin-bottom: 0;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
/* global importScripts, lunr */
// Load up the lunr library
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
importScripts('../components/lunr-0.7.2/lunr.min.js');
// Create the lunr index - the docs should be an array of object, each object containing
// the path and search terms for a page
+2 -2
View File
@@ -18,8 +18,8 @@ describe('doc.angularjs.org', function() {
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
ngBindLink.click();
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('ngClick');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('ngClick');
});
+11 -9
View File
@@ -44,30 +44,31 @@ describe('docs.angularjs.org', function() {
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
ngBindLink.click();
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('ngClick');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('ngClick');
});
it('should be resilient to trailing slashes', function() {
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('angular.noop');
});
it('should be resilient to trailing "index"', function() {
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('angular.noop');
});
it('should be resilient to trailing "index/"', function() {
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index/');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('angular.noop');
});
@@ -78,7 +79,8 @@ describe('docs.angularjs.org', function() {
it('should display an error if the page does not exist', function() {
browser.get('build/docs/index-production.html#!/api/does/not/exist');
expect(element(by.css('h1')).getText()).toBe('Oops!');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('Oops!');
});
});
+5 -4
View File
@@ -159,10 +159,11 @@ angular.module('examples', [])
};
// Initialize the example data, so it's ready when clicking the open button.
// Otherwise pop-up blockers will prevent a new window from opening
ctrl.prepareExampleData(ctrl.example.path);
ctrl.$onInit = function() {
// Initialize the example data, so it's ready when clicking the open button.
// Otherwise pop-up blockers will prevent a new window from opening
ctrl.prepareExampleData(ctrl.example.path);
};
}]
};
}])
-10
View File
@@ -1,10 +0,0 @@
{
"name": "AngularJS-docs-app",
"dependencies": {
"jquery": "2.2.3",
"lunr.js": "0.5.12",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"bootstrap": "3.1.1"
}
}
+3 -2
View File
@@ -17,9 +17,9 @@ module.exports = function debugDeployment(getVersion) {
'../angular-sanitize.js',
'../angular-touch.js',
'../angular-animate.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
'components/lunr-' + getVersion('lunr') + '/lunr.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -32,6 +32,7 @@ module.exports = function debugDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
+3 -2
View File
@@ -17,9 +17,9 @@ module.exports = function defaultDeployment(getVersion) {
'../angular-sanitize.min.js',
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -32,6 +32,7 @@ module.exports = function defaultDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
+3 -2
View File
@@ -21,9 +21,9 @@ module.exports = function jqueryDeployment(getVersion) {
'../angular-sanitize.min.js',
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -36,6 +36,7 @@ module.exports = function jqueryDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
@@ -34,9 +34,9 @@ module.exports = function productionDeployment(getVersion) {
cdnUrl + '/angular-sanitize.min.js',
cdnUrl + '/angular-touch.min.js',
cdnUrl + '/angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -49,6 +49,7 @@ module.exports = function productionDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
+2 -2
View File
@@ -10,8 +10,8 @@ module.exports = function getVersion(readFilesProcessor) {
var basePath = readFilesProcessor.basePath;
return function(component, sourceFolder, packageFile) {
sourceFolder = path.resolve(basePath, sourceFolder || 'docs/bower_components');
packageFile = packageFile || 'bower.json';
sourceFolder = path.resolve(basePath, sourceFolder || 'node_modules');
packageFile = packageFile || 'package.json';
return require(path.join(sourceFolder,component,packageFile)).version;
};
};
@@ -68,101 +68,95 @@
})();
</script>
</head>
<body>
<body class="homepage">
<div id="wrapper">
<header scroll-y-offset-element class="header header-fixed">
<section class="navbar navbar-inverse docs-navbar-primary" ng-controller="DocsSearchCtrl">
<div class="container">
<div class="row">
<div class="col-md-9 header-branding">
<a class="brand navbar-brand" href="http://angularjs.org">
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
</a>
<ul class="nav navbar-nav">
<li class="divider-vertical"></li>
<li><a href="http://angularjs.org"><i class="icon-home icon-white"></i> Home</a></li>
<li class="divider-vertical"></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-eye-open icon-white"></i> Learn <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li class="disabled"><a href="http://angularjs.org/">Why AngularJS?</a></li>
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
<li><a href="tutorial">Tutorial</a></li>
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
<li><a href="misc/faq">FAQ</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
<li class="dropdown active">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="tutorial">Tutorial</a></li>
<li><a href="guide">Developer Guide</a></li>
<li><a href="api">API Reference</a></li>
<li><a href="error">Error Reference</a></li>
<li><a href="misc/contribute">Contribute</a></li>
<li><a href="http://code.angularjs.org/">Download</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-comment icon-white"></i> Discuss <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="http://blog.angularjs.org">Blog</a></li>
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">Chat Room</a></li>
<li class="divider"></li>
<li><a href="https://twitter.com/#!/angularjs">Twitter</a></li>
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
<li class="divider"></li>
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
<li><a href="https://github.com/angular/angular.js/issues">Issue Tracker</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
</ul>
</div>
<form ng-class="{focus:focus}" class="navbar-search col-md-3 docs-search" ng-submit="submit()">
<span class="glyphicon glyphicon-search search-icon"></span>
<input type="text"
name="as_q"
class="search-query"
placeholder="Click or press / to search"
ng-focus="focus=true"
ng-blur="focus=false"
ng-change="search(q)"
ng-model="q"
docs-search-input
autocomplete="off" />
</form>
</div>
</div>
<div class="search-results-container" ng-show="hasResults">
<header class="header">
<nav id="navbar-main" class="navbar navbar-fixed-top">
<div class="navbar-inner" ng-controller="DocsSearchCtrl">
<div class="container">
<div class="search-results-frame">
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
<h4 class="search-results-group-heading">{{ key }}</h4>
<ul class="search-results">
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
TODO: use a html minifier instead -->
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
<h1 class="brand"><a href="http://angularjs.org"><img width="117" height="30" src="img/angularjs-for-header-only.svg" alt="AngularJS"></a></h1>
<form class="navbar-search" ng-submit="submit()">
<i class="glyphicon glyphicon-search search-icon"></i>
<input type="text" name="as_q" class="search-query" placeholder="SEARCH"
ng-focus="focus=true"
ng-blur="focus=false"
ng-change="search(q)"
ng-model="q"
ng-model-options="{debounce: 150}"
docs-search-input
autocomplete="off">
</form>
<ul class="nav navbar-nav">
<li class="dropdown" uib-dropdown>
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Learn</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li><a href="tutorial">Tutorial</a></li>
<li><a href="misc/faq">FAQ</a></li>
<li><a href="https://www.youtube.com/user/angularjs">Videos</a></li>
<li><a href="http://angular.codeschool.com/">Free Course</a></li>
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
</ul>
</li>
<li class="dropdown" uib-dropdown>
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Develop</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li><a href="guide">Developer Guide</a></li>
<li><a href="api">API Reference</a></li>
<li><a href="error">Error Reference</a></li>
<li><a href="misc/contribute">Contribute</a></li>
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
<li><a href="http://code.angularjs.org/">Download</a></li>
</ul>
</li>
<li class="dropdown" uib-dropdown>
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Discuss</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li><a href="http://blog.angularjs.org">Blog</a></li>
<li><a href="https://twitter.com/angular">Twitter</a></li>
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
<li><a href="https://github.com/angular/angular.js/issues">Feature &amp; Bug Tracker</a></li>
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">IRC</a></li>
<li><a href="https://gitter.im/angular/angular.js">Gitter</a></li>
</ul>
</li>
</ul>
</div>
<div class="search-results-container" ng-show="hasResults">
<div class="container">
<div class="search-results-frame">
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
<h4 class="search-results-group-heading">{{ key }}</h4>
<ul class="search-results">
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
</ul>
</div>
</div>
<a href="" ng-click="hideResults()" class="search-close">
<span class="glyphicon glyphicon-remove search-close-icon"></span> Close
</a>
</div>
<a href="" ng-click="hideResults()" class="search-close">
<span class="glyphicon glyphicon-remove search-close-icon"></span> Close
</a>
</div>
</div>
</section>
<section class="sup-header">
</nav>
<nav id="navbar-notice" class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<p class="site-notice visible-phone">
This site refers to AngularJS (v1.x). <a href="https://angular.io/">Go to the latest Angular</a>.
</p>
<p class="site-notice visible-desktop">
This site and all of its contents are referring to AngularJS (version 1.x),
if you are looking for the latest Angular, please visit <a href="https://angular.io/">angular.io</a>.
</p>
</div>
</div>
</nav>
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top" scroll-y-offset-element>
<div class="container main-grid main-header-grid">
<div class="grid-left">
<version-picker></version-picker>
@@ -176,7 +170,7 @@
</ul>
</div>
</div>
</section>
</nav>
</header>
<section role="main" class="container main-body">
@@ -214,7 +208,7 @@
<p class="pull-right"><a back-to-top>Back to top</a></p>
<p>
Super-powered by Google ©2010-2016
Super-powered by Google ©2010-2017
(<a id="version"
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
@@ -18,9 +18,6 @@
<ul>
{% if doc.restrict.element %}
<li>as element:
{% if doc.name.indexOf('ng') == 0 -%}
(This directive can be used as custom element, but be aware of <a href="guide/ie">IE restrictions</a>).
{%- endif %}
{% code %}
<{$ doc.name | dashCase $}
{%- for param in doc.params %}
@@ -61,12 +58,12 @@
</div>
{% endblock -%}
{% include "lib/params.template.html" %}
{% include "lib/events.template.html" %}
{%- if doc.animations %}
<h2 id="animations">Animations</h2>
{$ doc.animations | marked $}
{$ 'module:ngAnimate.$animate' | link('Click here', doc) $} to learn more about the steps involved in the animation.
{%- endif -%}
{% include "lib/params.template.html" %}
{% include "lib/events.template.html" %}
{% endblock %}
-12
View File
@@ -1,12 +0,0 @@
@ngdoc error
@name $animate:nocb
@fullName Do not pass a callback to animate methods
@description
Since Angular 1.3, the methods of {@link ng.$animate} do not accept a callback as the last parameter.
Instead, they return a promise to which you can attach `then` handlers to be run when the animation completes.
If you are getting this error then you need to update your code to use the promise-based API.
See https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9 for information about
the change to the animation API and the changes you need to make.
@@ -0,0 +1,8 @@
@ngdoc error
@name $animate:nongcls
@fullName `ng-animate` class not allowed
@description
This error occurs, when trying to set `$animateProvider.classNameFilter()` to a RegExp containing
the reserved `ng-animate` class. Since `.ng-animate` will be added/removed by `$animate` itself,
using it as part of the `classNameFilter` RegExp is not allowed.
@@ -5,7 +5,7 @@
This error occurs when the restrict property of a directive is not valid.
The directive restrict property must be a string including one of more of the following characters:
The directive restrict property must be a string including one or more of the following characters:
* E (element)
* A (attribute)
* C (class)
@@ -15,4 +15,4 @@ For example:
```javascript
restrict: 'E'
restrict: 'EAC'
```
```
+38
View File
@@ -0,0 +1,38 @@
@ngdoc error
@name $compile:noslot
@fullName No matching slot in parent directive
@description
This error occurs when declaring a specific slot in a {@link ng.ngTransclude `ngTransclude`}
which does not map to a specific slot defined in the transclude property of the directive.
In this example the template has declared a slot missing from the transclude definition.
This example will generate a noslot error.
```js
var componentConfig = {
template: '<div>' +
'<div ng-transclude="slotProvided"></div>' +
'<div ng-transclude="noSlotProvided"></div>' +
'</div>',
transclude: {
// The key value pairs here are considered "slots" that are provided for components to slot into.
slotProvided: 'slottedComponent', // mandatory transclusion
// There is no slot provided here for the transclude 'noSlotProvided' declared in the above template.
}
};
```
If we make the following change we will no longer get the noslot error.
```js
var componentConfig = {
template: '<div>' +
'<div ng-transclude="slotProvided"></div>' +
'<div ng-transclude="noSlotProvided"></div>' +
'</div>',
transclude: {
slotProvided: 'slottedComponent',
noSlotProvided: 'otherComponent' // now it is declared and the error should cease
}
};
```
+14
View File
@@ -0,0 +1,14 @@
@ngdoc error
@name $http:baddata
@fullName Bad JSON Data
@description
The default {@link ng.$http#default-transformations `transformResponse`} will try to parse the
response as JSON if the `Content-Type` header is `application/json`, or the response looks like a
valid JSON-stringified object or array.
This error occurs when that data is not a valid JSON object.
To resolve this error, make sure you pass valid JSON data to `transformResponse`. If the response
data looks like JSON, but has a different `Content-Type` header, you must
{@link ng.$http#overriding-the-default-transformations-per-request implement your own response
transformer on a per request basis}, or {@link ng.$http#default-transformations modify the default `$http` responseTransform}.
+11
View File
@@ -0,0 +1,11 @@
@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.
+7
View File
@@ -0,0 +1,7 @@
@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.
+41 -31
View File
@@ -232,27 +232,27 @@ than the hash fragment was changed.
### Example
```js
it('should show example', inject(
function($locationProvider) {
it('should show example', function() {
module(function($locationProvider) {
$locationProvider.html5Mode(false);
$locationProvider.hashPrefix('!');
},
function($location) {
});
inject(function($location) {
// open http://example.com/base/index.html#!/a
$location.absUrl() === 'http://example.com/base/index.html#!/a'
$location.path() === '/a'
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/a');
expect($location.path()).toBe('/a');
$location.path('/foo')
$location.absUrl() === 'http://example.com/base/index.html#!/foo'
$location.path('/foo');
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo');
$location.search() === {}
expect($location.search()).toEqual({});
$location.search({a: 'b', c: true});
$location.absUrl() === 'http://example.com/base/index.html#!/foo?a=b&c'
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo?a=b&c');
$location.path('/new').search('x=y');
$location.absUrl() === 'http://example.com/base/index.html#!/new?x=y'
}
));
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/new?x=y');
});
});
```
## HTML5 mode
@@ -274,40 +274,50 @@ and updates the url in a way that never performs a full page reload.
### Example
```js
it('should show example', inject(
function($locationProvider) {
it('should show example', function() {
module(function($locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
},
function($location) {
});
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)
$location.path() === '/a'
expect($location.path()).toBe('/a');
$location.path('/foo');
$location.absUrl() === 'http://example.com/foo'
expect($location.absUrl()).toBe('http://example.com/foo');
$location.search() === {}
expect($location.search()).toEqual({});
$location.search({a: 'b', c: true});
$location.absUrl() === 'http://example.com/foo?a=b&c'
expect($location.absUrl()).toBe('http://example.com/foo?a=b&c');
$location.path('/new').search('x=y');
$location.url() === 'new?x=y'
$location.absUrl() === 'http://example.com/new?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)
$location.path() === '/new'
$location.search() === {x: 'y'}
expect($location.path()).toBe('/new');
expect($location.search()).toEqual({x: 'y'});
$location.path('/foo/bar');
$location.path() === '/foo/bar'
$location.url() === '/foo/bar?x=y'
$location.absUrl() === 'http://example.com/#!/foo/bar?x=y'
}
));
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
@@ -555,7 +565,7 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
</example>
####Browser in HTML5 Fallback mode (Hashbang mode)
#### Browser in HTML5 Fallback mode (Hashbang mode)
<example module="hashbang-mode" name="location-hashbang-mode">
<file name="index.html">
<div ng-controller="LocationController">
+253 -206
View File
@@ -6,20 +6,26 @@
# Animations
AngularJS provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
or with JavaScript code when it's defined as a factory.
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.
<div class="alert alert-info">
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.
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.
</div>
Animations are not available unless you include the {@link ngAnimate `ngAnimate` module} as a dependency within your application.
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`:
@@ -59,8 +65,9 @@ You may also want to setup a separate CSS file for defining CSS-based animations
## How they work
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within
your website, you can apply animations to it. Lets say for example that we have an HTML template with a repeater in it like so:
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class
attached to a HTML element within your application, you can apply animations to it. Lets say for
example that we have an HTML template with a repeater like so:
```html
<div ng-repeat="item in items" class="repeated-item">
@@ -68,22 +75,21 @@ your website, you can apply animations to it. Lets say for example that we have
</div>
```
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 you can see, the `repeated-item` class is present on the element that will be repeated and this
class will be used as a reference within our application's CSS and/or JavaScript animation code to
tell AngularJS to perform an animation.
As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add
a `ng-enter` class name to the element that is being added. When removed it will apply a `ng-leave` class name and when moved around
it will apply a `ng-move` class name.
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 for each of those events that
occur when ngRepeat triggers them:
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're using CSS transitions for when
the enter and move events are triggered
for the element that has the .repeated-item
class
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;
@@ -91,10 +97,8 @@ occur when ngRepeat triggers them:
}
/*
The ng-enter-active and ng-move-active
are where the transition destination properties
are set so that the animation knows what to
animate.
`.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 {
@@ -102,148 +106,149 @@ occur when ngRepeat triggers them:
}
/*
We're using CSS keyframe animations for when
the leave event is triggered for the element
that has the .repeated-item class
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; }
from { opacity: 1; }
to { opacity: 0; }
}
```
The same approach to animation can be used using JavaScript code (**jQuery is used within to perform animations**):
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) {
element.css('opacity',0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function(isCancelled) {
if(isCancelled) {
jQuery(element).stop();
}
}
},
leave : function(element, done) {
element.css('opacity', 1);
jQuery(element).animate({
opacity: 0
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function(isCancelled) {
if(isCancelled) {
jQuery(element).stop();
}
}
},
move : function(element, done) {
enter: function(element, done) {
// Initialize the element's opacity
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
// 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) {
jQuery(element).stop();
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();
}
};
},
// you can also capture these animation events
addClass : function(element, className, done) {},
removeClass : function(element, className, done) {}
// 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. If both CSS and JavaScript animation
code is present, and match the CSS class name on the element, then AngularJS will run both animations at the same time.
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
## Class and `ngClass` animation hooks
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks.
This means that if a CSS class is added to or removed from an element then an animation can be executed in between,
before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be
able to capture class changes if an **expression** or the **ng-class** directive is used on the element.)
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:
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="animate-css-class">
<file name="index.html">
<p>
<input type="button" value="set" ng-click="myCssVar='css-class'">
<input type="button" value="clear" ng-click="myCssVar=''">
<br>
<span ng-class="myCssVar">CSS-Animated Text</span>
</p>
</file>
<file name="style.css">
.css-class-add, .css-class-remove {
transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
}
<file name="index.html">
<p>
<button ng-click="myCssVar='css-class'">Set</button>
<button ng-click="myCssVar=''">Clear</button>
<br>
<span ng-class="myCssVar">CSS-Animated Text</span>
</p>
</file>
<file name="style.css">
.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,
.css-class-add.css-class-add-active {
color: red;
font-size: 3em;
}
.css-class-remove.css-class-remove-active {
font-size:1.0em;
color: black;
}
</file>
.css-class-remove.css-class-remove-active {
font-size: 1em;
color: black;
}
</file>
</example>
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 its life cycle.
The table below explains in detail which animation events are triggered
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:ngRepeat#animations ngRepeat} | enter, leave, and move |
| {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
| {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
| {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
| {@link ng.directive:ngIf#animations ngIf} | enter and leave |
| {@link ng.directive:ngClass#animations ngClass or &#123;&#123;class&#125;&#125;} | add and remove |
| {@link ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) |
| Directive | Supported Animations |
|-------------------------------------------------------------------------------|---------------------------------------------------------------------------|
| {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move |
| {@link ng.directive:ngIf#animations ngIf} | enter and leave |
| {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
| {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
| {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
| {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave |
| {@link ng.directive:ngClass#animations ngClass / {{class&#125;&#8203;&#125;} | add and remove |
| {@link ng.directive:ngClass#animations ngClassEven / ngClassOdd} | add and remove |
| {@link ng.directive:ngHide#animations ngHide} | add and remove (the `ng-hide` class) |
| {@link ng.directive:ngShow#animations ngShow} | add and remove (the `ng-hide` class) |
| {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) |
| {@link ng.directive:form#animation-hooks form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) |
| {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) |
For a full breakdown of the steps involved during each animation event, refer to the {@link ng.$animate API docs}.
For a full breakdown of the steps involved during each animation event, refer to the
{@link ng.$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.
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, attrs) {
return function(scope, element) {
element.on('click', function() {
if(element.hasClass('clicked')) {
if (element.hasClass('clicked')) {
$animate.removeClass(element, 'clicked');
} else {
$animate.addClass(element, 'clicked');
@@ -255,17 +260,19 @@ myModule.directive('my-directive', ['$animate', function($animate) {
## Animations on app bootstrap / page load
By default, animations are disabled when the Angular 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.
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} and the one after that to finish.
This ensures that the whole app has been compiled fully before animations are attempted.
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:
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) {
@@ -275,17 +282,50 @@ myModule.run(function($animate) {
## How to (selectively) enable, disable and skip animations
There are three 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.
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.
### In the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()}
### During the config: {@link $animateProvider#customFilter $animateProvider.customFilter()}
This function can be called in 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 only for specific classes (useful when
you are working with 3rd party animations), or exclude specific classes from getting animated.
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.
<div class="alert alert-success">
**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.
</div>
### 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) {
@@ -294,42 +334,43 @@ app.config(function($animateProvider) {
```
```css
/&#42; prefixed with animate- &#42;/
/&#42; prefixed with `animate-` &#42;/
.animate-fade-add.animate-fade-add-active {
transition: all 1s linear;
opacity: 0;
}
```
The classNameFilter approach generally applies the biggest speed boost, because the matching is
done before any 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.
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.
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)`. This is the
most flexible way to change the animation state. For example, even if you have used it to disable
animations on a parent element, you can still re-enable it for a child element. And compared to the
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
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}.
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 the class,
you can skip an animation:
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{
.my-class {
transition: transform 2s;
}
@@ -340,23 +381,23 @@ you can skip an animation:
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}.
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.
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`).
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:
@@ -368,8 +409,9 @@ Here's an example where you might see flickering:
</div>
```
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:
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 {
@@ -379,66 +421,71 @@ In that case, you can add styles to the CSS that make sure the element stays hid
/* Other animation styles ... */
```
## Preventing Collisions with Existing Animations and Third Party Libraries
By default, any `ngAnimate` enabled directives will assume any 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`.
## Preventing collisions with existing animations and third-party libraries
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:
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
@keyframes rotating {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
.spinner {
animation: rotating 2s linear infinite;
}
.spinner {
animation: rotating 2s linear infinite;
@keyframes rotating {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
```
Now, when the `ngIf` 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
noticable delay.
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 reuseable.
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 an animation. Simply overwrite the transition / animation duration. In the
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 {
transition: 0s none;
animation: 0s none;
animation: 0s none;
transition: 0s none;
}
```
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
than any styles that are independent from ngAnimate.
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 two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
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 for elements outside of the Angular application DOM tree: {@link ng.$animate#pin $animate.pin()}
## Enable animations outside of the application DOM tree: {@link ng.$animate#pin $animate.pin()}
Before animating, `ngAnimate` checks to see if the element being animated is inside the application DOM tree,
and if it is not, no animation is run. Usually, this is not a problem as most apps use the `ngApp`
attribute / bootstrap the app on the `html` or `body` element.
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 and modal
elements as the last child in the body tag.
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(elementToAnimate, parentHost)`} to specify that an
element 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.
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}.
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 within the AngularJS phonecat tutorial}.
To see a complete demo, see the {@link tutorial/step_14 animation step in the phonecat tutorial}.
+2 -2
View File
@@ -40,8 +40,8 @@ initialization.
<html ng-app>
3. If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
`ng:`.)
when running the page in the XHTML mode. (This is here for historical reasons, and we no longer
recommend use of `ng:`.)
<html xmlns:ng="http://angularjs.org">
+6 -7
View File
@@ -5,19 +5,18 @@
# Component Router
<div class="alert alert-info">
**Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
<div class="alert alert-danger">
**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)).
</div>
This guide describes the new Component Router for AngularJS 1.5.
<div class="alert alert-info">
If you are looking for information about the old router for AngularJS 1.4 and
earlier have a look at the {@link ngRoute} module.
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 Angular 2 then
check out the [Angular 2 Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
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).
</div>
## Overview
+2 -16
View File
@@ -39,14 +39,8 @@ Components can be registered using the `.component()` method of an Angular modul
});
</file>
<file name="heroDetail.js">
function HeroDetailController() {
}
angular.module('heroApp').component('heroDetail', {
templateUrl: 'heroDetail.html',
controller: HeroDetailController,
bindings: {
hero: '='
}
@@ -78,6 +72,7 @@ It's also possible to add components via {@link $compileProvider#component} in a
| 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) |
@@ -461,7 +456,7 @@ The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
**Controller Test:**
```js
describe('component: heroDetail', function() {
describe('HeroDetailController', function() {
var $componentController;
beforeEach(module('heroApp'));
@@ -469,15 +464,6 @@ describe('component: heroDetail', function() {
$componentController = _$componentController_;
}));
it('should expose a `hero` object', function() {
// Here we are passing actual bindings to the component
var bindings = {hero: {name: 'Wolverine'}};
var ctrl = $componentController('heroDetail', null, bindings);
expect(ctrl.hero).toBeDefined();
expect(ctrl.hero.name).toBe('Wolverine');
});
it('should call the `onDelete` binding, when deleting the hero', function() {
var onDeleteSpy = jasmine.createSpy('onDelete');
var bindings = {hero: {}, onDelete: onDeleteSpy};
+10 -4
View File
@@ -120,11 +120,13 @@ The other forms shown above are accepted for legacy reasons but we advise you to
### Directive types
`$compile` can match directives based on element names, attributes, class names, as well as comments.
`$compile` can match directives based on element names (E), attributes (A), class names (C),
and comments (M).
All of the Angular-provided directives match attribute name, tag name, comments, or class name.
The following demonstrates the various ways a directive (`myDir` in this case) can be referenced
from within a template:
The built-in AngularJS directives show in their documentation page which type of matching they support.
The following demonstrates the various ways a directive that matches all 4 types
(`myDir` in this case) can be referenced from within a template.
```html
<my-dir></my-dir>
@@ -133,6 +135,10 @@ from within a template:
<span class="my-dir: exp;"></span>
```
A directive can specify which of the 4 matching types it supports in the
{@link ng.$compile#-restrict- `restrict`} property of the directive definition object.
The default is `EA`.
<div class="alert alert-success">
**Best Practice:** Prefer using directives via tag name and attributes over comment and class names.
Doing so generally makes it easier to determine what directives a given element matches.
+7 -7
View File
@@ -241,7 +241,7 @@ An expression that starts with `::` is considered a one-time expression. One-tim
will stop recalculating once they are stable, which happens after the first digest if the expression
result is a non-undefined value (see value stabilization algorithm below).
<example module="oneTimeBidingExampleApp" name="expression-one-time">
<example module="oneTimeBindingExampleApp" name="expression-one-time">
<file name="index.html">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Click Me</button>
@@ -250,7 +250,7 @@ result is a non-undefined value (see value stabilization algorithm below).
</div>
</file>
<file name="script.js">
angular.module('oneTimeBidingExampleApp', []).
angular.module('oneTimeBindingExampleApp', []).
controller('EventController', ['$scope', function($scope) {
var counter = 0;
var names = ['Igor', 'Misko', 'Chirayu', 'Lucas'];
@@ -265,24 +265,24 @@ result is a non-undefined value (see value stabilization algorithm below).
</file>
<file name="protractor.js" type="protractor">
it('should freeze binding after its value has stabilized', function() {
var oneTimeBiding = element(by.id('one-time-binding-example'));
var oneTimeBinding = element(by.id('one-time-binding-example'));
var normalBinding = element(by.id('normal-binding-example'));
expect(oneTimeBiding.getText()).toEqual('One time binding:');
expect(oneTimeBinding.getText()).toEqual('One time binding:');
expect(normalBinding.getText()).toEqual('Normal binding:');
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Igor');
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Misko');
element(by.buttonText('Click Me')).click();
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Lucas');
});
</file>
+3 -2
View File
@@ -119,7 +119,8 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http
### Books
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
* [AngularJS Essentials (Free eBook)](https://www.packtpub.com/packt/free-ebook/angularjs-essentials) by Rodrigo Branas
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS in Action](https://www.manning.com/books/angularjs-in-action) by Lukas Ruebbelke
* [AngularJS: Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
@@ -138,7 +139,7 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
* **Paid online:**
[Pluralsight (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Pluralsight](https://www.pluralsight.com/search?q=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs),
+2 -2
View File
@@ -28,8 +28,8 @@ for other directives to augment its behavior.
<form novalidate class="simple-form">
<label>Name: <input type="text" ng-model="user.name" /></label><br />
<label>E-mail: <input type="email" ng-model="user.email" /></label><br />
Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
<label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
Best Editor: <label><input type="radio" ng-model="user.preference" value="vi" />vi</label>
<label><input type="radio" ng-model="user.preference" value="emacs" />emacs</label><br />
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
+8 -8
View File
@@ -26,7 +26,7 @@ directive}. Additionally, you can use {@link guide/i18n#messageformat-extension
All localizable Angular components depend on locale-specific rule sets managed by the {@link
ng.$locale `$locale` service}.
There a few examples that showcase how to use Angular filters with various locale rule sets in the
There are a few examples that showcase how to use Angular filters with various locale rule sets in the
[`i18n/e2e` directory](https://github.com/angular/angular.js/tree/master/i18n/e2e) of the Angular
source code.
@@ -85,7 +85,7 @@ requires German locale, you would serve index_de-de.html which will look somethi
Both approaches described above require you to prepare different `index.html` pages or JavaScript
files for each locale that your app may use. You also need to configure your server to serve
the correct file that correspond to the desired locale.
the correct file that corresponds to the desired locale.
The second approach (including the locale JavaScript file in `index.html`) may be slower because
an extra script needs to be loaded.
@@ -281,18 +281,18 @@ categories as you need.
#### Selection Keywords
The selection keywords can be either exact matches or language dependent [plural
categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
categories](http://cldr.unicode.org/index/cldr-spec/plural-rules).
Exact matches are written as the equal sign followed by the exact value. `=0`, `=1`, `=2` and
`=123` are all examples of exact matches. Note that there should be no space between the equal sign
and the numeric value.
Plural category matches are single words corresponding to the [plural
categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) of
the CLDR plural category spec. These categories vary by locale. The "en" (English) locale, for
example, defines just "one" and "other" while the "ga" (Irish) locale defines "one", "two", "few",
"many" and "other". Typically, you would just write the categories for your language. During
translation, the translators will add or remove more categories depending on the target locale.
categories](http://cldr.unicode.org/index/cldr-spec/plural-rules) of the CLDR plural category spec.
These categories vary by locale. The "en" (English) locale, for example, defines just "one" and
"other" while the "ga" (Irish) locale defines "one", "two", "few", "many" and "other". Typically,
you would just write the categories for your language. During translation, the translators will add
or remove more categories depending on the target locale.
Exact matches always win over keyword matches. Therefore, if you define both `=0` and `zero`, when
the value of the expression is zero, the `=0` message is the one that will be selected. (The
+4 -4
View File
@@ -7,7 +7,7 @@
<div class="alert alert-warning">
**Note:** AngularJS 1.3 has dropped support for IE8. Read more about it on
[our blog](http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
[our blog](https://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
AngularJS 1.2 will continue to support IE8, but the core team does not plan to spend time
addressing issues specific to IE8 or earlier.
</div>
@@ -19,7 +19,7 @@ on IE.
The project currently supports and will attempt to fix bugs for IE9 and above. The continuous
integration server runs all the tests against IE9, IE10, and IE11. See
[Travis CI](https://travis-ci.org/angular/angular.js) and
[ci.angularjs.org](http://ci.angularjs.org).
[ci.angularjs.org](https://ci.angularjs.org).
We do not run tests on IE8 and below. A subset of the AngularJS functionality may work on these
browsers, but it is up to you to test and decide whether it works for your particular app.
@@ -27,8 +27,8 @@ browsers, but it is up to you to test and decide whether it works for your parti
To ensure your Angular application works on IE please consider:
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome, Firefox,
Safari and Edge but does not work in Internet Explorer (even 11).
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
with `type="submit"` before Angular has a chance to interpolate it.
+1 -1
View File
@@ -32,7 +32,7 @@ If the interpolated value is not a `String`, it is computed as follows:
- `undefined` and `null` are converted to `''`
- if the value is an object that is not a `Number`, `Date` or `Array`, $interpolate looks for
a custom `toString()` function on the object, and uses that. Custom means that
`myObject.toString !== `Object.prototype.toString`.
`myObject.toString !== Object.prototype.toString`.
- if the above doesn't apply, `JSON.stringify` is used.
### Binding to boolean attributes
+130 -22
View File
@@ -88,7 +88,9 @@ commits for more info.
- **jqLite** is more aligned to jQuery 3, which required the following changes
(see [details](guide/migration#migrate1.5to1.6-ng-misc-jqLite) below):
- Keys passed to `.data()` and `.css()` are now camelCased in the same as jQuery does it.
- Keys passed to `.data()` and `.css()` are now camelCased in the same way as the jQuery methods
do.
- Getting/setting boolean attributes no longer takes the corresponding properties into account.
- Setting boolean attributes to empty string no longer removes the attribute.
- Calling `.val()` on a multiple select will always return an array, even if no option is
selected.
@@ -416,7 +418,7 @@ will be removed in a future version, so we strongly recommend migrating your app
rely on it as soon as possible.
Initialization logic that relies on bindings being present should be put in the controller's
`$onInit()` method, which is guarranteed to always be called _after_ the bindings have been
`$onInit()` method, which is guaranteed to always be called _after_ the bindings have been
assigned.
Before:
@@ -482,14 +484,6 @@ lifecycle hook), you may need to manually call `$onInit()` from your constructor
})
```
<hr />
<minor />
**Due to [13c252](https://github.com/angular/angular.js/commit/13c2522baf7c8f616b2efcaab4bffd54c8736591)**,
on **IE11 only**, consecutive text nodes will always get merged. Previously, they would not get
merged if they had no parent. The new behavior, which fixes an IE11 bug affecting interpolation
under certain circumstances, might in some edge-cases have unexpected side effects that you should
be aware of. Please, check the commit message for more details.
<hr />
<minor />
**Due to [04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa)**,
@@ -501,7 +495,7 @@ running at `https://docs.angularjs.org` then the following will fail:
<link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet" />
```
By default, only URLs with the same domain and prototocl as the application document are considered
By default, only URLs with the same domain and protocol as the application document are considered
safe in the `RESOURCE_URL` context. To use URLs from other domains and/or protocols, you may either
whitelist them or wrap them into a trusted value by calling `$sce.trustAsResourceUrl(url)`.
@@ -586,7 +580,7 @@ trust a URL:
```js
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhiteList([
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads.
'self',
// Allow JSONP calls that match this pattern
@@ -880,6 +874,48 @@ var bgColor = elem.css('background-color');
var bgColor = elem.css('backgroundColor');
```
<hr />
<major />
**Due to [7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**,
getting/setting boolean attributes will no longer take the corresponding properties into account.
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);
```
<hr />
<major />
**Due to [3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**,
@@ -1169,7 +1205,7 @@ with an unencoded `;` character.
Previously, in cases where `ngView` was loaded asynchronously, `$route` (and its dependencies) might
also have been instantiated asynchronously.
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
Although this is not expected to have unwanted side-effects in normal application behavior, it may
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
request will be made for the default route's template. If not properly "trained", `$httpBackend`
will complain about this unexpected request. You can restore the previous behavior (and avoid
@@ -1276,7 +1312,7 @@ jqLite/jQuery collections
#### Helper Functions:
The {@link angular.lowercase `angular.lowercase`} and {@link angular.uppercase `angular.uppercase`} functions have been **deprecated** and will be removed
The `angular.lowercase` and `angular.uppercase` functions have been **deprecated** and will be removed
in version 1.7.0. It is recommended to use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) and [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) functions instead.
@@ -1410,9 +1446,8 @@ For more info on the topic, you can take a look at this
## Migrating from 1.3 to 1.4
Angular 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions `and some fixes to core filters:
`limitTo` and `filter`.
AngularJS 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions`, `ngPattern`, `pattern` and some fixes to core filters: `limitTo` and `filter`.
The reason for the ngAnimate refactor was to fix timing issues and to expose new APIs to allow
for developers to construct more versatile animations. We now have access to `$animateCss`
@@ -1425,9 +1460,9 @@ to render error messages with ngMessages that are listed with a directive such a
involves pulling error message data from a server and then displaying that data via the mechanics of ngMessages. Be
sure to read the breaking change involved with `ngMessagesInclude` to upgrade your template code.
Other changes, such as the ordering of elements with ngRepeat and ngOptions, may also affect the behavior of your
application. And be sure to also read up on the changes to `$cookies`. The migration jump from 1.3 to 1.4 should be
relatively straightforward otherwise.
Other changes, such as the ordering of elements with ngRepeat and ngOptions and the way ngPattern and pattern directives
validate the regex, may also affect the behavior of your application. And be sure to also read up on the changes to `$cookies`.
The migration jump from 1.3 to 1.4 should be relatively straightforward otherwise.
@@ -1531,7 +1566,7 @@ class based animations (animations triggered via ngClass) in order to ensure tha
### Forms (`ngMessages`, `ngOptions`, `select`)
### Forms (`ngMessages`, `ngOptions`, `select`, `ngPattern` and `pattern`)
#### ngMessages
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
@@ -1639,6 +1674,79 @@ ngModelCtrl.$formatters.push(function(value) {
});
```
#### ngPattern and pattern
Due to [0e001084](https://github.com/angular/angular.js/commit/0e001084ffff8674efad289d37cb16cc4e46b50a),
The `ngPattern` and `pattern` directives will validate the regex
against the `$viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the `$modelValue`
(the result of the $parsers) was validated.
This fixes issues where `input[date]` and `input[number]` cannot
be validated because the `$viewValue` string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.
This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.
If you rely on the pattern being validated against the `$modelValue`,
you must create your own validator directive that overwrites
the built-in pattern validator:
```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
compile: function() {
var regexp, patternExp;
return {
pre: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.$observe('pattern', function(regex) {
/**
* The built-in directive will call our overwritten validator
* (see below). We just need to update the regex.
* The preLink fn guaranetees our observer is called first.
*/
if (isString(regex) && regex.length > 0) {
regex = new RegExp('^' + regex + '$');
}
if (regex && !regex.test) {
//The built-in validator will throw at this point
return;
}
regexp = regex || undefined;
});
},
post: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
regexp, patternExp = attr.ngPattern || attr.pattern;
//The postLink fn guarantees we overwrite the built-in pattern validator
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) ||
isUndefined(regexp) ||
regexp.test(value);
};
}
};
}
};
});
```
### form
@@ -2605,7 +2713,7 @@ See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262
### Interpolations inside DOM event handlers are now disallowed
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers
means that the interpolated value is a JS string that is evaluated. Storing or generating such
strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other
Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which
+2 -2
View File
@@ -221,8 +221,8 @@ it('should clear messages after alert', function() {
notify('two');
notify('third');
expect(mock.alert.callCount).toEqual(2);
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
expect(mock.alert.calls.count()).toEqual(2);
expect(mock.alert.calls.mostRecent().args).toEqual(["more\ntwo\nthird"]);
});
```
+1 -1
View File
@@ -30,7 +30,7 @@ curly-brace {@link expression expression} bindings:
string expression 'buttonText'
wrapped in "{{ }}" markup -->
<button ng-click="changeFoo()">{{buttonText}}</button>
<script src="angular.js">
<script src="angular.js"></script>
</body>
</html>
```
+3 -3
View File
@@ -140,13 +140,13 @@ tests once on Chrome run:
grunt test:unit
```
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
To run the tests on other browsers (Chrome, ChromeCanary, Firefox and Safari are pre-configured) use:
```shell
grunt test:unit --browsers=Opera,Firefox
grunt test:unit --browsers=Chrome,Firefox
```
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
Note there should be _no spaces between browsers_. `Chrome, Firefox` is INVALID.
During development, however, it's more productive to continuously run unit tests every time the source or test files
change. To execute tests in this mode run:
+95 -47
View File
@@ -22,39 +22,89 @@ So it's definitely not a plugin or some other native browser extension.
### What is the AngularJS versioning strategy?
In Angular 1 we do not allow intentional breaking changes to appear in versions where only the "patch"
In AngularJS we do not allow intentional breaking changes to appear in versions where only the "patch"
number changes. For example between 1.3.12 and 1.3.13 there can be no breaking changes. We do allow
breaking changes happen between "minor" number changes. For example between 1.3.15 and 1.4.0 there
will be a number of breaking changes. We also allow breaking changes between beta releases of Angular.
are a number of breaking changes. That means AngularJS does not follow
[semantic versioning (semver)](http://semver.org/) where breaking changes are only
allowed when the "major" version changes.
We also allow breaking changes between beta releases of AngularJS.
For example between 1.4.0-beta.4 and 1.4.0-beta.5 there may be breaking changes. We try hard to minimize
these kinds of change only to those where there is a strong use case such as a strongly requested feature
improvement, a considerable simplification of the code or a measurable performance improvement.
improvement, a considerable simplification of the code, a measurable performance improvement, or a better
developer experience (especially with regard to upgrading to Angular).
When adding new code to branches of Angular, have a very stringent commit policy:
When we are making a release we generate updates to the changelog directly from the commits. This
generated update contains a highlighted section that contains all the breaking changes that have been
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
changes and so can application developers when they are deciding whether to update to a new version of
Angular.
- Every commit must contain tests and documentation updates alongside the code changes and that all the
tests must pass;
Features with non-breaking changes can also appear in the "patch" version, e.g. in version 1.6.3 there might
be a feature that is not available in 1.6.2.
Finally, deprecation of features might also appear in "minor" version updates. That means the features
will still work in this version, but sometimes must be activated specifically.
#### When are deprecated features removed from the library?
Most of the time we remove a deprecated feature in a next minor version bump. For example, the
`preAssignBindingsEnabled` `$compileProvider` method was defined in AngularJS `1.5.10`, deprecated in `1.6` and
will be removed in `1.7`.
In case of jqLite we apply a different strategy - we deprecate features that have an equivalent in jQuery that
is also deprecated but we only remove the feature once it's removed from jQuery to improve compatibility between
jqLite and jQuery. One such example is the `bind` method, deprecated in favor of `on` but unlikely to be removed
from jqLite any time soon.
#### What is the version compatibility between AngularJS main and optional modules?
AngularJS code is separated into a main module ("angular"), and a few different optional modules
("angular-animate", "angular-route" etc) that are dependant on the main module.
When a new AngularJS version is released, all modules are updated to the new version.
This means that the main module and the optional modules must always have the exact same version,
down to the patch number, otherwise your application might break.
Therefore you must always explicitly lock down your dependencies, for example in the package.json,
the following means that "angular" and "angular-animate" are always updated to the same version:
```
{
"angular": "~1.6.0",
"angular-animate": "~1.6.0"
}
```
If you define exact versions, make sure core and optional modules are the same:
```
{
"angular": "1.6.3",
"angular-animate": "1.6.3"
}
```
#### How does AngularJS ensure code quality and guard against regressions?
When adding new code to AngularJS, we have a very stringent commit policy:
- Every commit must pass all existing tests, contain tests for code changes, and update the documentation
- Commit messages must be written in a specific manner that allows us to parse them and extract the changes
for release notes.
for release notes ([see the contributing guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md))
The Angular code base has a very large set of unit tests (over 4000) and end to end tests, which are pretty
comprehensive. This means that a breaking change will require one or more tests to be changed to allow the
The AngularJS code base has a very large set of unit tests and end-to-end tests. This means that a breaking change will require one or more tests to be changed to allow the
tests to pass. So when a commit includes tests that are being removed or modified, this is a flag that the
code might include a breaking change. When reviewing the commit we can then decide whether there really is
a breaking change and if it is appropriate for the branch to which it is being merged. If so, then we
require that the commit message contains an appropriate breaking change message.
Additionally, when a commit lands in our master repository it is synced to Google where we test it against
over 2000 applications using the test suites of these applications. This allows us to catch regressions
Additionally, commits are periodically synced to Google where we test it against applications using
the test suites of these applications. This allows us to catch regressions
quickly before a release. We've had a pretty good experience with this setup. Only bugs that affect features
not used at Google or without sufficient test coverage, have a chance of making it through.
Lastly, when we are making a release we generate updates to the changelog directly from the commits. This
generated update contains a highlighted section that contains all the breaking changes that have been
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
changes and so can application developers when they are deciding whether to update to a new version of
Angular.
### Is AngularJS a templating system?
@@ -86,20 +136,18 @@ Yes. See instructions in {@link downloading}.
### What browsers does Angular work with?
### What browsers does AngularJS work with?
We run our extensive test suite against the following browsers: the latest versions of Chrome,
Firefox, Safari, and Safari for iOs, as well as Internet Explorer versions 9-11. See {@link guide/ie
Internet Explorer Compatibility} for more details on supporting legacy IE browsers.
Firefox, Safari, and Safari for iOS, as well as Internet Explorer versions 9-11. See
{@link guide/ie Internet Explorer Compatibility} for more details on supporting legacy IE browsers.
If a browser is untested, it doesn't mean it won't work; for example, older Android (2.3.x)
is supported in the sense that we avoid the dot notation for reserved words as property names,
but we don't actively test changes against it. You can also expect browsers to work that share
a large part of their codebase with a browser we test, such as Opera > version 12
If a browser is untested, it doesn't mean it won't work. You can also expect browsers to work that
share a large part of their codebase with a browser we test, such as Opera 15 or newer
(uses the Blink engine), or the various Firefox derivatives.
### What's Angular's performance like?
### What's AngularJS's performance like?
The startup time heavily depends on your network connection, state of the cache, browser used and
available hardware, but typically we measure bootstrap time in tens or hundreds of milliseconds.
@@ -114,40 +162,40 @@ illustration, we typically build snappy apps with hundreds or thousands of activ
The size of the file is ~50KB compressed and minified.
### Can I use the open-source Closure Library with Angular?
### Can I use the open-source Closure Library with AngularJS?
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
in Angular.
in AngularJS.
### Does Angular use the jQuery library?
### Does AngularJS use the jQuery library?
Yes, Angular can use [jQuery](http://jquery.com/) if it's present in your app when the
application is being bootstrapped. If jQuery is not present in your script path, Angular falls back
Yes, AngularJS can use [jQuery](http://jquery.com/) if it's present in your app when the
application is being bootstrapped. If jQuery is not present in your script path, AngularJS falls back
to its own implementation of the subset of jQuery that we call {@link angular.element jQLite}.
Angular 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with Angular
AngularJS 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with AngularJS
but we don't guarantee that.
### What is testability like in Angular?
### What is testability like in AngularJS?
Very testable and designed this way from the ground up. It has an integrated dependency injection
framework, provides mocks for many heavy dependencies (server-side communication). See
{@link ngMock} for details.
### How can I learn more about Angular?
### How can I learn more about AngularJS?
Watch the July 17, 2012 talk
"[AngularJS Intro + Dependency Injection](http://www.youtube.com/watch?v=1CpiB3Wk25U)".
### How is Angular licensed?
### How is AngularJS licensed?
The [MIT License](https://github.com/angular/angular.js/blob/master/LICENSE).
### Can I download and use the Angular logo artwork?
### Can I download and use the AngularJS logo artwork?
Yes! You can find design files in our github repository, under "[angular.js/images/logo](https://github.com/angular/angular.js/tree/master/images/logo)"
The logo design is licensed under a "[Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/)". If you have some other use in mind, contact us.
@@ -168,7 +216,7 @@ For a smaller order, or for other countries, we suggest downloading the logo art
## Common Pitfalls
The Angular support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of Angular fall into.
The AngularJS support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of AngularJS fall into.
This document aims to point them out before you discover them the hard way.
### DOM Manipulation
@@ -179,13 +227,13 @@ Use built-in directives, or write your own where necessary, to do your DOM manip
See below about duplicating functionality.
If you're struggling to break the habit, consider removing jQuery from your app.
Really. Angular has the $http service and powerful directives that make it almost always unnecessary.
Angular's bundled jQLite has a handful of the features most commonly used in writing Angular directives, especially binding to events.
Really. AngularJS has the $http service and powerful directives that make it almost always unnecessary.
AngularJS's bundled jQLite has a handful of the features most commonly used in writing AngularJS directives, especially binding to events.
### Trying to duplicate functionality that already exists
There's a good chance that your app isn't the first to require certain functionality.
There are a few pieces of Angular that are particularly likely to be reimplemented out of old habits.
There are a few pieces of AngularJS that are particularly likely to be reimplemented out of old habits.
**ng-repeat**
@@ -198,7 +246,7 @@ Store the data from the server in an array on your `$scope`, and bind it to the
**ng-show**
`ng-show` gets this frequently too.
Conditionally showing and hiding things using jQuery is a common pattern in other apps, but Angular has a better way.
Conditionally showing and hiding things using jQuery is a common pattern in other apps, but AngularJS has a better way.
`ng-show` (and `ng-hide`) conditionally show and hide elements based on boolean expressions.
Describe the conditions for showing and hiding an element in terms of `$scope` variables:
@@ -211,7 +259,7 @@ Note especially the powerful `ng-switch` that should be used instead of several
`ng-class` is the last of the big three.
Conditionally applying classes to elements is another thing commonly done manually using jQuery.
Angular, of course, has a better way.
AngularJS, of course, has a better way.
You can give `ng-class` a whitespace-separated set of class names, and then it's identical to ordinary `class`.
That's not very exciting, so there's a second syntax:
@@ -225,22 +273,22 @@ Note also the handy `ng-class-even` and `ng-class-odd`, and the related though s
### `$watch` and `$apply`
Angular's two-way data binding is the root of all awesome in Angular.
AngularJS's two-way data binding is the root of all awesome in AngularJS.
However, it's not magic, and there are some situations where you need to give it a nudge in the right direction.
When you bind a value to an element in Angular using `ng-model`, `ng-repeat`, etc., Angular creates a `$watch` on that value.
When you bind a value to an element in AngularJS using `ng-model`, `ng-repeat`, etc., AngularJS creates a `$watch` on that value.
Then whenever a value on a scope changes, all `$watch`es observing that element are executed, and everything updates.
Sometimes, usually when you're writing a custom directive, you will have to define your own `$watch` on a scope value to make the directive react to changes.
On the flip side, sometimes you change a scope value in some code, but the app doesn't react to it.
Angular checks for scope variable changes after pieces of your code have finished running; for example, when `ng-click` calls a function on your scope, Angular will check for changes and react.
However, some code is outside of Angular and you'll have to call `scope.$apply()` yourself to trigger the update.
AngularJS checks for scope variable changes after pieces of your code have finished running; for example, when `ng-click` calls a function on your scope, AngularJS will check for changes and react.
However, some code is outside of AngularJS and you'll have to call `scope.$apply()` yourself to trigger the update.
This is most commonly seen in event handlers in custom directives.
### Combining `ng-repeat` with other directives
`ng-repeat` is extremely useful, one of the most powerful directives in Angular.
`ng-repeat` is extremely useful, one of the most powerful directives in AngularJS.
However the transformation it applies to the DOM is substantial.
Therefore applying other directives (such as `ng-show`, `ng-controller` and others) to the same element as `ng-repeat` generally leads to problems.
@@ -249,7 +297,7 @@ If you want to apply a directive to each inner piece of the repeat, put it on a
### `$rootScope` exists, but it can be used for evil
Scopes in Angular form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
Scopes in AngularJS form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Occasionally there are pieces of data that you want to make global to the whole app.
+1 -1
View File
@@ -32,7 +32,7 @@ tutorials.
## Subscribe
* Subscribe to the [mailing list](http://groups.google.com/forum/?fromgroups#!forum/angular). Ask questions here!
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F&region=follow_link&screen_name=angularjs&source=followbutton&variant=2.0)
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F&region=follow_link&screen_name=angular&source=followbutton&variant=2.0)
* Add us to your circles on [Google+](https://plus.google.com/110323587230527980117/posts)
## Read more
+2 -2
View File
@@ -41,7 +41,7 @@ maintain. As we add more and more features, our files will get bigger and bigger
difficult to navigate and find the code we are looking for.
Instead we should put each feature/entity in its own file. Each stand-alone controller will be
defined in its own file, each component will be defined in each own file, etc.
defined in its own file, each component will be defined in its own file, etc.
Luckily, we don't need to change anything with respect to that guideline in our code, since we have
already defined our `phoneList` component in its own `phone-list.component.js` file. Good job!
@@ -54,7 +54,7 @@ We will keep this in mind though, as we add more features.
So, now that we learned we should put everything in its own file, our `app/` directory will soon be
full with dozens of files and specs (remember we keep our unit test files next to the corresponding
source code files). What's more important, logically related files will not be grouped together; it
will be really difficult of locate all files related to a specific section of the application and
will be really difficult to locate all files related to a specific section of the application and
make a change or fix a bug.
So, what shall we do?
+2 -2
View File
@@ -194,7 +194,7 @@ angular.module('phonecatApp', [
```
Now, in addition to the core services and directives, we can also configure the `$route` service
(using it's provider) for our application. In order to be able to quickly locate the configuration
(using its provider) for our application. In order to be able to quickly locate the configuration
code, we put it into a separate file and used the `.config` suffix.
<br />
@@ -402,7 +402,7 @@ You can now rerun `npm run protractor` to see the tests run (and hopefully pass)
<div></div>
* Try to add a `{{$ctrl.phoneId}` binding in the template string for the phone details view:
* Try to add a `{{$ctrl.phoneId}}` binding in the template string for the phone details view:
```js
when('/phones/:phoneId', {
+4 -1
View File
@@ -173,7 +173,10 @@ angular.module('phoneList', ['core.phone']);
**`app/phone-detail/phone-detail.module.js`:**
```js
angular.module('phoneDetail', ['core.phone']);
angular.module('phoneDetail', [
'ngRoute',
'core.phone'
]);
```
<br />
+2 -2
View File
@@ -46,14 +46,14 @@ Since we are using [Bower][bower] to install client-side dependencies, this step
"angular-resource": "1.5.x",
"angular-route": "1.5.x",
"bootstrap": "3.3.x",
"jquery": "2.2.x"
"jquery": "3.2.x"
}
}
```
* `"angular-animate": "1.5.x"` tells bower to install a version of the angular-animate module that
is compatible with version 1.5.x of Angular.
* `"jquery": "2.2.x"` tells bower to install the latest patch release of the 2.2 version of jQuery.
* `"jquery": "3.2.x"` tells bower to install the latest patch release of the 3.2 version of jQuery.
Note that this is not an Angular library; it is the standard jQuery library. We can use bower to
install a wide range of 3rd party libraries.
+6 -21
View File
@@ -1,10 +1,8 @@
'use strict';
var gulp = require('gulp');
var log = require('gulp-util').log;
var concat = require('gulp-concat');
var eslint = require('gulp-eslint');
var bower = require('bower');
var Dgeni = require('dgeni');
var merge = require('event-stream').merge;
var path = require('canonical-path');
@@ -18,7 +16,6 @@ var rename = require('gulp-rename');
// See clean and bower for async tasks, and see assets and doc-gen for dependent tasks below
var outputFolder = '../build/docs';
var bowerFolder = 'bower_components';
var src = 'app/src/**/*.js';
var ignoredFiles = '!src/angular.bind.js';
@@ -57,8 +54,8 @@ var getMergedEslintConfig = function(filepath) {
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
pattern = pattern || '/**/*';
sourceFolder = sourceFolder || bowerFolder;
packageFile = packageFile || 'bower.json';
sourceFolder = sourceFolder || '../node_modules';
packageFile = packageFile || 'package.json';
var version = require(path.resolve(sourceFolder, component, packageFile)).version;
return gulp
.src(sourceFolder + '/' + component + pattern)
@@ -66,18 +63,6 @@ var copyComponent = function(component, pattern, sourceFolder, packageFile) {
};
gulp.task('bower', function() {
var bowerTask = bower.commands.install();
bowerTask.on('log', function(result) {
log('bower:', result.id, result.data.endpoint.name);
});
bowerTask.on('error', function(error) {
log(error);
});
return bowerTask;
});
gulp.task('build-app', function() {
var file = 'docs.js';
var minFile = 'docs.min.js';
@@ -94,7 +79,7 @@ gulp.task('build-app', function() {
});
gulp.task('assets', ['bower'], function() {
gulp.task('assets', function() {
var JS_EXT = /\.js$/;
return merge(
gulp.src(['img/**/*']).pipe(gulp.dest(outputFolder + '/img')),
@@ -113,15 +98,15 @@ gulp.task('assets', ['bower'], function() {
})),
copyComponent('bootstrap', '/dist/**/*'),
copyComponent('open-sans-fontface'),
copyComponent('lunr.js', '/*.js'),
copyComponent('lunr', '/*.js'),
copyComponent('google-code-prettify'),
copyComponent('jquery', '/dist/*.js'),
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
copyComponent('marked', '/**/*.js')
);
});
gulp.task('doc-gen', ['bower'], function() {
gulp.task('doc-gen', function() {
var dgeni = new Dgeni([require('./config')]);
return dgeni.generate().catch(function() {
process.exit(1);
+45 -4
View File
@@ -65,6 +65,21 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
goog.i18n.currency.tier2Enabled_ = false;
/**
* Tests if currency is available.
*
* Note: If the currency is not available it might be in the tier2 currency set:
* {@link goog.i18n.currency.CurrencyInfoTier2}. If that is the case call
* {@link goog.i18n.currency.addTier2Support} before calling any other function
* in this namespace.
*
* @param {string} currencyCode Currency code to tested.
* @return {boolean} If the currency is available.
*/
goog.i18n.currency.isAvailable = function(currencyCode) {
return currencyCode in goog.i18n.currency.CurrencyInfo;
};
/**
* This function will add tier2 currency support. Be default, only tier1
* (most popular currencies) are supported. If an application really needs
@@ -84,6 +99,7 @@ goog.i18n.currency.addTier2Support = function() {
/**
* Deprecated.
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
* currency sign is added if it is different from currency code. Each currency
* is unique in this form. The negative side is that ISO code looks weird in
@@ -92,6 +108,9 @@ goog.i18n.currency.addTier2Support = function() {
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Global currency pattern string for given currency.
* @deprecated Format numbers using {@link goog.i18n.NumberFormat} with
* {@link goog.i18n.NumberFormat.Format.CURRENCY} and
* {@link goog.i18n.NumberFormat.CurrencyStyle.GLOBAL}
*/
goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
@@ -119,12 +138,16 @@ goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
/**
* Deprecated.
* Local currency pattern is the most frequently used pattern in currency's
* native region. It does not care about how it is distinguished from other
* currencies.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Local currency pattern string for given currency.
* @deprecated Format numbers using {@link goog.i18n.NumberFormat} with
* {@link goog.i18n.NumberFormat.Format.CURRENCY} and
* {@link goog.i18n.NumberFormat.CurrencyStyle.LOCAL}
*/
goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
@@ -145,6 +168,7 @@ goog.i18n.currency.getLocalCurrencySign = function(currencyCode) {
/**
* Deprecated.
* Portable currency pattern is a compromise between local and global. It is
* not a mere blend or mid-way between the two. Currency sign is chosen so that
* it looks familiar to native users. It also has enough information to
@@ -154,6 +178,9 @@ goog.i18n.currency.getLocalCurrencySign = function(currencyCode) {
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Portable currency pattern string for given currency.
* @deprecated Format numbers using {@link goog.i18n.NumberFormat} with
* {@link goog.i18n.NumberFormat.Format.CURRENCY} and
* {@link goog.i18n.NumberFormat.CurrencyStyle.PORTABLE}
*/
goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
@@ -174,12 +201,17 @@ goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
/**
* This function returns the default currency sign position. Some applications
* This function returns the default currency sign's position. Some applications
* may want to handle currency sign and currency amount separately. This
* function can be used in such situations to correctly position the currency
* sign relative to the amount.
*
* To match the behavior of ICU, position is not determined by display locale.
* Use {@link goog.i18n.NumberFormat#isCurrencyCodeBeforeValue} for a locale
* aware version of this API (recommended). isPrefixSignPosition() returns the
* default currency sign's position in the currency's default locale (e.g. 'en'
* for 'USD'), but most commonly the position is needed for the locale in which
* the number is going to be displayed. For example, in 'fr' 10.10 USD would be
* displayed as '10,10 $'.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {boolean} true if currency should be positioned before amount field.
@@ -267,6 +299,10 @@ goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
* 18: two decimals precision (2), currency sign last (16), no space (0)
* 50: two decimals precision (2), currency sign last (16), space (32)
*
* It's not recommended to read this data directly. Format numbers using
* {@link goog.i18n.NumberFormat} with
* {@link goog.i18n.NumberFormat.Format.CURRENCY} instead.
*
* @const {!Object<!Array<?>>}
*/
goog.i18n.currency.CurrencyInfo = {
@@ -310,7 +346,7 @@ goog.i18n.currency.CurrencyInfo = {
'NOK': [50, 'kr', 'NOkr'],
'PAB': [2, 'B/.', 'B/.'],
'PEN': [2, 'S/.', 'S/.'],
'PHP': [2, '\u20B1', 'Php'],
'PHP': [2, '\u20B1', 'PHP'],
'PKR': [0, 'Rs', 'PKRs.'],
'PLN': [50, 'z\u0142', 'z\u0142'],
'RON': [2, 'RON', 'RON'],
@@ -334,6 +370,11 @@ goog.i18n.currency.CurrencyInfo = {
/**
* Tier 2 currency information.
*
* It's not recommended to read this data directly. Format numbers using
* {@link goog.i18n.NumberFormat} with
* {@link goog.i18n.NumberFormat.Format.CURRENCY} instead.
*
* @const {!Object<!Array<?>>}
*/
goog.i18n.currency.CurrencyInfoTier2 = {
@@ -431,7 +472,7 @@ goog.i18n.currency.CurrencyInfoTier2 = {
'XAF': [0, 'FCFA', 'FCFA'],
'XCD': [2, '$', 'EC$'],
'XOF': [0, 'CFA', 'CFA'],
'XPF': [0, 'FCFP', 'FCFP'],
'XPF': [48, 'FCFP', 'FCFP'],
'ZMW': [0, 'ZMW', 'ZMW'],
'ZWD': [0, '$', 'Z$']
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+21 -33
View File
@@ -17,7 +17,7 @@
*
* This file is autogenerated by script:
* http://go/generate_pluralrules.py
* File generated from CLDR ver. 29
* File generated from CLDR ver. 31.0.1
*
* Before check in, this file could have been manually edited. This is to
* incorporate changes before we could fix CLDR. All manual modification must be
@@ -128,22 +128,6 @@ goog.i18n.pluralRules.filSelect_ = function(n, opt_precision) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for pt_PT locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.pt_PTSelect_ = function(n, opt_precision) {
var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
if (n == 1 && vf.v == 0) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for br locale
*
@@ -240,6 +224,22 @@ goog.i18n.pluralRules.frSelect_ = function(n, opt_precision) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for pt locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
var i = n | 0;
if (i >= 0 && i <= 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for cs locale
*
@@ -559,21 +559,6 @@ goog.i18n.pluralRules.gaSelect_ = function(n, opt_precision) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for pt locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
if (n >= 0 && n <= 2 && n != 2) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for es locale
*
@@ -820,6 +805,9 @@ if (goog.LOCALE == 'am') {
if (goog.LOCALE == 'ar') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.arSelect_;
}
if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.arSelect_;
}
if (goog.LOCALE == 'az') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
}
@@ -1061,7 +1049,7 @@ if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.ptSelect_;
}
if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.pt_PTSelect_;
goog.i18n.pluralRules.select = goog.i18n.pluralRules.ptSelect_;
}
if (goog.LOCALE == 'ro') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
-1
View File
@@ -75,7 +75,6 @@ describe('findLocaleId', function() {
it('should not find localeId if data is missing', function() {
expect(findLocaleId('', 'num')).toBeUndefined();
expect(findLocaleId('aa', 'datetime')).toBeUndefined();
expect(findLocaleId('aa', 'randomType')).toBeUndefined();
expect(findLocaleId('NumberFormatSymbols_en', 'datetime')).toBeUndefined();
expect(findLocaleId('DateTimeSymbols_en', 'num')).toBeUndefined();
});
+49 -1
View File
@@ -1,6 +1,43 @@
'use strict';
var parsePattern = require('../src/parser.js').parsePattern;
var parser = require('../src/parser');
var ensureDecimalSep = parser.ensureDecimalSep;
var parsePattern = parser.parsePattern;
describe('ensureDecimalSep', function() {
it('should leave patterns with DECIMAL_SEP untouched', function() {
[
'#,##0.00',
'$#,##0.00',
'#,##0.00$',
'$0.00',
'0.00$',
'0.0',
'#,##0.',
'0.'
].forEach(function(pattern) {
expect(ensureDecimalSep(pattern)).toBe(pattern);
});
});
it('should add a DECIMAL_SEP in patterns that don\'t have one (after the last ZERO)', function() {
var patterns = {
'#,##000': '#,##000.',
'$#,#0#00': '$#,#0#00.',
'#,##000$': '#,##000.$',
'$000': '$000.',
'000$': '000.$',
'00': '00.',
'#,##0': '#,##0.',
'0': '0.'
};
Object.keys(patterns).forEach(function(input) {
var output = patterns[input];
expect(ensureDecimalSep(input)).toBe(output);
});
});
});
describe('parsePattern', function() {
function parseAndExpect(pattern, pp, np, ps, ns, mii, mif, maf, g, lg) {
@@ -28,6 +65,11 @@ describe('parsePattern', function() {
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
// Even patterns without a DECIMAL_SEP
parseAndExpect('#,##0', '', '-', '', '', 1, 0, 0, 3, 3);
parseAndExpect('+#,##0', '+', '-+', '', '', 1, 0, 0, 3, 3);
parseAndExpect('#,#0;+#,#0', '', '+', '', '', 1, 0, 0, 2, 2);
parseAndExpect('#,##,##0+;(#,##,##0)', '', '(', '+', ')', 1, 0, 0, 2, 3);
});
it('should parse CURRENCY patterns', function() {
@@ -51,5 +93,11 @@ describe('parsePattern', function() {
parseAndExpect('\u00A4 #,##0.00;\u00A4 #,##0.00-',
'\u00A4 ', '\u00A4 ', '', '-', 1, 2, 2, 3, 3);
parseAndExpect('\u00A4 #,##,##0.00', '\u00A4 ', '-\u00A4 ', '', '', 1, 2, 2, 2, 3);
// Even patterns without a DECIMAL_SEP
parseAndExpect('#,##0 \u00A4', '', '-', ' \u00A4', ' \u00A4', 1, 0, 0, 3, 3);
parseAndExpect('\u00A4 #,##0', '\u00A4 ', '-\u00A4 ', '', '', 1, 0, 0, 3, 3);
parseAndExpect('#,#0 \u00A4;+#,#0\u00A4', '', '+', ' \u00A4', '\u00A4', 1, 0, 0, 2, 2);
parseAndExpect('\u00A4 #,##,##0;(\u00A4 #,##,##0)', '\u00A4 ', '(\u00A4 ', '', ')', 1, 0, 0, 2, 3);
});
});
+1 -1
View File
@@ -7,7 +7,7 @@ var Q = require('q'),
localeInfo = {};
var NG_LOCALE_DIR = '../src/ngLocale/';
var NG_LOCALE_DIR = __dirname + '/../../src/ngLocale/';
function readSymbols() {
+28 -6
View File
@@ -4,13 +4,26 @@
* A simple parser to parse a number format into a pattern object
*/
exports.ensureDecimalSep = ensureDecimalSep;
exports.parsePattern = parsePattern;
var PATTERN_SEP = ';',
DECIMAL_SEP = '.',
GROUP_SEP = ',',
ZERO = '0',
DIGIT = '#';
var PATTERN_SEP = ';',
DECIMAL_SEP = '.',
GROUP_SEP = ',',
DIGIT = '#',
ZERO = '0',
LAST_ZERO_RE = /^(.*0)(?!0)(.*)$/;
/**
* Helper function for parser.
* Ensures that `pattern` (e.g #,##0.###) contains a DECIMAL_SEP, which is necessary for further
* parsing. If a pattern does not include one, it is added after the last ZERO (which is the last
* thing before the `posSuf` - if any).
*/
function ensureDecimalSep(pattern) {
return (pattern.indexOf(DECIMAL_SEP) !== -1)
? pattern : pattern.replace(LAST_ZERO_RE, '$1' + DECIMAL_SEP + '$2');
}
/**
* main function for parser
@@ -33,7 +46,16 @@ function parsePattern(pattern) {
positive = patternParts[0],
negative = patternParts[1];
var positiveParts = positive.split(DECIMAL_SEP),
// The parsing logic further below assumes that there will always be a DECIMAL_SEP in the pattern.
// However, some locales (e.g. agq_CM) do not have one, thus we add one after the last ZERO
// (which is the last thing before the `posSuf` - if any). Since there will be no ZEROs or DIGITs
// after DECIMAL_SEP, `min/maxFrac` will remain 0 (which is accurate - no fraction digits) and
// `posSuf` will be processed correctly.
// For example `#,##0$` would be converted to `#,##0.$`, which would (correctly) result in:
// `minFrac: 0`, `maxFrac: 0`, `posSuf: '$'`
// Note: We shouldn't modify `positive` directly, because it is used to parse the negative part.)
var positiveWithDecimalSep = ensureDecimalSep(positive),
positiveParts = positiveWithDecimalSep.split(DECIMAL_SEP),
integer = positiveParts[0],
fraction = positiveParts[1];
+4 -4
View File
@@ -9,18 +9,18 @@ var propertiesToExtract = {'IDS': 'Y', 'IDC': 'Y'};
function main() {
extractValues(
fs.createReadStream('./ucd/src/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
fs.createReadStream(__dirname + '/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
propertiesToExtract,
writeFile);
function writeFile(validRanges) {
var code = generateCode(validRanges);
try {
fs.lstatSync('../src/ngParseExt');
fs.lstatSync(__dirname + '/../../../src/ngParseExt');
} catch (e) {
fs.mkdirSync('../src/ngParseExt');
fs.mkdirSync(__dirname + '/../../../src/ngParseExt');
}
fs.writeFile('../src/ngParseExt/ucd.js', code);
fs.writeFile(__dirname + '/../../../src/ngParseExt/ucd.js', code);
}
}
+2 -2
View File
@@ -122,9 +122,9 @@ module.exports = function(config, specificOptions) {
},
'BS_iOS': {
base: 'BrowserStack',
device: 'iPhone 6',
device: 'iPhone 6S',
os: 'ios',
os_version: '8.0'
os_version: '9.3'
}
}
});
+1 -1
View File
@@ -25,7 +25,7 @@ PORTS.forEach(function(port) {
var tunnel = new BrowserStackTunnel({
key: ACCESS_KEY,
tunnelIdentifier: TUNNEL_IDENTIFIER,
localIdentifier: TUNNEL_IDENTIFIER,
hosts: hosts
});
+19 -14
View File
@@ -1,7 +1,7 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.5.8",
"branchVersion": "^1.6.0",
"branchPattern": "1.6.*",
"distTag": "latest",
"repository": {
@@ -10,7 +10,7 @@
},
"engines": {
"node": "^6.9.1",
"yarn": "^0.17.9",
"yarn": ">=0.21.3",
"grunt": "^1.2.0"
},
"scripts": {
@@ -22,19 +22,21 @@
"devDependencies": {
"angular-benchpress": "0.x.x",
"benchmark": "1.x.x",
"bootstrap": "3.1.1",
"bower": "~1.3.9",
"browserstacktunnel-wrapper": "^1.4.2",
"browserstacktunnel-wrapper": "2.0.0",
"canonical-path": "0.0.2",
"changez": "^2.1.1",
"changez-angular": "^2.1.0",
"changez-angular": "^2.1.2",
"cheerio": "^0.17.0",
"commitizen": "^2.3.0",
"cross-spawn": "^4.0.0",
"cz-conventional-changelog": "1.1.4",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.16.0",
"dgeni-packages": "^0.16.4",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
"google-code-prettify": "1.0.1",
"grunt": "^1.0.1",
"grunt-bump": "^0.8.0",
"grunt-cli": "^1.2.0",
@@ -54,26 +56,29 @@
"gulp-sourcemaps": "^1.2.2",
"gulp-uglify": "^1.0.1",
"gulp-util": "^3.0.1",
"jasmine-core": "^2.4.0",
"jasmine-core": "2.5.2",
"jasmine-node": "^2.0.0",
"jasmine-reporters": "^2.2.0",
"karma": "^1.1.2",
"karma-browserstack-launcher": "^1.0.1",
"karma-chrome-launcher": "^1.0.1",
"karma-firefox-launcher": "^1.0.0",
"karma-jasmine": "^1.0.2",
"karma-junit-reporter": "^1.1.0",
"jquery": "^3.2.1",
"karma": "^1.7.0",
"karma-browserstack-launcher": "^1.2.0",
"karma-chrome-launcher": "^2.1.1",
"karma-firefox-launcher": "^1.0.1",
"karma-jasmine": "^1.1.0",
"karma-junit-reporter": "^1.2.0",
"karma-ng-scenario": "^1.0.0",
"karma-sauce-launcher": "^1.0.0",
"karma-sauce-launcher": "^1.1.0",
"karma-script-launcher": "^1.0.0",
"load-grunt-tasks": "^3.5.0",
"lodash": "~2.4.1",
"log4js": "^0.6.27",
"lunr": "^0.7.2",
"marked": "~0.3.0",
"node-html-encoder": "0.0.2",
"npm-run": "^4.1.0",
"open-sans-fontface": "^1.4.0",
"promises-aplus-tests": "~2.1.0",
"protractor": "^4.0.10",
"protractor": "^5.1.2",
"q": "~1.0.0",
"q-io": "^1.10.9",
"qq": "^0.3.5",
+5 -1
View File
@@ -62,7 +62,11 @@ function _update_code() {
for backend in "$@" ; do
echo "-- Refreshing code.angularjs.org: backend=$backend"
curl http://$backend:8003/gitFetchSite.php
# FIXME: We gave up publishing to code.angularjs.org because the GCE automatically removes firewall
# rules that allow access to port 8003.
# curl http://$backend:8003/gitFetchSite.php
done
}
+1 -1
View File
@@ -12,7 +12,7 @@ set -xe
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
BROWSERS="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
+1 -1
View File
@@ -8,7 +8,7 @@ nvm install
# clean out and install yarn
rm -rf ~/.yarn
curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.21.3
export PATH="$HOME/.yarn/bin:$PATH"
# Ensure that we have the local dependencies installed
+9 -1
View File
@@ -15,6 +15,9 @@
"splice": false,
"push": false,
"toString": false,
"minErrConfig": false,
"errorHandlingConfig": false,
"isValidObjectMaxDepth": false,
"ngMinErr": false,
"_angular": false,
"angularModule": false,
@@ -47,6 +50,7 @@
"isNumber": false,
"isNumberNaN": false,
"isDate": false,
"isError": false,
"isArray": false,
"isFunction": false,
"isRegExp": false,
@@ -66,6 +70,7 @@
"arrayRemove": false,
"copy": false,
"shallowCopy": false,
"simpleCompare": false,
"equals": false,
"csp": false,
"concat": false,
@@ -150,7 +155,7 @@
/* apis.js */
"hashKey": false,
"HashMap": false,
"NgMap": false,
/* urlUtils.js */
"urlResolve": false,
@@ -162,6 +167,9 @@
/* ng/compile.js */
"directiveNormalize": false,
/* ng/q.js */
"markQExceptionHandled": false,
/* ng/directive/directives.js */
"ngDirective": false,
+101 -38
View File
@@ -10,6 +10,9 @@
splice,
push,
toString,
minErrConfig,
errorHandlingConfig,
isValidObjectMaxDepth,
ngMinErr,
angularModule,
uid,
@@ -42,6 +45,7 @@
isNumber,
isNumberNaN,
isDate,
isError,
isArray,
isFunction,
isRegExp,
@@ -59,6 +63,7 @@
includes,
arrayRemove,
copy,
simpleCompare,
equals,
csp,
jq,
@@ -432,6 +437,20 @@ function extend(dst) {
* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
* objects, performing a deep copy.
*
* @deprecated
* sinceVersion="1.6.5"
* This function is deprecated, but will not be removed in the 1.x lifecycle.
* There are edge cases (see {@link angular.merge#known-issues known issues}) that are not
* supported by this function. We suggest
* using [lodash's merge()](https://lodash.com/docs/4.17.4#merge) instead.
*
* @knownIssue
* This is a list of (known) object types that are not handled correctly by this function:
* - [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob)
* - [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream)
* - [`CanvasGradient`](https://developer.mozilla.org/docs/Web/API/CanvasGradient)
* - AngularJS {@link $rootScope.Scope scopes};
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
@@ -641,6 +660,24 @@ function isDate(value) {
*/
var isArray = Array.isArray;
/**
* @description
* Determines if a reference is an `Error`.
* Loosely based on https://www.npmjs.com/package/iserror
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Error`.
*/
function isError(value) {
var tag = toString.call(value);
switch (tag) {
case '[object Error]': return true;
case '[object Exception]': return true;
case '[object DOMException]': return true;
default: return value instanceof Error;
}
}
/**
* @ngdoc function
* @name angular.isFunction
@@ -847,9 +884,10 @@ function arrayRemove(array, value) {
</file>
</example>
*/
function copy(source, destination) {
function copy(source, destination, maxDepth) {
var stackSource = [];
var stackDest = [];
maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
if (destination) {
if (isTypedArray(destination) || isArrayBuffer(destination)) {
@@ -872,35 +910,39 @@ function copy(source, destination) {
stackSource.push(source);
stackDest.push(destination);
return copyRecurse(source, destination);
return copyRecurse(source, destination, maxDepth);
}
return copyElement(source);
return copyElement(source, maxDepth);
function copyRecurse(source, destination) {
function copyRecurse(source, destination, maxDepth) {
maxDepth--;
if (maxDepth < 0) {
return '...';
}
var h = destination.$$hashKey;
var key;
if (isArray(source)) {
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
destination.push(copyElement(source[i], maxDepth));
}
} else if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
destination[key] = copyElement(source[key]);
destination[key] = copyElement(source[key], maxDepth);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = copyElement(source[key]);
destination[key] = copyElement(source[key], maxDepth);
}
}
} else {
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
destination[key] = copyElement(source[key]);
destination[key] = copyElement(source[key], maxDepth);
}
}
}
@@ -908,7 +950,7 @@ function copy(source, destination) {
return destination;
}
function copyElement(source) {
function copyElement(source, maxDepth) {
// Simple values
if (!isObject(source)) {
return source;
@@ -937,7 +979,7 @@ function copy(source, destination) {
stackDest.push(destination);
return needsRecurse
? copyRecurse(source, destination)
? copyRecurse(source, destination, maxDepth)
: destination;
}
@@ -988,6 +1030,10 @@ function copy(source, destination) {
}
// eslint-disable-next-line no-self-compare
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
/**
* @ngdoc function
* @name angular.equals
@@ -1068,7 +1114,7 @@ function equals(o1, o2) {
}
} else if (isDate(o1)) {
if (!isDate(o2)) return false;
return equals(o1.getTime(), o2.getTime());
return simpleCompare(o1.getTime(), o2.getTime());
} else if (isRegExp(o1)) {
if (!isRegExp(o2)) return false;
return o1.toString() === o2.toString();
@@ -1312,7 +1358,7 @@ function fromJson(json) {
var ALL_COLONS = /:/g;
function timezoneToOffset(timezone, fallback) {
// Support: IE 9-11 only, Edge 13-14+
// Support: IE 9-11 only, Edge 13-15+
// IE/Edge do not "understand" colon (`:`) in timezone
timezone = timezone.replace(ALL_COLONS, '');
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
@@ -1339,12 +1385,7 @@ function convertTimezoneToLocal(date, timezone, reverse) {
* @returns {string} Returns the string representation of the element.
*/
function startingTag(element) {
element = jqLite(element).clone();
try {
// turns out IE does not let you set .html() on elements which
// are not allowed to have children. So we just ignore it.
element.empty();
} catch (e) { /* empty */ }
element = jqLite(element).clone().empty();
var elemHtml = jqLite('<div>').append(element).html();
try {
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
@@ -1479,30 +1520,52 @@ function getNgAttribute(element, ngAttr) {
}
function allowAutoBootstrap(document) {
if (!document.currentScript) {
var script = document.currentScript;
if (!script) {
// Support: IE 9-11 only
// IE does not have `document.currentScript`
return true;
}
var src = document.currentScript.getAttribute('src');
var link = document.createElement('a');
link.href = src;
if (document.location.origin === link.origin) {
// Same-origin resources are always allowed, even for non-whitelisted schemes.
return true;
// If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
return false;
}
// Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
// This is to prevent angular.js bundled with browser extensions from being used to bypass the
// content security policy in web pages and other browser extensions.
switch (link.protocol) {
case 'http:':
case 'https:':
case 'ftp:':
case 'blob:':
case 'file:':
case 'data:':
var attributes = script.attributes;
var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
return srcs.every(function(src) {
if (!src) {
return true;
default:
}
if (!src.value) {
return false;
}
}
var link = document.createElement('a');
link.href = src.value;
if (document.location.origin === link.origin) {
// Same-origin resources are always allowed, even for non-whitelisted schemes.
return true;
}
// Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
// This is to prevent angular.js bundled with browser extensions from being used to bypass the
// content security policy in web pages and other browser extensions.
switch (link.protocol) {
case 'http:':
case 'https:':
case 'ftp:':
case 'blob:':
case 'file:':
case 'data:':
return true;
default:
return false;
}
});
}
// Cached as it has to run during loading so that document.currentScript is available.
@@ -1866,7 +1929,7 @@ function bindJQuery() {
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
controller: /** @type {?} */ (JQLitePrototype).controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
+5 -3
View File
@@ -70,7 +70,6 @@
$$ForceReflowProvider,
$InterpolateProvider,
$IntervalProvider,
$$HashMapProvider,
$HttpProvider,
$HttpParamSerializerProvider,
$HttpParamSerializerJQLikeProvider,
@@ -79,6 +78,7 @@
$jsonpCallbacksProvider,
$LocationProvider,
$LogProvider,
$$MapProvider,
$ParseProvider,
$RootScopeProvider,
$QProvider,
@@ -126,6 +126,7 @@ var version = {
function publishExternalAPI(angular) {
extend(angular, {
'errorHandlingConfig': errorHandlingConfig,
'bootstrap': bootstrap,
'copy': copy,
'extend': extend,
@@ -260,9 +261,10 @@ function publishExternalAPI(angular) {
$window: $WindowProvider,
$$rAF: $$RAFProvider,
$$jqLite: $$jqLiteProvider,
$$HashMap: $$HashMapProvider,
$$Map: $$MapProvider,
$$cookieReader: $$CookieReaderProvider
});
}
]);
])
.info({ angularVersion: '"NG_VERSION_FULL"' });
}
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window) {
+55 -36
View File
@@ -1,6 +1,5 @@
'use strict';
/**
* Computes a hash of an 'obj'.
* Hash of a:
@@ -33,49 +32,69 @@ function hashKey(obj, nextUidFn) {
return key;
}
/**
* HashMap which can use objects as keys
*/
function HashMap(array, isolatedUid) {
if (isolatedUid) {
var uid = 0;
this.nextUid = function() {
return ++uid;
};
}
forEach(array, this.put, this);
// A minimal ES2015 Map implementation.
// Should be bug/feature equivalent to the native implementations of supported browsers
// (for the features required in Angular).
// See https://kangax.github.io/compat-table/es6/#test-Map
var nanKey = Object.create(null);
function NgMapShim() {
this._keys = [];
this._values = [];
this._lastKey = NaN;
this._lastIndex = -1;
}
HashMap.prototype = {
/**
* Store key value pair
* @param key key to store can be any type
* @param value value to store can be any type
*/
put: function(key, value) {
this[hashKey(key, this.nextUid)] = value;
NgMapShim.prototype = {
_idx: function(key) {
if (key === this._lastKey) {
return this._lastIndex;
}
this._lastKey = key;
this._lastIndex = this._keys.indexOf(key);
return this._lastIndex;
},
_transformKey: function(key) {
return isNumberNaN(key) ? nanKey : key;
},
/**
* @param key
* @returns {Object} the value for the key
*/
get: function(key) {
return this[hashKey(key, this.nextUid)];
key = this._transformKey(key);
var idx = this._idx(key);
if (idx !== -1) {
return this._values[idx];
}
},
set: function(key, value) {
key = this._transformKey(key);
var idx = this._idx(key);
if (idx === -1) {
idx = this._lastIndex = this._keys.length;
}
this._keys[idx] = key;
this._values[idx] = value;
/**
* Remove the key/value pair
* @param key
*/
remove: function(key) {
var value = this[key = hashKey(key, this.nextUid)];
delete this[key];
return value;
// Support: IE11
// Do not `return this` to simulate the partial IE11 implementation
},
delete: function(key) {
key = this._transformKey(key);
var idx = this._idx(key);
if (idx === -1) {
return false;
}
this._keys.splice(idx, 1);
this._values.splice(idx, 1);
this._lastKey = NaN;
this._lastIndex = -1;
return true;
}
};
var $$HashMapProvider = [/** @this */function() {
// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations
// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map`
// implementations get more stable, we can reconsider switching to `window.Map` (when available).
var NgMap = NgMapShim;
var $$MapProvider = [/** @this */function() {
this.$get = [function() {
return HashMap;
return NgMap;
}];
}];
+27 -7
View File
@@ -71,11 +71,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function stringifyFn(fn) {
// Support: Chrome 50-51 only
// Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
// (See https://github.com/angular/angular.js/issues/14487.)
// TODO (gkalpak): Remove workaround when Chrome v52 is released
return Function.prototype.toString.call(fn) + ' ';
return Function.prototype.toString.call(fn);
}
function extractArgs(fn) {
@@ -184,6 +180,28 @@ function annotate(fn, strictDi, name) {
* As an array of injection names, where the last item in the array is the function to call.
*/
/**
* @ngdoc property
* @name $injector#modules
* @type {Object}
* @description
* A hash containing all the modules that have been loaded into the
* $injector.
*
* You can use this property to find out information about a module via the
* {@link angular.Module#info `myModule.info(...)`} method.
*
* For example:
*
* ```
* var info = $injector.modules['ngAnimate'].info();
* ```
*
* **Do not use this property to attempt to modify the modules after the application
* has been bootstrapped.**
*/
/**
* @ngdoc method
* @name $injector#get
@@ -649,7 +667,7 @@ function createInjector(modulesToLoad, strictDi) {
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap([], true),
loadedModules = new NgMap(),
providerCache = {
$provide: {
provider: supportObject(provider),
@@ -677,6 +695,7 @@ function createInjector(modulesToLoad, strictDi) {
instanceInjector = protoInstanceInjector;
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
instanceInjector.modules = providerInjector.modules = createMap();
var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
@@ -757,7 +776,7 @@ function createInjector(modulesToLoad, strictDi) {
var runBlocks = [], moduleFn;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
loadedModules.set(module, true);
function runInvokeQueue(queue) {
var i, ii;
@@ -772,6 +791,7 @@ function createInjector(modulesToLoad, strictDi) {
try {
if (isString(module)) {
moduleFn = angularModule(module);
instanceInjector.modules[module] = moduleFn;
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
+15 -17
View File
@@ -201,12 +201,6 @@ function jqLiteHasData(node) {
return false;
}
function jqLiteCleanData(nodes) {
for (var i = 0, ii = nodes.length; i < ii; i++) {
jqLiteRemoveData(nodes[i]);
}
}
function jqLiteBuildFragment(html, context) {
var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
@@ -309,13 +303,10 @@ function jqLiteClone(element) {
}
function jqLiteDealoc(element, onlyDescendants) {
if (!onlyDescendants) jqLiteRemoveData(element);
if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]);
if (element.querySelectorAll) {
var descendants = element.querySelectorAll('*');
for (var i = 0, l = descendants.length; i < l; i++) {
jqLiteRemoveData(descendants[i]);
}
jqLite.cleanData(element.querySelectorAll('*'));
}
}
@@ -613,7 +604,11 @@ forEach({
data: jqLiteData,
removeData: jqLiteRemoveData,
hasData: jqLiteHasData,
cleanData: jqLiteCleanData
cleanData: function jqLiteCleanData(nodes) {
for (var i = 0, ii = nodes.length; i < ii; i++) {
jqLiteRemoveData(nodes[i]);
}
}
}, function(fn, name) {
JQLite[name] = fn;
});
@@ -979,12 +974,15 @@ forEach({
after: function(element, newElement) {
var index = element, parent = element.parentNode;
newElement = new JQLite(newElement);
for (var i = 0, ii = newElement.length; i < ii; i++) {
var node = newElement[i];
parent.insertBefore(node, index.nextSibling);
index = node;
if (parent) {
newElement = new JQLite(newElement);
for (var i = 0, ii = newElement.length; i < ii; i++) {
var node = newElement[i];
parent.insertBefore(node, index.nextSibling);
index = node;
}
}
},
+42
View File
@@ -79,6 +79,9 @@ function setupModuleLoader(window) {
* @returns {angular.Module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
var info = {};
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
@@ -114,6 +117,45 @@ function setupModuleLoader(window) {
_configBlocks: configBlocks,
_runBlocks: runBlocks,
/**
* @ngdoc method
* @name angular.Module#info
* @module ng
*
* @param {Object=} info Information about the module
* @returns {Object|Module} The current info object for this module if called as a getter,
* or `this` if called as a setter.
*
* @description
* Read and write custom information about this module.
* For example you could put the version of the module in here.
*
* ```js
* angular.module('myModule', []).info({ version: '1.0.0' });
* ```
*
* The version could then be read back out by accessing the module elsewhere:
*
* ```
* var version = angular.module('myModule').info().version;
* ```
*
* You can also retrieve this information during runtime via the
* {@link $injector#modules `$injector.modules`} property:
*
* ```js
* var version = $injector.modules['myModule'].info().version;
* ```
*/
info: function(value) {
if (isDefined(value)) {
if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
info = value;
return this;
}
return info;
},
/**
* @ngdoc property
* @name angular.Module#requires
+29 -2
View File
@@ -1,8 +1,35 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
'use strict';
(function() {
function isFunction(value) {return typeof value === 'function';};
// NOTE:
// These functions are copied here from `src/Angular.js`, because they are needed inside the
// `angular-loader.js` closure and need to be available before the main `angular.js` script has
// been loaded.
function isFunction(value) {return typeof value === 'function';}
function isDefined(value) {return typeof value !== 'undefined';}
function isNumber(value) {return typeof value === 'number';}
function isObject(value) {return value !== null && typeof value === 'object';}
function isScope(obj) {return obj && obj.$evalAsync && obj.$watch;}
function isUndefined(value) {return typeof value === 'undefined';}
function isWindow(obj) {return obj && obj.window === obj;}
function sliceArgs(args, startIndex) {return Array.prototype.slice.call(args, startIndex || 0);}
function toJsonReplacer(key, value) {
var val = value;
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
} else if (value && window.document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
return val;
}
+60 -12
View File
@@ -1,5 +1,55 @@
'use strict';
/* exported
minErrConfig,
errorHandlingConfig,
isValidObjectMaxDepth
*/
var minErrConfig = {
objectMaxDepth: 5
};
/**
* @ngdoc function
* @name angular.errorHandlingConfig
* @module ng
* @kind function
*
* @description
* Configure several aspects of error handling in AngularJS if used as a setter or return the
* current configuration if used as a getter. The following options are supported:
*
* - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
*
* Omitted or undefined options will leave the corresponding configuration values unchanged.
*
* @param {Object=} config - The configuration object. May only contain the options that need to be
* updated. Supported keys:
*
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
* non-positive or non-numeric value, removes the max depth limit.
* Default: 5
*/
function errorHandlingConfig(config) {
if (isObject(config)) {
if (isDefined(config.objectMaxDepth)) {
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
}
} else {
return minErrConfig;
}
}
/**
* @private
* @param {Number} maxDepth
* @return {boolean}
*/
function isValidObjectMaxDepth(maxDepth) {
return isNumber(maxDepth) && maxDepth > 0;
}
/**
* @description
*
@@ -33,20 +83,19 @@
function minErr(module, ErrorConstructor) {
ErrorConstructor = ErrorConstructor || Error;
return function() {
var SKIP_INDEXES = 2;
var templateArgs = arguments,
code = templateArgs[0],
var code = arguments[0],
template = arguments[1],
message = '[' + (module ? module + ':' : '') + code + '] ',
template = templateArgs[1],
templateArgs = sliceArgs(arguments, 2).map(function(arg) {
return toDebugString(arg, minErrConfig.objectMaxDepth);
}),
paramPrefix, i;
message += template.replace(/\{\d+\}/g, function(match) {
var index = +match.slice(1, -1),
shiftedIndex = index + SKIP_INDEXES;
var index = +match.slice(1, -1);
if (shiftedIndex < templateArgs.length) {
return toDebugString(templateArgs[shiftedIndex]);
if (index < templateArgs.length) {
return templateArgs[index];
}
return match;
@@ -55,9 +104,8 @@ function minErr(module, ErrorConstructor) {
message += '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
(module ? module + '/' : '') + code;
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
encodeURIComponent(toDebugString(templateArgs[i]));
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
return new ErrorConstructor(message);
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular) {
+62 -9
View File
@@ -60,7 +60,7 @@ var $$CoreAnimateJsProvider = /** @this */ function() {
// this is prefixed with Core since it conflicts with
// the animateQueueProvider defined in ngAnimate/animateQueue.js
var $$CoreAnimateQueueProvider = /** @this */ function() {
var postDigestQueue = new HashMap();
var postDigestQueue = new NgMap();
var postDigestElements = [];
this.$get = ['$$AnimateRunner', '$rootScope',
@@ -139,7 +139,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
jqLiteRemoveClass(elm, toRemove);
}
});
postDigestQueue.remove(element);
postDigestQueue.delete(element);
}
});
postDigestElements.length = 0;
@@ -154,7 +154,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
if (classesAdded || classesRemoved) {
postDigestQueue.put(element, data);
postDigestQueue.set(element, data);
postDigestElements.push(element);
if (postDigestElements.length === 1) {
@@ -179,6 +179,8 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
*/
var $AnimateProvider = ['$provide', /** @this */ function($provide) {
var provider = this;
var classNameFilter = null;
var customFilter = null;
this.$$registeredAnimations = Object.create(null);
@@ -231,6 +233,51 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
$provide.factory(key, factory);
};
/**
* @ngdoc method
* @name $animateProvider#customFilter
*
* @description
* Sets and/or returns the custom filter function that is used to "filter" animations, i.e.
* determine if an animation is allowed or not. When no filter is specified (the default), no
* animation will be blocked. Setting the `customFilter` value will only allow animations for
* which the filter function's return value is truthy.
*
* This allows to easily create arbitrarily complex rules for filtering animations, such as
* allowing specific events only, or enabling animations on specific subtrees of the DOM, etc.
* Filtering animations can also boost performance for low-powered devices, as well as
* applications containing a lot of structural operations.
*
* <div class="alert alert-success">
* **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.
* </div>
*
* **Note:** If present, `customFilter` will be checked before
* {@link $animateProvider#classNameFilter classNameFilter}.
*
* @param {Function=} filterFn - The filter function which will be used to filter all animations.
* If a falsy value is returned, no animation will be performed. The function will be called
* with the following arguments:
* - **node** `{DOMElement}` - The DOM element to be animated.
* - **event** `{String}` - The name of the animation event (e.g. `enter`, `leave`, `addClass`
* etc).
* - **options** `{Object}` - A collection of options/styles used for the animation.
* @return {Function} The current filter function or `null` if there is none set.
*/
this.customFilter = function(filterFn) {
if (arguments.length === 1) {
customFilter = isFunction(filterFn) ? filterFn : null;
}
return customFilter;
};
/**
* @ngdoc method
* @name $animateProvider#classNameFilter
@@ -242,20 +289,26 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* When setting the `classNameFilter` value, animations will only be performed on elements
* that successfully match the filter expression. This in turn can boost performance
* for low-powered devices as well as applications containing a lot of structural operations.
*
* **Note:** If present, `classNameFilter` will be checked after
* {@link $animateProvider#customFilter customFilter}. If `customFilter` is present and returns
* false, `classNameFilter` will not be checked.
*
* @param {RegExp=} expression The className expression which will be checked against all animations
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
*/
this.classNameFilter = function(expression) {
if (arguments.length === 1) {
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
if (this.$$classNameFilter) {
var reservedRegex = new RegExp('(\\s+|\\/)' + NG_ANIMATE_CLASSNAME + '(\\s+|\\/)');
if (reservedRegex.test(this.$$classNameFilter.toString())) {
throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
classNameFilter = (expression instanceof RegExp) ? expression : null;
if (classNameFilter) {
var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
if (reservedRegex.test(classNameFilter.toString())) {
classNameFilter = null;
throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
}
}
}
return this.$$classNameFilter;
return classNameFilter;
};
this.$get = ['$$animateQueue', function($$animateQueue) {
+11 -10
View File
@@ -96,7 +96,6 @@ function Browser(window, document, $log, $sniffer) {
};
cacheState();
lastHistoryState = cachedState;
/**
* @name $browser#url
@@ -150,8 +149,6 @@ function Browser(window, document, $log, $sniffer) {
if ($sniffer.history && (!sameBase || !sameState)) {
history[replace ? 'replaceState' : 'pushState'](state, '', url);
cacheState();
// Do the assignment again so that those two variables are referentially identical.
lastHistoryState = cachedState;
} else {
if (!sameBase) {
pendingLocation = url;
@@ -200,8 +197,7 @@ function Browser(window, document, $log, $sniffer) {
function cacheStateAndFireUrlChange() {
pendingLocation = null;
cacheState();
fireUrlChange();
fireStateOrUrlChange();
}
// This variable should be used *only* inside the cacheState function.
@@ -215,11 +211,16 @@ function Browser(window, document, $log, $sniffer) {
if (equals(cachedState, lastCachedState)) {
cachedState = lastCachedState;
}
lastCachedState = cachedState;
lastHistoryState = cachedState;
}
function fireUrlChange() {
if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
function fireStateOrUrlChange() {
var prevLastHistoryState = lastHistoryState;
cacheState();
if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) {
return;
}
@@ -254,8 +255,8 @@ function Browser(window, document, $log, $sniffer) {
self.onUrlChange = function(callback) {
// TODO(vojta): refactor to use node's syntax for events
if (!urlChangeInit) {
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
// don't fire popstate when user change the address bar and don't fire hashchange when url
// We listen on both (hashchange/popstate) when available, as some browsers don't
// fire popstate when user changes the address bar and don't fire hashchange when url
// changed by push/replaceState
// html5 history api - popstate event
@@ -285,7 +286,7 @@ function Browser(window, document, $log, $sniffer) {
* Needs to be exported to be able to check for changes that have been done in sync,
* as hashchange/popstate events fire in async.
*/
self.$$checkUrlChange = fireUrlChange;
self.$$checkUrlChange = fireStateOrUrlChange;
//////////////////////////////////////////////////////////////
// Misc API
+31 -18
View File
@@ -128,7 +128,8 @@
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) 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.
* component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
* also be called when your bindings are initialized.
* * `$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
@@ -276,10 +277,12 @@
* the directive's element. If multiple directives on the same element request a new scope,
* only one new scope is created.
*
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
* 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
* scope. This is useful when creating reusable components, which should not accidentally read or modify
* data in the parent scope.
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
* The 'isolate' scope differs from normal scope in that it does not prototypically
* inherit from its parent scope. This is useful when creating reusable components, which should not
* accidentally read or modify data in the parent scope. Note that an isolate scope
* directive without a `template` or `templateUrl` will not apply the isolate scope
* to its children elements.
*
* The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
* directive's element. These local properties are useful for aliasing values for templates. The keys in
@@ -372,9 +375,9 @@
* initialized.
*
* <div class="alert alert-warning">
* **Deprecation warning:** although bindings for non-ES6 class controllers are currently
* bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
* code that relies upon bindings inside a `$onInit` method on the controller, instead.
* **Deprecation warning:** if `$compileProcvider.preAssignBindingsEnabled(true)` was called, bindings for non-ES6 class
* controllers are bound to `this` before the controller constructor is called but this use is now deprecated. Please
* place initialization code that relies upon bindings inside a `$onInit` method on the controller, instead.
* </div>
*
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
@@ -983,7 +986,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var bindingCache = createMap();
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;
var bindings = createMap();
@@ -1141,7 +1144,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @ngdoc method
* @name $compileProvider#component
* @module ng
* @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
* @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`),
* or an object map of components where the keys are the names and the values are the component definition objects.
* @param {Object} options Component definition object (a simplified
* {@link ng.$compile#directive-definition-object directive definition object}),
* with the following properties (all optional):
@@ -1224,6 +1228,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
this.component = function registerComponent(name, options) {
if (!isString(name)) {
forEach(name, reverseParams(bind(this, registerComponent)));
return this;
}
var controller = options.controller || function() {};
function factory($injector) {
@@ -1387,7 +1396,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
*
* If disabled (false), the compiler calls the constructor first before assigning bindings.
*
* The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
* The default value is false.
*
* @deprecated
* sinceVersion="1.6.0"
* removeVersion="1.7.0"
*
* This method and the option to assign the bindings before calling the controller's constructor
* will be removed in v1.7.0.
*/
var preAssignBindingsEnabled = false;
this.preAssignBindingsEnabled = function(enabled) {
@@ -3152,10 +3168,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
linkQueue = null;
}).catch(function(error) {
if (error instanceof Error) {
if (isError(error)) {
$exceptionHandler(error);
}
}).catch(noop);
});
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
var childBoundTranscludeFn = boundTranscludeFn;
@@ -3477,8 +3493,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (parentGet.literal) {
compare = equals;
} else {
// eslint-disable-next-line no-self-compare
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
compare = simpleCompare;
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
@@ -3553,9 +3568,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
function recordChanges(key, currentValue, previousValue) {
if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
// eslint-disable-next-line no-self-compare
(currentValue === currentValue || previousValue === previousValue)) {
if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) {
// If we have not already scheduled the top level onChangesQueue handler then do so now
if (!onChangesQueue) {
scope.$$postDigest(flushOnChangesQueue);
+9 -1
View File
@@ -14,6 +14,14 @@ function $$CookieReader($document) {
var lastCookies = {};
var lastCookieString = '';
function safeGetCookie(rawDocument) {
try {
return rawDocument.cookie || '';
} catch (e) {
return '';
}
}
function safeDecodeURIComponent(str) {
try {
return decodeURIComponent(str);
@@ -24,7 +32,7 @@ function $$CookieReader($document) {
return function() {
var cookieArray, cookie, i, index, name;
var currentCookieString = rawDocument.cookie || '';
var currentCookieString = safeGetCookie(rawDocument);
if (currentCookieString !== lastCookieString) {
lastCookieString = currentCookieString;
+2 -1
View File
@@ -159,7 +159,8 @@
*
* @description
*
* This directive sets the `disabled` attribute on the element if the
* This directive sets the `disabled` attribute on the element (typically a form control,
* e.g. `input`, `button`, `select` etc.) if the
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
*
* A special directive is necessary because we cannot use interpolation inside the `disabled`
+28 -7
View File
@@ -26,17 +26,23 @@ function nullFormRenameControl(control, name) {
* @property {boolean} $dirty True if user has already interacted with the form.
* @property {boolean} $valid True if all of the containing forms and controls are valid.
* @property {boolean} $invalid True if at least one containing control or form is invalid.
* @property {boolean} $pending True if at least one containing control or form is pending.
* @property {boolean} $submitted True if user has submitted the form even if its invalid.
*
* @property {Object} $error Is an object hash, containing references to controls or
* forms with failing validators, where:
* @property {Object} $pending An object hash, containing references to controls or forms with
* pending validators, where:
*
* - keys are validations tokens (error names).
* - values are arrays of controls or forms that have a pending validator for the given error name.
*
* See {@link form.FormController#$error $error} for a list of built-in validation tokens.
*
* @property {Object} $error An object hash, containing references to controls or forms with failing
* validators, where:
*
* - keys are validation tokens (error names),
* - values are arrays of controls or forms that have a failing validator for given error name.
* - values are arrays of controls or forms that have a failing validator for the given error name.
*
* Built-in validation tokens:
*
* - `email`
* - `max`
* - `maxlength`
@@ -282,9 +288,24 @@ FormController.prototype = {
* @name form.FormController#$setValidity
*
* @description
* Sets the validity of a form control.
* Change the validity state of the form, and notify the parent form (if any).
*
* This method will also propagate to parent forms.
* Application developers will rarely need to call this method directly. It is used internally, by
* {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a
* control's validity state to the parent `FormController`.
*
* @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be
* assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for
* unfulfilled `$asyncValidators`), so that it is available for data-binding. The
* `validationErrorKey` should be in camelCase and will get converted into dash-case for
* class name. Example: `myError` will result in `ng-valid-my-error` and
* `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`.
* @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending
* (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
* Skipped is used by AngularJS when validators do not run because of parse errors and when
* `$asyncValidators` do not run because any of the `$validators` failed.
* @param {NgModelController | FormController} controller - The controller whose validity state is
* triggering the change.
*/
addSetValidityMethod({
clazz: FormController,
+18 -3
View File
@@ -1565,15 +1565,27 @@ function isValidForStep(viewValue, stepBase, step) {
// and `viewValue` is expected to be a valid stringified number.
var value = Number(viewValue);
var isNonIntegerValue = !isNumberInteger(value);
var isNonIntegerStepBase = !isNumberInteger(stepBase);
var isNonIntegerStep = !isNumberInteger(step);
// Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
// `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) {
var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step));
if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
var multiplier = Math.pow(10, decimalCount);
value = value * multiplier;
stepBase = stepBase * multiplier;
step = step * multiplier;
if (isNonIntegerValue) value = Math.round(value);
if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
if (isNonIntegerStep) step = Math.round(step);
}
return (value - stepBase) % step === 0;
@@ -2130,7 +2142,10 @@ var ngValueDirective = function() {
* makes it possible to use ngValue as a sort of one-way bind.
*/
function updateElementValue(element, attr, value) {
element.prop('value', value);
// Support: IE9 only
// In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`).
var propValue = isDefined(value) ? value : (msie === 9) ? '' : null;
element.prop('value', propValue);
attr.$set('value', value);
}
+144 -95
View File
@@ -8,51 +8,71 @@
function classDirective(name, selector) {
name = 'ngClass' + name;
return ['$animate', function($animate) {
var indexWatchExpression;
return ['$parse', function($parse) {
return {
restrict: 'AC',
link: function(scope, element, attr) {
var oldVal;
var expression = attr[name].trim();
var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':');
scope.$watch(attr[name], ngClassWatchAction, true);
var watchInterceptor = isOneTime ? toFlatValue : toClassString;
var watchExpression = $parse(expression, watchInterceptor);
var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction;
attr.$observe('class', function(value) {
ngClassWatchAction(scope.$eval(attr[name]));
});
var classCounts = element.data('$classCounts');
var oldModulo = true;
var oldClassString;
if (name !== 'ngClass') {
scope.$watch('$index', function($index, old$index) {
/* eslint-disable no-bitwise */
var mod = $index & 1;
if (mod !== (old$index & 1)) {
var classes = arrayClasses(scope.$eval(attr[name]));
if (mod === selector) {
addClasses(classes);
} else {
removeClasses(classes);
}
}
/* eslint-enable */
});
}
function addClasses(classes) {
var newClasses = digestClassCounts(classes, 1);
attr.$addClass(newClasses);
}
function removeClasses(classes) {
var newClasses = digestClassCounts(classes, -1);
attr.$removeClass(newClasses);
}
function digestClassCounts(classes, count) {
if (!classCounts) {
// Use createMap() to prevent class assumptions involving property
// names in Object.prototype
var classCounts = element.data('$classCounts') || createMap();
classCounts = createMap();
element.data('$classCounts', classCounts);
}
if (name !== 'ngClass') {
if (!indexWatchExpression) {
indexWatchExpression = $parse('$index', function moduloTwo($index) {
// eslint-disable-next-line no-bitwise
return $index & 1;
});
}
scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
}
scope.$watch(watchExpression, watchAction, isOneTime);
function addClasses(classString) {
classString = digestClassCounts(split(classString), 1);
attr.$addClass(classString);
}
function removeClasses(classString) {
classString = digestClassCounts(split(classString), -1);
attr.$removeClass(classString);
}
function updateClasses(oldClassString, newClassString) {
var oldClassArray = split(oldClassString);
var newClassArray = split(newClassString);
var toRemoveArray = arrayDifference(oldClassArray, newClassArray);
var toAddArray = arrayDifference(newClassArray, oldClassArray);
var toRemoveString = digestClassCounts(toRemoveArray, -1);
var toAddString = digestClassCounts(toAddArray, 1);
attr.$addClass(toAddString);
attr.$removeClass(toRemoveString);
}
function digestClassCounts(classArray, count) {
var classesToUpdate = [];
forEach(classes, function(className) {
forEach(classArray, function(className) {
if (count > 0 || classCounts[className]) {
classCounts[className] = (classCounts[className] || 0) + count;
if (classCounts[className] === +(count > 0)) {
@@ -60,77 +80,106 @@ function classDirective(name, selector) {
}
}
});
element.data('$classCounts', classCounts);
return classesToUpdate.join(' ');
}
function updateClasses(oldClasses, newClasses) {
var toAdd = arrayDifference(newClasses, oldClasses);
var toRemove = arrayDifference(oldClasses, newClasses);
toAdd = digestClassCounts(toAdd, 1);
toRemove = digestClassCounts(toRemove, -1);
if (toAdd && toAdd.length) {
$animate.addClass(element, toAdd);
function ngClassIndexWatchAction(newModulo) {
// This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it
// adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
// `ngClass[OneTime]WatchAction()` will update the classes.
if (newModulo === selector) {
addClasses(oldClassString);
} else {
removeClasses(oldClassString);
}
if (toRemove && toRemove.length) {
$animate.removeClass(element, toRemove);
oldModulo = newModulo;
}
function ngClassOneTimeWatchAction(newClassValue) {
var newClassString = toClassString(newClassValue);
if (newClassString !== oldClassString) {
ngClassWatchAction(newClassString);
}
}
function ngClassWatchAction(newVal) {
// eslint-disable-next-line no-bitwise
if (selector === true || (scope.$index & 1) === selector) {
var newClasses = arrayClasses(newVal || []);
if (!oldVal) {
addClasses(newClasses);
} else if (!equals(newVal,oldVal)) {
var oldClasses = arrayClasses(oldVal);
updateClasses(oldClasses, newClasses);
}
}
if (isArray(newVal)) {
oldVal = newVal.map(function(v) { return shallowCopy(v); });
} else {
oldVal = shallowCopy(newVal);
function ngClassWatchAction(newClassString) {
if (oldModulo === selector) {
updateClasses(oldClassString, newClassString);
}
oldClassString = newClassString;
}
}
};
function arrayDifference(tokens1, tokens2) {
var values = [];
outer:
for (var i = 0; i < tokens1.length; i++) {
var token = tokens1[i];
for (var j = 0; j < tokens2.length; j++) {
if (token === tokens2[j]) continue outer;
}
values.push(token);
}
return values;
}
function arrayClasses(classVal) {
var classes = [];
if (isArray(classVal)) {
forEach(classVal, function(v) {
classes = classes.concat(arrayClasses(v));
});
return classes;
} else if (isString(classVal)) {
return classVal.split(' ');
} else if (isObject(classVal)) {
forEach(classVal, function(v, k) {
if (v) {
classes = classes.concat(k.split(' '));
}
});
return classes;
}
return classVal;
}
}];
// Helpers
function arrayDifference(tokens1, tokens2) {
if (!tokens1 || !tokens1.length) return [];
if (!tokens2 || !tokens2.length) return tokens1;
var values = [];
outer:
for (var i = 0; i < tokens1.length; i++) {
var token = tokens1[i];
for (var j = 0; j < tokens2.length; j++) {
if (token === tokens2[j]) continue outer;
}
values.push(token);
}
return values;
}
function split(classString) {
return classString && classString.split(' ');
}
function toClassString(classValue) {
var classString = classValue;
if (isArray(classValue)) {
classString = classValue.map(toClassString).join(' ');
} else if (isObject(classValue)) {
classString = Object.keys(classValue).
filter(function(key) { return classValue[key]; }).
join(' ');
}
return classString;
}
function toFlatValue(classValue) {
var flatValue = classValue;
if (isArray(classValue)) {
flatValue = classValue.map(toFlatValue);
} else if (isObject(classValue)) {
var hasUndefined = false;
flatValue = Object.keys(classValue).filter(function(key) {
var value = classValue[key];
if (!hasUndefined && isUndefined(value)) {
hasUndefined = true;
}
return value;
});
if (hasUndefined) {
// Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
// the watcher, by including at least one `undefined` value.
flatValue.push(undefined);
}
}
return flatValue;
}
}
/**
+6 -6
View File
@@ -53,15 +53,15 @@ forEach(
return {
restrict: 'A',
compile: function($element, attr) {
// We expose the powerful $event object on the scope that provides access to the Window,
// etc. that isn't protected by the fast paths in $parse. We explicitly request better
// checks at the cost of speed since event handler expressions are not executed as
// frequently as regular change detection.
var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
// NOTE:
// We expose the powerful `$event` object on the scope that provides access to the Window,
// etc. This is OK, because expressions are not sandboxed any more (and the expression
// sandbox was never meant to be a security feature anyway).
var fn = $parse(attr[directiveName]);
return function ngEventHandler(scope, element) {
element.on(eventName, function(event) {
var callback = function() {
fn(scope, {$event:event});
fn(scope, {$event: event});
};
if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
scope.$evalAsync(callback);
+72 -21
View File
@@ -31,32 +31,57 @@ var ngModelMinErr = minErr('ngModel');
* @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
* String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
* is set.
*
* @property {*} $modelValue The value in the model that the control is bound to.
*
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
the control reads value from the DOM. The functions are called in array order, each passing
its return value through to the next. The last return value is forwarded to the
{@link ngModel.NgModelController#$validators `$validators`} collection.
* the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
`$viewValue`} from the DOM, usually via user input.
See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
`$viewValue`}.
The functions are called in array order, each passing
its return value through to the next. The last return value is forwarded to the
{@link ngModel.NgModelController#$validators `$validators`} collection.
Returning `undefined` from a parser means a parse error occurred. In that case,
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
`$viewValue`}.
Returning `undefined` from a parser means a parse error occurred. In that case,
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
This simple example shows a parser that would convert text input value to lowercase:
* ```js
* function parse(value) {
* if (value) {
* return value.toLowerCase();
* }
* }
* ngModelController.$parsers.push(parse);
* ```
*
* @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
the model value changes. The functions are called in reverse array order, each passing the value through to the
next. The last return value is used as the actual DOM value.
Used to format / convert values for display in the control.
the bound ngModel expression changes programmatically. The `$formatters` are not called when the
value of the control is changed by user interaction.
Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
`$modelValue`} for display in the control.
The functions are called in reverse array order, each passing the value through to the
next. The last return value is used as the actual DOM value.
This simple example shows a formatter that would convert the model value to uppercase:
* ```js
* function formatter(value) {
* function format(value) {
* if (value) {
* return value.toUpperCase();
* }
* }
* ngModel.$formatters.push(formatter);
* ngModel.$formatters.push(format);
* ```
*
* @property {Object.<string, function>} $validators A collection of validators that are applied
@@ -256,7 +281,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
this.$$currentValidationRunId = 0;
this.$$scope = $scope;
// https://github.com/angular/angular.js/issues/15833
// Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched
Object.defineProperty(this, '$$scope', {value: $scope});
this.$$attr = $attr;
this.$$element = $element;
this.$$animate = $animate;
@@ -764,9 +791,10 @@ NgModelController.prototype = {
*
* When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
* and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
* value sent directly for processing, finally to be applied to `$modelValue` and then the
* **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
* in the `$viewChangeListeners` list, are called.
* value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
* `$asyncValidators` are called and the value is applied to `$modelValue`.
* Finally, the value is set to the **expression** specified in the `ng-model` attribute and
* all the registered change listeners, in the `$viewChangeListeners` list are called.
*
* In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
* and the `default` trigger is not listed, all those actions will remain pending until one of the
@@ -829,6 +857,29 @@ NgModelController.prototype = {
that.$commitViewValue();
});
}
},
/**
* @ngdoc method
*
* @name ngModel.NgModelController#$overrideModelOptions
*
* @description
*
* Override the current model options settings programmatically.
*
* The previous `ModelOptions` value will not be modified. Instead, a
* new `ModelOptions` object will inherit from the previous one overriding
* or inheriting settings that are defined in the given parameter.
*
* See {@link ngModelOptions} for information about what options can be specified
* and how model option inheritance works.
*
* @param {Object} options a hash of settings to override the previous options
*
*/
$overrideModelOptions: function(options) {
this.$options = this.$options.createChild(options);
}
};
@@ -841,8 +892,8 @@ function setupModelWatcher(ctrl) {
// -> scope value did not change since the last digest as
// ng-change executes in apply phase
// 4. view should be changed back to 'a'
ctrl.$$scope.$watch(function ngModelWatch() {
var modelValue = ctrl.$$ngModelGet(ctrl.$$scope);
ctrl.$$scope.$watch(function ngModelWatch(scope) {
var modelValue = ctrl.$$ngModelGet(scope);
// if scope model value and ngModel value are out of sync
// TODO(perf): why not move this to the action fn?
@@ -891,7 +942,7 @@ function setupModelWatcher(ctrl) {
* (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
* The `validationErrorKey` should be in camelCase and will get converted into dash-case
* for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
* class and can be bound to as `{{someForm.someControl.$error.myError}}` .
* classes and can be bound to as `{{ someForm.someControl.$error.myError }}`.
* @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
* or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
* Skipped is used by Angular when validators do not run because of parse errors and
+17 -9
View File
@@ -331,19 +331,27 @@ defaultModelOptions = new ModelOptions({
*
*/
var ngModelOptionsDirective = function() {
NgModelOptionsController.$inject = ['$attrs', '$scope'];
function NgModelOptionsController($attrs, $scope) {
this.$$attrs = $attrs;
this.$$scope = $scope;
}
NgModelOptionsController.prototype = {
$onInit: function() {
var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions;
var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions);
this.$options = parentOptions.createChild(modelOptionsDefinition);
}
};
return {
restrict: 'A',
// ngModelOptions needs to run before ngModel and input directives
priority: 10,
require: ['ngModelOptions', '?^^ngModelOptions'],
controller: function NgModelOptionsController() {},
link: {
pre: function ngModelOptionsPreLinkFn(scope, element, attrs, ctrls) {
var optionsCtrl = ctrls[0];
var parentOptions = ctrls[1] ? ctrls[1].$options : defaultModelOptions;
optionsCtrl.$options = parentOptions.createChild(scope.$eval(attrs.ngModelOptions));
}
}
require: {parentCtrl: '?^^ngModelOptions'},
bindToController: true,
controller: NgModelOptionsController
};
};
+48 -46
View File
@@ -111,13 +111,8 @@ var ngOptionsMinErr = minErr('ngOptions');
* is not matched against any `<option>` and the `<select>` appears as having no selected value.
*
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required The control is considered valid only if value is entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
* @param {comprehension_expression=} ngOptions in one of the following forms:
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {comprehension_expression} ngOptions in one of the following forms:
*
* * for array data sources:
* * `label` **`for`** `value` **`in`** `array`
@@ -156,6 +151,13 @@ var ngOptionsMinErr = minErr('ngOptions');
* used to identify the objects in the array. The `trackexpr` will most likely refer to the
* `value` variable (e.g. `value.propertyName`). With this the selection is preserved
* even when the options are recreated (e.g. reloaded from the server).
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required The control is considered valid only if value is entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
*
* @example
<example module="selectExample" name="select">
@@ -405,7 +407,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
// we can't just jqLite('<option>') since jqLite is not smart enough
// Support: IE 9 only
// We can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
var optionTemplate = window.document.createElement('option'),
optGroupTemplate = window.document.createElement('optgroup');
@@ -426,6 +429,9 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
}
// The empty option will be compiled and rendered before we first generate the options
selectElement.empty();
var providedEmptyOption = !!selectCtrl.emptyOption;
var unknownOption = jqLite(optionTemplate.cloneNode(false));
@@ -447,12 +453,15 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (!multiple) {
selectCtrl.writeValue = function writeNgOptionsValue(value) {
var selectedOption = options.selectValueMap[selectElement.val()];
// The options might not be defined yet when ngModel tries to render
if (!options) return;
var selectedOption = selectElement[0].options[selectElement[0].selectedIndex];
var option = options.getOptionFromViewValue(value);
// Make sure to remove the selected attribute from the previously selected option
// Otherwise, screen readers might get confused
if (selectedOption) selectedOption.element.removeAttribute('selected');
if (selectedOption) selectedOption.removeAttribute('selected');
if (option) {
// Don't update the option when it is already selected.
@@ -462,7 +471,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (selectElement[0].value !== option.selectValue) {
selectCtrl.removeUnknownOption();
selectCtrl.unselectEmptyOption();
selectElement[0].value = option.selectValue;
option.element.selected = true;
@@ -470,14 +478,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
option.element.setAttribute('selected', 'selected');
} else {
if (providedEmptyOption) {
selectCtrl.selectEmptyOption();
} else if (selectCtrl.unknownOption.parent().length) {
selectCtrl.updateUnknownOption(value);
} else {
selectCtrl.renderUnknownOption(value);
}
selectCtrl.selectUnknownOrEmptyOption(value);
}
};
@@ -505,17 +506,19 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
} else {
selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
options.items.forEach(function(option) {
option.element.selected = false;
});
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
// The options might not be defined yet when ngModel tries to render
if (!options) return;
if (value) {
value.forEach(function(item) {
var option = options.getOptionFromViewValue(item);
if (option) option.element.selected = true;
});
}
// Only set `<option>.selected` if necessary, in order to prevent some browsers from
// scrolling to `<option>` elements that are outside the `<select>` element's viewport.
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
options.items.forEach(function(option) {
if (option.element.selected && !includes(selectedOptions, option)) {
option.element.selected = false;
}
});
};
@@ -550,13 +553,11 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (providedEmptyOption) {
// we need to remove it before calling selectElement.empty() because otherwise IE will
// remove the label from the element. wtf?
selectCtrl.emptyOption.remove();
// compile the element since there might be bindings in it
$compile(selectCtrl.emptyOption)(scope);
selectElement.prepend(selectCtrl.emptyOption);
if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
// This means the empty option has currently no actual DOM node, probably because
// it has been modified by a transclusion directive.
@@ -574,8 +575,12 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
ngModelCtrl.$render();
optionEl.on('$destroy', function() {
var needsRerender = selectCtrl.$isEmptyOptionSelected();
selectCtrl.hasEmptyOption = false;
selectCtrl.emptyOption = undefined;
if (needsRerender) ngModelCtrl.$render();
});
}
};
@@ -588,12 +593,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
selectElement.empty();
// We need to do this here to ensure that the options object is defined
// when we first hit it in writeNgOptionsValue
updateOptions();
// We will re-render the option elements if the option values or labels change
scope.$watchCollection(ngOptions.getWatchables, updateOptions);
@@ -605,11 +604,20 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
updateOptionElement(option, optionElement);
}
function getAndUpdateSelectedOption(viewValue) {
var option = options.getOptionFromViewValue(viewValue);
var element = option && option.element;
if (element && !element.selected) element.selected = true;
return option;
}
function updateOptionElement(option, element) {
option.element = element;
element.disabled = option.disabled;
// NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
// Support: IE 11 only, Edge 12-13 only
// NOTE: The label must be set before the value, otherwise IE 11 & Edge create unresponsive
// selects in certain circumstances when multiple selects are next to each other and display
// the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
// See https://github.com/angular/angular.js/issues/11314 for more info.
@@ -645,11 +653,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
var groupElementMap = {};
// Ensure that the empty option is always there if it was explicitly provided
if (providedEmptyOption) {
selectElement.prepend(selectCtrl.emptyOption);
}
options.items.forEach(function addOption(option) {
var groupElement;
@@ -694,7 +697,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
ngModelCtrl.$render();
}
}
}
}
+2 -1
View File
@@ -6,6 +6,7 @@
* @ngdoc directive
* @name ngRepeat
* @multiElement
* @restrict A
*
* @description
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
@@ -428,7 +429,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
// Store a list of elements from previous run. This is a hash where key is the item from the
// iterator, and the value is objects with following properties.
// - scope: bound scope
// - element: previous element.
// - clone: previous element.
// - index: position
//
// We are using no-proto object so that we don't need to guard against inherited props via
+224 -159
View File
@@ -8,11 +8,13 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* @multiElement
*
* @description
* The `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. The `.ng-hide` CSS class is predefined
* in AngularJS and sets the display style to none (using an !important flag).
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
* The `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.
* The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
* `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
* {@link ng.directive:ngCsp ngCsp}).
*
* ```html
* <!-- when $scope.myValue is truthy (element is visible) -->
@@ -22,31 +24,32 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* <div ng-show="myValue" class="ng-hide"></div>
* ```
*
* When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
* attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
* from the element causing the element not to appear hidden.
* When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
* to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
* CSS class is removed from the element causing the element not to appear hidden.
*
* ## Why is !important used?
* ## Why is `!important` used?
*
* You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
* can be easily overridden by heavier selectors. For example, something as simple
* as changing the display style on a HTML list item would make hidden elements appear visible.
* This also becomes a bigger issue when dealing with CSS frameworks.
* You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
* `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
* simple as changing the display style on a HTML list item would make hidden elements appear
* visible. This also becomes a bigger issue when dealing with CSS frameworks.
*
* By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
* specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
* styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
* By using `!important`, the show and hide behavior will work as expected despite any clash between
* CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
* developer chooses to override the styling to change how to hide an element then it is just a
* matter of using `!important` in their own CSS code.
*
* ### Overriding `.ng-hide`
*
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
* class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
* with extra animation classes that can be added.
* By default, the `.ng-hide` class will style the element with `display: none !important`. If you
* wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
* the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
* `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
*
* ```css
* .ng-hide:not(.ng-hide-animate) {
* /&#42; this is just another form of hiding an element &#42;/
* /&#42; These are just alternative ways of hiding an element &#42;/
* display: block!important;
* position: absolute;
* top: -9999px;
@@ -54,29 +57,20 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* }
* ```
*
* By default you don't need to override in CSS anything and the animations will work around the display style.
* By default you don't need to override anything in CSS and the animations will work around the
* display style.
*
* ## A note about animations with `ngShow`
*
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
* is true and false. This system works like the animation system present with ngClass except that
* you must also include the !important flag to override the display property
* so that you can perform an animation when the element is hidden during the time of the animation.
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
* directive expression is true and false. This system works like the animation system present with
* `ngClass` except that you must also include the `!important` flag to override the display
* property so that the elements are not actually hidden during the animation.
*
* ```css
* //
* //a working example can be found at the bottom of this page
* //
* /&#42; A working example can be found at the bottom of this page. &#42;/
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
* /&#42; this is required as of 1.3x to properly
* apply all styling in a show/hide animation &#42;/
* transition: 0s linear all;
* }
*
* .my-element.ng-hide-add-active,
* .my-element.ng-hide-remove-active {
* /&#42; the transition is defined in the active class &#42;/
* transition: 1s linear all;
* transition: all 0.5s linear;
* }
*
* .my-element.ng-hide-add { ... }
@@ -85,76 +79,108 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
*
* @animations
* | Animation | Occurs |
* |----------------------------------|-------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
* | Animation | Occurs |
* |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
* | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. |
*
* @element ANY
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
* then the element is shown or hidden respectively.
* @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
* element is shown/hidden respectively.
*
* @example
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show">
* A simple example, animating the element's opacity:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
<file name="index.html">
Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
<div>
Show:
<div class="check-element animate-show" ng-show="checked">
<span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
</div>
Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
<div class="check-element animate-show-hide" ng-show="checked">
I show up when your checkbox is checked.
</div>
<div>
Hide:
<div class="check-element animate-show" ng-hide="checked">
<span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
</div>
</div>
</file>
<file name="glyphicons.css">
@import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
</file>
<file name="animations.css">
.animate-show {
line-height: 20px;
opacity: 1;
padding: 10px;
border: 1px solid black;
background: white;
.animate-show-hide.ng-hide {
opacity: 0;
}
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
.animate-show-hide.ng-hide-add,
.animate-show-hide.ng-hide-remove {
transition: all linear 0.5s;
}
.animate-show.ng-hide {
line-height: 0;
opacity: 0;
padding: 0 10px;
}
.check-element {
padding: 10px;
border: 1px solid black;
background: white;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
it('should check ngShow', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
it('should check ng-show / ng-hide', function() {
expect(thumbsUp.isDisplayed()).toBeFalsy();
expect(thumbsDown.isDisplayed()).toBeTruthy();
expect(checkElem.isDisplayed()).toBe(false);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(true);
});
</file>
</example>
*
* <hr />
* @example
* A more complex example, featuring different show/hide animations:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
<file name="index.html">
Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
<div class="check-element funky-show-hide" ng-show="checked">
I show up when your checkbox is checked.
</div>
</file>
<file name="animations.css">
body {
overflow: hidden;
perspective: 1000px;
}
element(by.model('checked')).click();
.funky-show-hide.ng-hide-add {
transform: rotateZ(0);
transform-origin: right;
transition: all 0.5s ease-in-out;
}
expect(thumbsUp.isDisplayed()).toBeTruthy();
expect(thumbsDown.isDisplayed()).toBeFalsy();
.funky-show-hide.ng-hide-add.ng-hide-add-active {
transform: rotateZ(-135deg);
}
.funky-show-hide.ng-hide-remove {
transform: rotateY(90deg);
transform-origin: left;
transition: all 0.5s ease;
}
.funky-show-hide.ng-hide-remove.ng-hide-remove-active {
transform: rotateY(0);
}
.check-element {
border: 1px solid black;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
it('should check ngShow', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
expect(checkElem.isDisplayed()).toBe(false);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(true);
});
</file>
</example>
@@ -184,11 +210,13 @@ var ngShowDirective = ['$animate', function($animate) {
* @multiElement
*
* @description
* The `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 `.ng-hide` CSS class is predefined
* in AngularJS and sets the display style to none (using an !important flag).
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
* The `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 `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
* `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
* {@link ng.directive:ngCsp ngCsp}).
*
* ```html
* <!-- when $scope.myValue is truthy (element is hidden) -->
@@ -198,30 +226,32 @@ var ngShowDirective = ['$animate', function($animate) {
* <div ng-hide="myValue"></div>
* ```
*
* When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
* attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
* from the element causing the element not to appear hidden.
* When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
* to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
* CSS class is removed from the element causing the element not to appear hidden.
*
* ## Why is !important used?
* ## Why is `!important` used?
*
* You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
* can be easily overridden by heavier selectors. For example, something as simple
* as changing the display style on a HTML list item would make hidden elements appear visible.
* This also becomes a bigger issue when dealing with CSS frameworks.
* You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
* `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
* simple as changing the display style on a HTML list item would make hidden elements appear
* visible. This also becomes a bigger issue when dealing with CSS frameworks.
*
* By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
* specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
* styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
* By using `!important`, the show and hide behavior will work as expected despite any clash between
* CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
* developer chooses to override the styling to change how to hide an element then it is just a
* matter of using `!important` in their own CSS code.
*
* ### Overriding `.ng-hide`
*
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
* class in CSS:
* By default, the `.ng-hide` class will style the element with `display: none !important`. If you
* wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
* the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
* `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
*
* ```css
* .ng-hide {
* /&#42; this is just another form of hiding an element &#42;/
* .ng-hide:not(.ng-hide-animate) {
* /&#42; These are just alternative ways of hiding an element &#42;/
* display: block!important;
* position: absolute;
* top: -9999px;
@@ -229,20 +259,20 @@ var ngShowDirective = ['$animate', function($animate) {
* }
* ```
*
* By default you don't need to override in CSS anything and the animations will work around the display style.
* By default you don't need to override in CSS anything and the animations will work around the
* display style.
*
* ## A note about animations with `ngHide`
*
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
* is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
* CSS class is added and removed for you instead of your own CSS class.
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
* directive expression is true and false. This system works like the animation system present with
* `ngClass` except that you must also include the `!important` flag to override the display
* property so that the elements are not actually hidden during the animation.
*
* ```css
* //
* //a working example can be found at the bottom of this page
* //
* /&#42; A working example can be found at the bottom of this page. &#42;/
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
* transition: 0.5s linear all;
* transition: all 0.5s linear;
* }
*
* .my-element.ng-hide-add { ... }
@@ -251,74 +281,109 @@ var ngShowDirective = ['$animate', function($animate) {
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
*
* @animations
* | Animation | Occurs |
* |----------------------------------|-------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
* | Animation | Occurs |
* |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. |
* | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
*
*
* @element ANY
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
* the element is shown or hidden respectively.
* @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
* element is hidden/shown respectively.
*
* @example
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide">
* A simple example, animating the element's opacity:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
<file name="index.html">
Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
<div>
Show:
<div class="check-element animate-hide" ng-show="checked">
<span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
</div>
Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
<div class="check-element animate-show-hide" ng-hide="checked">
I hide when your checkbox is checked.
</div>
<div>
Hide:
<div class="check-element animate-hide" ng-hide="checked">
<span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
</div>
</div>
</file>
<file name="glyphicons.css">
@import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
</file>
<file name="animations.css">
.animate-hide {
transition: all linear 0.5s;
line-height: 20px;
opacity: 1;
padding: 10px;
border: 1px solid black;
background: white;
.animate-show-hide.ng-hide {
opacity: 0;
}
.animate-hide.ng-hide {
line-height: 0;
opacity: 0;
padding: 0 10px;
.animate-show-hide.ng-hide-add,
.animate-show-hide.ng-hide-remove {
transition: all linear 0.5s;
}
.check-element {
padding: 10px;
border: 1px solid black;
background: white;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
it('should check ngHide', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
it('should check ng-show / ng-hide', function() {
expect(thumbsUp.isDisplayed()).toBeFalsy();
expect(thumbsDown.isDisplayed()).toBeTruthy();
expect(checkElem.isDisplayed()).toBe(true);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(false);
});
</file>
</example>
*
* <hr />
* @example
* A more complex example, featuring different show/hide animations:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
<file name="index.html">
Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
<div class="check-element funky-show-hide" ng-hide="checked">
I hide when your checkbox is checked.
</div>
</file>
<file name="animations.css">
body {
overflow: hidden;
perspective: 1000px;
}
element(by.model('checked')).click();
.funky-show-hide.ng-hide-add {
transform: rotateZ(0);
transform-origin: right;
transition: all 0.5s ease-in-out;
}
expect(thumbsUp.isDisplayed()).toBeTruthy();
expect(thumbsDown.isDisplayed()).toBeFalsy();
.funky-show-hide.ng-hide-add.ng-hide-add-active {
transform: rotateZ(-135deg);
}
.funky-show-hide.ng-hide-remove {
transform: rotateY(90deg);
transform-origin: left;
transition: all 0.5s ease;
}
.funky-show-hide.ng-hide-remove.ng-hide-remove-active {
transform: rotateY(0);
}
.check-element {
border: 1px solid black;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
it('should check ngHide', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
expect(checkElem.isDisplayed()).toBe(true);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(false);
});
</file>
</example>
+231 -34
View File
@@ -4,19 +4,141 @@
var noopNgModelController = { $setViewValue: noop, $render: noop };
function setOptionSelectedStatus(optionEl, value) {
optionEl.prop('selected', value);
/**
* When unselecting an option, setting the property to null / false should be enough
* However, screenreaders might react to the selected attribute instead, see
* https://github.com/angular/angular.js/issues/14419
* Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
* or null
*/
optionEl.attr('selected', value);
}
/**
* @ngdoc type
* @name select.SelectController
*
* @description
* The controller for the `<select>` directive. This provides support for reading
* and writing the selected value(s) of the control and also coordinates dynamically
* added `<option>` elements, perhaps by an `ngRepeat` directive.
* The controller for the {@link ng.select select} directive. The controller exposes
* a few utility methods that can be used to augment the behavior of a regular or an
* {@link ng.ngOptions ngOptions} select element.
*
* @example
* ### Set a custom error when the unknown option is selected
*
* This example sets a custom error "unknownValue" on the ngModelController
* when the select element's unknown option is selected, i.e. when the model is set to a value
* that is not matched by any option.
*
* <example name="select-unknown-value-error" module="staticSelect">
* <file name="index.html">
* <div ng-controller="ExampleController">
* <form name="myForm">
* <label for="testSelect"> Single select: </label><br>
* <select name="testSelect" ng-model="selected" unknown-value-error>
* <option value="option-1">Option 1</option>
* <option value="option-2">Option 2</option>
* </select><br>
* <span ng-if="myForm.testSelect.$error.unknownValue">Error: The current model doesn't match any option</span>
*
* <button ng-click="forceUnknownOption()">Force unknown option</button><br>
* </form>
* </div>
* </file>
* <file name="app.js">
* angular.module('staticSelect', [])
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.selected = null;
*
* $scope.forceUnknownOption = function() {
* $scope.selected = 'nonsense';
* };
* }])
* .directive('unknownValueError', function() {
* return {
* require: ['ngModel', 'select'],
* link: function(scope, element, attrs, ctrls) {
* var ngModelCtrl = ctrls[0];
* var selectCtrl = ctrls[1];
*
* ngModelCtrl.$validators.unknownValue = function(modelValue, viewValue) {
* if (selectCtrl.$isUnknownOptionSelected()) {
* return false;
* }
*
* return true;
* };
* }
*
* };
* });
* </file>
*</example>
*
*
* @example
* ### Set the "required" error when the unknown option is selected.
*
* By default, the "required" error on the ngModelController is only set on a required select
* when the empty option is selected. This example adds a custom directive that also sets the
* error when the unknown option is selected.
*
* <example name="select-unknown-value-required" module="staticSelect">
* <file name="index.html">
* <div ng-controller="ExampleController">
* <form name="myForm">
* <label for="testSelect"> Select: </label><br>
* <select name="testSelect" ng-model="selected" unknown-value-required>
* <option value="option-1">Option 1</option>
* <option value="option-2">Option 2</option>
* </select><br>
* <span ng-if="myForm.testSelect.$error.required">Error: Please select a value</span><br>
*
* <button ng-click="forceUnknownOption()">Force unknown option</button><br>
* </form>
* </div>
* </file>
* <file name="app.js">
* angular.module('staticSelect', [])
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.selected = null;
*
* $scope.forceUnknownOption = function() {
* $scope.selected = 'nonsense';
* };
* }])
* .directive('unknownValueRequired', function() {
* return {
* priority: 1, // This directive must run after the required directive has added its validator
* require: ['ngModel', 'select'],
* link: function(scope, element, attrs, ctrls) {
* var ngModelCtrl = ctrls[0];
* var selectCtrl = ctrls[1];
*
* var originalRequiredValidator = ngModelCtrl.$validators.required;
*
* ngModelCtrl.$validators.required = function() {
* if (attrs.required && selectCtrl.$isUnknownOptionSelected()) {
* return false;
* }
*
* return originalRequiredValidator.apply(this, arguments);
* };
* }
* };
* });
* </file>
*</example>
*
*
*/
var SelectController =
['$element', '$scope', /** @this */ function($element, $scope) {
var self = this,
optionsMap = new HashMap();
optionsMap = new NgMap();
self.selectValueMap = {}; // Keys are the hashed values, values the original values
@@ -28,15 +150,18 @@ var SelectController =
// does not match any of the options. When it is rendered the value of the unknown
// option is '? XXX ?' where XXX is the hashKey of the value that is not known.
//
// Support: IE 9 only
// We can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
self.unknownOption = jqLite(window.document.createElement('option'));
// The empty option is an option with the value '' that te application developer can
// provide inside the select. When the model changes to a value that doesn't match an option,
// it is selected - so if an empty option is provided, no unknown option is generated.
// However, the empty option is not removed when the model matches an option. It is always selectable
// and indicates that a "null" selection has been made.
// The empty option is an option with the value '' that the application developer can
// provide inside the select. It is always selectable and indicates that a "null" selection has
// been made by the user.
// If the select has an empty option, and the model of the select is set to "undefined" or "null",
// the empty option is selected.
// If the model is set to a different unmatched value, the unknown option is rendered and
// selected, i.e both are present, because a "null" selection and an unknown value are different.
self.hasEmptyOption = false;
self.emptyOption = undefined;
@@ -44,14 +169,14 @@ var SelectController =
var unknownVal = self.generateUnknownOptionValue(val);
self.unknownOption.val(unknownVal);
$element.prepend(self.unknownOption);
setOptionAsSelected(self.unknownOption);
setOptionSelectedStatus(self.unknownOption, true);
$element.val(unknownVal);
};
self.updateUnknownOption = function(val) {
var unknownVal = self.generateUnknownOptionValue(val);
self.unknownOption.val(unknownVal);
setOptionAsSelected(self.unknownOption);
setOptionSelectedStatus(self.unknownOption, true);
$element.val(unknownVal);
};
@@ -66,13 +191,13 @@ var SelectController =
self.selectEmptyOption = function() {
if (self.emptyOption) {
$element.val('');
setOptionAsSelected(self.emptyOption);
setOptionSelectedStatus(self.emptyOption, true);
}
};
self.unselectEmptyOption = function() {
if (self.hasEmptyOption) {
self.emptyOption.removeAttr('selected');
setOptionSelectedStatus(self.emptyOption, false);
}
};
@@ -102,7 +227,7 @@ var SelectController =
// Make sure to remove the selected attribute from the previously selected option
// Otherwise, screen readers might get confused
var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
if (currentlySelectedOption) currentlySelectedOption.removeAttribute('selected');
if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
if (self.hasOption(value)) {
self.removeUnknownOption();
@@ -112,16 +237,9 @@ var SelectController =
// Set selected attribute and property on selected option for screen readers
var selectedOption = $element[0].options[$element[0].selectedIndex];
setOptionAsSelected(jqLite(selectedOption));
setOptionSelectedStatus(jqLite(selectedOption), true);
} else {
if (value == null && self.emptyOption) {
self.removeUnknownOption();
self.selectEmptyOption();
} else if (self.unknownOption.parent().length) {
self.updateUnknownOption(value);
} else {
self.renderUnknownOption(value);
}
self.selectUnknownOrEmptyOption(value);
}
};
@@ -137,7 +255,7 @@ var SelectController =
self.emptyOption = element;
}
var count = optionsMap.get(value) || 0;
optionsMap.put(value, count + 1);
optionsMap.set(value, count + 1);
// Only render at the end of a digest. This improves render performance when many options
// are added during a digest and ensures all relevant options are correctly marked as selected
scheduleRender();
@@ -148,13 +266,13 @@ var SelectController =
var count = optionsMap.get(value);
if (count) {
if (count === 1) {
optionsMap.remove(value);
optionsMap.delete(value);
if (value === '') {
self.hasEmptyOption = false;
self.emptyOption = undefined;
}
} else {
optionsMap.put(value, count - 1);
optionsMap.set(value, count - 1);
}
}
};
@@ -164,6 +282,59 @@ var SelectController =
return !!optionsMap.get(value);
};
/**
* @ngdoc method
* @name select.SelectController#$hasEmptyOption
*
* @description
*
* Returns `true` if the select element currently has an empty option
* element, i.e. an option that signifies that the select is empty / the selection is null.
*
*/
self.$hasEmptyOption = function() {
return self.hasEmptyOption;
};
/**
* @ngdoc method
* @name select.SelectController#$isUnknownOptionSelected
*
* @description
*
* Returns `true` if the select element's unknown option is selected. The unknown option is added
* and automatically selected whenever the select model doesn't match any option.
*
*/
self.$isUnknownOptionSelected = function() {
// Presence of the unknown option means it is selected
return $element[0].options[0] === self.unknownOption[0];
};
/**
* @ngdoc method
* @name select.SelectController#$isEmptyOptionSelected
*
* @description
*
* Returns `true` if the select element has an empty option and this empty option is currently
* selected. Returns `false` if the select element has no empty option or it is not selected.
*
*/
self.$isEmptyOptionSelected = function() {
return self.hasEmptyOption && $element[0].options[$element[0].selectedIndex] === self.emptyOption[0];
};
self.selectUnknownOrEmptyOption = function(value) {
if (value == null && self.emptyOption) {
self.removeUnknownOption();
self.selectEmptyOption();
} else if (self.unknownOption.parent().length) {
self.updateUnknownOption(value);
} else {
self.renderUnknownOption(value);
}
};
var renderScheduled = false;
function scheduleRender() {
@@ -281,7 +452,7 @@ var SelectController =
var removeValue = optionAttrs.value;
self.removeOption(removeValue);
self.ngModelCtrl.$render();
scheduleRender();
if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 ||
currentValue === removeValue
@@ -292,11 +463,6 @@ var SelectController =
}
});
};
function setOptionAsSelected(optionEl) {
optionEl.prop('selected', true); // needed for IE
optionEl.attr('selected', true);
}
}];
/**
@@ -317,6 +483,9 @@ var SelectController =
* the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
* Value and textContent can be interpolated.
*
* The {@link select.SelectController select controller} exposes utility functions that can be used
* to manipulate the select's behavior.
*
* ## Matching model and option values
*
* In general, the match between the model and an option is evaluated by strictly comparing the model
@@ -366,6 +535,21 @@ var SelectController =
* interaction with the select element.
* @param {string=} ngOptions sets the options that the select is populated with and defines what is
* set on the model on selection. See {@link ngOptions `ngOptions`}.
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
*
*
* @knownIssue
*
* In Firefox, the select model is only updated when the select element is blurred. For example,
* when switching between options with the keyboard, the select model is only set to the
* currently selected option when the select is blurred, e.g via tab key or clicking the mouse
* outside the select.
*
* This is due to an ambiguity in the select element specification. See the
* [issue on the Firefox bug tracker](https://bugzilla.mozilla.org/show_bug.cgi?id=126379)
* for more information, and this
* [Github comment for a workaround](https://github.com/angular/angular.js/issues/9134#issuecomment-130800488)
*
* @example
* ### Simple `select` elements with static options
@@ -606,9 +790,22 @@ var selectDirective = function() {
// Write value now needs to set the selected property of each matching option
selectCtrl.writeValue = function writeMultipleValue(value) {
var items = new HashMap(value);
forEach(element.find('option'), function(option) {
option.selected = isDefined(items.get(option.value)) || isDefined(items.get(selectCtrl.selectValueMap[option.value]));
var shouldBeSelected = !!value && (includes(value, option.value) ||
includes(value, selectCtrl.selectValueMap[option.value]));
var currentlySelected = option.selected;
// Support: IE 9-11 only, Edge 12-15+
// In IE and Edge adding options to the selection via shift+click/UP/DOWN
// will de-select already selected options if "selected" on those options was set
// more than once (i.e. when the options were already selected)
// So we only modify the selected property if necessary.
// Note: this behavior cannot be replicated via unit tests because it only shows in the
// actual user interface.
if (shouldBeSelected !== currentlySelected) {
setOptionSelectedStatus(jqLite(option), shouldBeSelected);
}
});
};
+10 -3
View File
@@ -9,6 +9,9 @@
* Selects a subset of items from `array` and returns it as a new array.
*
* @param {Array} array The source array.
* <div class="alert alert-info">
* **Note**: If the array contains objects that reference themselves, filtering is not possible.
* </div>
* @param {string|Object|function()} expression The predicate to be used for selecting items from
* `array`.
*
@@ -42,8 +45,9 @@
* The final result is an array of those elements that the predicate returned true for.
*
* @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
* determining if the expected value (from the filter expression) and actual value (from
* the object in the array) should be considered a match.
* determining if values retrieved using `expression` (when it is not a function) should be
* considered a match based on the expected value (from the filter expression) and actual
* value (from the object in the array).
*
* Can be one of:
*
@@ -226,7 +230,10 @@ function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstA
var key;
if (matchAgainstAnyProp) {
for (key in actual) {
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
// Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
// See: https://github.com/angular/angular.js/issues/15644
if (key.charAt && (key.charAt(0) !== '$') &&
deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More