diff --git a/CHANGELOG.md b/CHANGELOG.md index adf609f08382..a545f98ca61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,1375 @@ + +# 1.6.0 XXXXX-YYYYY (2016-12-08) + +**Here are the full changes for the release of 1.6.0 that are not already released in the 1.5.x branch, +consolidating all the changes shown in the previous 1.6.0 release candidates.** + +## New Features +- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` + ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa), + [#10922](https://github.com/angular/angular.js/issues/10922)) +- **$compile:** set `preAssignBindingsEnabled` to false by default + ([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858), + [#15352](https://github.com/angular/angular.js/issues/15352)) +- **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 remove a boolean attribute for `.attr(attrName, '')` + ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)) + - remove the attribute for `.attr(attribute, null)` + ([4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)) + - return `[]` for `.val()` on ` + + + +``` + +The migration strategy is to convert values that matched with non-strict +conversion so that they will match with strict conversion. + + +- **feat(ngModelOptions): allow options to be inherited from ancestor `ngModelOptions` + ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa))**: + +The programmatic API for `ngModelOptions` has changed. You must now read options +via the `ngModelController.$options.getOption(name)` method, rather than accessing the +option directly as a property of the `ngModelContoller.$options` object. This does not +affect the usage in templates and only affects custom directives that might have been +reading options for their own purposes. + +One benefit of these changes, though, is that the `ngModelControler.$options` property +is now guaranteed to be defined so there is no need to check before accessing. + +So, previously: + +``` +var myOption = ngModelController.$options && ngModelController.$options['my-option']; +``` + +and now: + +``` +var myOption = ngModelController.$options.getOption('my-option'); +``` + +### **jqLite** due to: +- **[fc0c11](https://github.com/angular/angular.js/commit/fc0c11db845d53061430b7f05e773dcb3fb5b860)**: + camelCase keys in `jqLite#data` + +Previously, keys passed to the data method were left untouched. +Now they are internally camelCased similarly to how jQuery handles it, i.e. +only single (!) hyphens followed by a lowercase letter get converted to an +uppercase letter. This means keys `a-b` and `aB` represent the same data piece; +writing to one of them will also be reflected if you ask for the other one. + +If you use Angular with jQuery, it already behaved in this way so no changes +are required on your part. + +To migrate the code follow the examples below: + +BEFORE: + +```js +/* 1 */ +elem.data('my-key', 2); +elem.data('myKey', 3); + +/* 2 */ +elem.data('foo-bar', 42); +elem.data()['foo-bar']; // 42 +elem.data()['fooBar']; // undefined + +/* 3 */ +elem.data()['foo-bar'] = 1; +elem.data()['fooBar'] = 2; +elem.data('foo-bar'); // 1 +``` + +AFTER: + +```js +/* 1 */ +// Rename one of the keys as they would now map to the same data slot. +elem.data('my-key', 2); +elem.data('my-key2', 3); + +/* 2 */ +elem.data('foo-bar', 42); +elem.data()['foo-bar']; // undefined +elem.data()['fooBar']; // 42 + +/* 3 */ +elem.data()['foo-bar'] = 1; +elem.data()['fooBar'] = 2; +elem.data('foo-bar'); // 2 +``` + +- **[73050c](https://github.com/angular/angular.js/commit/73050cdda04675bfa6705dc841ddbbb6919eb048)**: + align jqLite camelCasing logic with JQuery + +Before, when Angular was used without jQuery, the key passed +to the css method was more heavily camelCased; now only a single (!) hyphen +followed by a lowercase letter is getting transformed. This also affects APIs +that rely on the css method, like ngStyle. + +If you use Angular with jQuery, it already behaved in this way so no changes +are needed on your part. + +To migrate the code follow the example below: + +Before: + +HTML: + +```html +// All five versions used to be equivalent. +
+
+
+
+
+``` + +JS: + +```js +// All five versions used to be equivalent. +elem.css('background_color', 'blue'); +elem.css('background:color', 'blue'); +elem.css('background-color', 'blue'); +elem.css('background--color', 'blue'); +elem.css('backgroundColor', 'blue'); + +// All five versions used to be equivalent. +var bgColor = elem.css('background_color'); +var bgColor = elem.css('background:color'); +var bgColor = elem.css('background-color'); +var bgColor = elem.css('background--color'); +var bgColor = elem.css('backgroundColor'); +``` + +After: + +HTML: + +```html +// Previous five versions are no longer equivalent but these two still are. +
+
+``` + +JS: + +```js +// Previous five versions are no longer equivalent but these two still are. +elem.css('background-color', 'blue'); +elem.css('backgroundColor', 'blue'); + +// Previous five versions are no longer equivalent but these two still are. +var bgColor = elem.css('background-color'); +var bgColor = elem.css('backgroundColor'); +``` + +- **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**: + don't remove a boolean attribute for `.attr(attrName, '')` + +Before, using the `attr` method with an empty string as a value +would remove the boolean attribute. Now it sets it to its lowercase name as +was happening for every non-empty string so far. The only two values that remove +the boolean attribute are now null & false, just like in jQuery. + +To migrate the code follow the example below: + +Before: + +```js +elem.attr(booleanAttrName, ''); +``` + +After: + +```js +elem.attr(booleanAttrName, false); +``` + +or: + +```js +elem.attr(booleanAttrName, null); +``` + +- **[4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)**: + remove the attribute for `.attr(attribute, null)` + +Invoking `elem.attr(attributeName, null)` would set the +`attributeName` attribute value to a string `"null"`, now it removes the +attribute instead. + +To migrate the code follow the example below: + +Before: + +```js +elem.attr(attributeName, null); +``` + +After: + +```js +elem.attr(attributeName, "null"); +``` + +- **[d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8)**: + return [] for .val() on ` + + + +``` + +JavaScript: + +```js + var value = $element.val(); + if (value) { + /* do something */ + } +``` + +After: + +HTML: + +```html + +``` + +JavaScript: + +```js + var value = $element.val(); + if (value.length > 0) { + /* do something */ + } +``` + + +### `ngModel` due to: + +- **[7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b)**: + treat synchronous validators as boolean always + +Previously, only a literal `false` return would resolve as the +synchronous validator failing. Now, all falsy JavaScript values +are treated as failing the validator, as one would naturally expect. + +Specifically, the values `0` (the number zero), `null`, `NaN` and `''` (the +empty string) used to be considered valid (passing) and they are now considered +invalid (failing). The value `undefined` was treated similarly to a pending +asynchronous validator, causing the validation to be pending. `undefined` is +also now considered invalid. + +To migrate, make sure your synchronous validators are returning either a +literal `true` or a literal `false` value. For most code, we expect this to +already be the case. Only a very small subset of projects will be affected. + +Namely, anyone using `undefined` or any falsy value as a return will now see +their validation failing, whereas previously falsy values other than `undefined` +would have been seen as passing and `undefined` would have been seen as pending. + +- **[9e24e7](https://github.com/angular/angular.js/commit/9e24e774a558143b3478536911a3a4c1714564ba)**: + change controllers to use prototype methods + +The use of prototype methods instead of new methods per instance removes the ability to pass +NgModelController and FormController methods without context. + +For example + +```js +$scope.$watch('something', myNgModelCtrl.$render) +``` + +will no longer work because the `$render` method is passed without any context. +This must now be replaced with + +```js +$scope.$watch('something', function() { + myNgModelCtrl.$render(); +}) +``` + +or possibly by using `Function.prototype.bind` or `angular.bind`. + + +### `aria/ngModel` due to: + +- **[975a61](https://github.com/angular/angular.js/commit/975a6170efceb2a5e6377c57329731c0636eb8c8)**: + do not overwrite the default `$isEmpty()` method for checkboxes + +Custom `checkbox`-shaped controls (e.g. checkboxes, menuitemcheckboxes), no longer have a custom +`$isEmpty()` method on their `NgModelController` that checks for `value === false`. Unless +overwritten, the default `$isEmpty()` method will be used, which treats `undefined`, `null`, `NaN` +and `''` as "empty". + +**Note:** The `$isEmpty()` method is used to determine if the checkbox is checked ("not empty" means + "checked") and thus it can indirectly affect other things, such as the control's validity + with respect to the `required` validator (e.g. "empty" + "required" --> "invalid"). + +Before: + +```js +var template = ''; +var customCheckbox = $compile(template)(scope); +var ctrl = customCheckbox.controller('ngModel'); + +scope.$apply('value = false'); +console.log(ctrl.$isEmpty()); //--> true + +scope.$apply('value = true'); +console.log(ctrl.$isEmpty()); //--> false + +scope.$apply('value = undefined'/* or null or NaN or '' */); +console.log(ctrl.$isEmpty()); //--> false +``` + +After: + +```js +var template = ''; +var customCheckbox = $compile(template)(scope); +var ctrl = customCheckbox.controller('ngModel'); + +scope.$apply('value = false'); +console.log(ctrl.$isEmpty()); //--> false + +scope.$apply('value = true'); +console.log(ctrl.$isEmpty()); //--> false + +scope.$apply('value = undefined'/* or null or NaN or '' */); +console.log(ctrl.$isEmpty()); //--> true +``` + +-- +If you want to have a custom `$isEmpty()` method, you need to overwrite the default. For example: + +```js +.directive('myCheckbox', function myCheckboxDirective() { + return { + require: 'ngModel', + link: function myCheckboxPostLink(scope, elem, attrs, ngModelCtrl) { + ngModelCtrl.$isEmpty = function myCheckboxIsEmpty(value) { + return !value; // Any falsy value means "empty" + + // Or to restore the previous behavior: + // return value === false; + }; + } + }; +}) +``` + +### `$http` due to: +- **[b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014)**: + remove deprecated callback methods: `success()/error()` + +`$http`'s deprecated custom callback methods - `success()` and `error()` - have been removed. +You can use the standard `then()`/`catch()` promise methods instead, but note that the method +signatures and return values are different. + +`success(fn)` can be replaced with `then(fn)`, and `error(fn)` can be replaced with either +`then(null, fn)` or `catch(fn)`. + +Before: + +```js +$http(...). + success(function onSuccess(data, status, headers, config) { + // Handle success + ... + }). + error(function onError(data, status, headers, config) { + // Handle error + ... + }); +``` + +After: + +```js +$http(...). + then(function onSuccess(response) { + // Handle success + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }, function onError(response) { + // Handle error + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }); + +// or + +$http(...). + then(function onSuccess(response) { + // Handle success + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }). + catch(function onError(response) { + // Handle error + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }); +``` + +**Note:** +There is a subtle difference between the variations showed above. When using +`$http(...).success(onSuccess).error(onError)` or `$http(...).then(onSuccess, onError)`, the +`onError()` callback will only handle errors/rejections produced by the `$http()` call. If the +`onSuccess()` callback produces an error/rejection, it won't be handled by `onError()` and might go +unnoticed. In contrast, when using `$http(...).then(onSuccess).catch(onError)`, `onError()` will +handle errors/rejections produced by both `$http()` _and_ `onSuccess()`. + +- **[fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb)**: + JSONP callback must be specified by `jsonpCallbackParam` config + +You can no longer use the `JSON_CALLBACK` placeholder in your JSONP requests. +Instead you must provide the name of the query parameter that will pass the +callback via the `jsonpCallbackParam` property of the config object, or app-wide via +the `$http.defaults.jsonpCallbackParam` property, which is `"callback"` by default. + +Before this change: + +```js +$http.json('trusted/url?callback=JSON_CALLBACK'); +$http.json('other/trusted/url', {params: {cb:'JSON_CALLBACK'}}); +``` + +After this change: + +```js +$http.json('trusted/url'); +$http.json('other/trusted/url', {jsonpCallbackParam:'cb'}); +``` + +- **[6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4)**: + JSONP requests now require a trusted resource URL + +All JSONP requests now require the URL to be trusted as resource URLs. +There are two approaches to trust a URL: + +**Whitelisting with the `$sceDelegateProvider.resourceUrlWhitelist()` +method.** + +You configure this list in a module configuration block: + +```js +appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) { + $sceDelegateProvider.resourceUrlWhiteList([ + // Allow same origin resource loads. + 'self', + // Allow JSONP calls that match this pattern + 'https://some.dataserver.com/**.jsonp?**' + ]); +}]); +``` + +**Explicitly trusting the URL via the `$sce.trustAsResourceUrl(url)` +method.** + +You can pass a trusted object instead of a string as a URL to the `$http` +service: + +```js +var promise = $http.jsonp($sce.trustAsResourceUrl(url)); +``` + +- **[4f6f2b](https://github.com/angular/angular.js/commit/4f6f2bce4ac93b85320e42e5023c09d099779b7d)**: + properly increment/decrement `$browser.outstandingRequestCount` + +HTTP requests now update the outstanding request count synchronously. +Previously the request count would not have been updated until the +request to the server is actually in flight. Now the request count is +updated before the async interceptor is called. + +The new behaviour is correct but it may change the expected behaviour in +a small number of e2e test cases where an async request interceptor is +being used. + + +### `$q` due to: + +- **[e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9)**: + treat thrown errors as regular rejections + +Previously, throwing an error from a promise's `onFulfilled` or `onRejection` handlers, would result +in passing the error to the `$exceptionHandler()` (in addition to rejecting the promise with the +error as reason). + +Now, a thrown error is treated exactly the same as a regular rejection. This applies to all +services/controllers/filters etc that rely on `$q` (including built-in services, such as `$http` and +`$route`). For example, `$http`'s `transformRequest/Response` functions or a route's `redirectTo` +function as well as functions specified in a route's `resolve` object, will no longer result in a +call to `$exceptionHandler()` if they throw an error. Other than that, everything will continue to +behave in the same way; i.e. the promises will be rejected, route transition will be cancelled, +`$routeChangeError` events will be broadcasted etc. + +- **[c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0)**: + report promises with non rejection callback + +Unhandled rejected promises will be logged to $exceptionHandler. + +Tests that depend on specific order or number of messages in $exceptionHandler +will need to handle rejected promises report. + + +### `ngTransclude` due to: + +- **[32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301)**: + use fallback content if only whitespace is provided + +Previously whitespace only transclusion would be treated as the transclusion +being "not empty", which meant that fallback content was not used in that +case. + +Now if you only provide whitespace as the transclusion content, it will be +assumed to be empty and the fallback content will be used instead. + +If you really do want whitespace then you can force it to be used by adding +a comment to the whitespace. + +Previously this would not fallback to default content: + +```html + + +``` + +Now the whitespace between the opening and closing tags is treated as empty. To force the +previous behaviour simply add a comment: + +```html + + +``` + + +### `$compile` due to: + +- **[13c252](https://github.com/angular/angular.js/commit/13c2522baf7c8f616b2efcaab4bffd54c8736591)**: + correctly merge consecutive text nodes on IE11 + +**Note:** Everything described below affects **IE11 only**. + +Previously, consecutive text nodes would not get merged if they had no parent. They will now, which +might have unexpectd side effects in the following cases: + +1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly: + + ```js + // Assuming: + var textNodes = [ + document.createTextNode('{{'), + document.createTextNode('"foo:"'), + document.createTextNode('}}') + ]; + var compiledNodes = $compile(textNodes)($rootScope); + + // Before: + console.log(compiledNodes.length); // 3 + console.log(compiledNodes.text()); // {{'foo'}} + + // After: + console.log(compiledNodes.length); // 1 + console.log(compiledNodes.text()); // foo + + // To get the old behavior, compile each node separately: + var textNodes = [ + document.createTextNode('{{'), + document.createTextNode('"foo"'), + document.createTextNode('}}') + ]; + var compiledNodes = angular.element(textNodes.map(function (node) { + return $compile(node)($rootScope)[0]; + })); + ``` + +2. Using multi-slot transclusion with non-consecutive, default-content text nodes (that form + interpolated expressions when merged): + + ```js + // Assuming the following component: + .component('someThing', { + template: '' + transclude: { + ignored: 'veryImportantContent' + } + }) + ``` + + ```html + + + {{ + Nooot + 'foo'}} + + + + + + {{ <-- Two separate + 'foo'}} <-- text nodes + + + + + + + foo <-- The text nodes were merged into `{{'foo'}}`, which was then interpolated + + + + + + + {{ + Nooot + 'foo'}} + + + + + + {{ <-- Two separate + 'foo'}} <-- nodes + + + ``` + +- **[b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4)**: + move check for interpolation of `on-"event"` attributes to compile time + +Using interpolation in any on* event attributes (e.g. `