Changelog History
Page 3
-
v1.6.4 Changes
March 31, 2017๐ Bug Fixes
- ๐ $parse:
- ngModel: prevent internal scope reference from being copied (e1f8a6, #15833)
- jqLite: make jqLite invoke jqLite.cleanData as a method (9cde98, #15846)
- $http: throw more informative error on invalid JSON response (df8887, #15695, #15724)
- dateFilter: correctly handle newlines in
format
string (982271, #15794, #15792)
๐ New Features
-
v1.6.3 Changes
March 08, 2017๐ Bug Fixes
- AngularJS:
- ๐ฒ $log: don't parse error stacks manually outside of IE/Edge (64e5af, #15590, #15767)
- $sanitize: prevent clobbered elements from freezing the browser (3bb1dd, #15699)
- $animate:
- filterFilter: don't throw if
key.charAt
is not a function (f27d19, #15644, #15660) - select:
- $jsonpCallbacks: allow
$window
to be mocked in unit tests (5ca0de, #15685, #15686)
๐ New Features
-
v1.6.2 Changes
February 07, 2017๐ Bug Fixes
- $compile:
- $location: correctly handle external URL change during
$digest
(b60761, #11075, #12571, #15556, #15561) - ๐ป $browser: detect external changes in
history.state
(fa50fb) - $resource:
- $animate: correctly animate transcluded clones with
templateUrl
(f01212, #15510, #15514) - $route: make asynchronous tasks count as pending requests (eb968c, #14159)
- ๐ $parse: make sure ES6 object computed properties are watched (5e418b, #15678)
- $sniffer: allow
history
for NW.js apps (4a593d, #15474, #15633) - input: fix
step
validation forinput[type=number/range]
(c95a67, #15504, #15506) - select: keep
ngModel
when selected option is recreated byngRepeat
(131af8, #15630, #15632) - ngValue: correctly update the
value
property whenvalue
is undefined (05aab6 #15603, #15605) - angularInit: allow auto-bootstrapping from inline script (bb464d, #15567, #15571)
- ngMockE2E: ensure that mocked
$httpBackend
uses correct$browser
(bd63b2, #15593)
๐ New Features
-
v1.6.1 Changes
December 23, 2016๐ Bug Fixes
- $q: Add traceback to unhandled promise rejections (174cb4, #14631)
- $$cookieReader: correctly handle forbidden access to
document.cookie
(33f769, #15523) - ngOptions: do not unset the
selected
property unless necessary (bc4844, #15477) - ngModelOptions: work correctly when on the template of
replace
directives (5f8ed6, #15492) - ngClassOdd/Even: add/remove the correct classes when expression/
$index
change simultaneously (d52864) - jqLite: silently ignore
after()
if element has no parent (3d68b9, #15331, #15475) - $rootScope: when adding/removing watchers during $digest (163aca, #15422)
๐ Performance Improvements
-
v1.6.0 Changes
December 08, 2016๐ 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, #10922) - $compile:
- jqLite:
- implement
jqLite(f)
as an alias tojqLite(document).ready(f)
(369fb7) - don't throw for elements with missing
getAttribute
(4e6c14) - don't get/set properties when getting/setting boolean attributes (7ceb5f, #14126)
- don't remove a boolean attribute for
.attr(attrName, '')
(3faf45) - remove the attribute for
.attr(attribute, null)
(4e3624) - return
[]
for.val()
on<select multiple>
with no selection (d882fd) - camelCase keys in
jqLite#data
(fc0c11, #15126) - align jqLite camelCasing logic with JQuery (73050c, #7744)
- implement
- $http:
- $anchorScroll: convert numeric hash targets to string (9062ba #14680)
- select: support values of any type added with
ngValue
(f02b70, #9842) - input:
- ngSwitch: allow multiple case matches via optional attribute
ngSwitchWhenSeparator
(0b221 #3410 #3516) - $interpolate: use custom
toString()
function if present (a5fd2e, #7317, #11406) - ngRoute:
- $q: report promises with non rejection callback (c9dffd, #13653, #7992)
- $resource: pass
status
/statusText
to success callbacks (e3a378 #8341 #8841) - $location:
- $controller: throw when requested controller is not registered (eacfe4 #14980)
๐ Security Related
- ๐ Please read the Sandbox Removal Blog Post.
- bootstrap:
- $compile:
๐ Bug Fixes
- $sce: fix
adjustMatcher
to replace multiple*
and**
(991a2b) - ngModelOptions: handle debounce of
updateOn
triggers that are not in debounce list (789790) - ngMock/$controller: respect
$compileProvider.preAssignBindingsEnabled()
(7d9a79) - $location:
- core: do not auto-bootstrap when loaded from an extension. (0ff10e)
- input[radio]: use strict comparison when evaluating checked-ness (5ac7da, #15288)
- input: fix
step
validation forinput[type=number]
/input[type=range]
(081d06, #15257) - ๐ $parse:
- ngModel: treat synchronous validators as boolean always (7bc71a, #14734)
- $q: treat thrown errors as regular rejections (e13eea, #3174, #15213)
- ngTransclude: use fallback content if only whitespace is provided (32aa7e, #15077)
- $compile:
- don't throw tplrt error when there is a whitespace around a top-level comment (76d3da, #15108)
- clean up
@
-binding observers when re-assigning bindings (586e2a #15268) - set attribute value even if
ngAttr*
contains no interpolation (3fe3da #15133) bindToController
should work withoutcontrollerAs
(16dcce #15088)- do not overwrite values set in
$onInit()
for<
-bound literals (a1bdff #15118) - avoid calling
$onChanges()
twice forNaN
initial values (7d7efb) - disallow linking the same element more than once (1e1fbc)
- correctly merge consecutive text nodes on IE11 (13c252, #14924)
- don't add leading white-space in attributes for a specific merge case (305ba1)
- don't trim white-space in attributes (97bbf8, #5513, #5597)
- move check for interpolation of on-event attributes to compile time (b89c21, #13267)
- select, ngOptions, ngValue:
- don't add comment nodes as empty options (245b27, #15454)
- do not throw when removing the element (e.g. via
ngIf
) (7a667c) - add/remove selected attribute for selected/unselected options (c75698)
- don't register options when select has no ngModel (e8c2e1)
- handle model updates when options are manipulated (47c15f)
- remove workaround for a Chrome bug (87eff2)
- make the handling of unknown / empty options consistent (2785ad)
- set the element's value property in addition to the value attribute (e6afca, #14031)
- $resource:
- $http:
- ngMock: trigger digest in
$httpBackend.verifyNoOutstandingRequest()
(267ee9, #13506) - ngAria:
- ngBind: use same string representation as
$interpolate
(fa80a6) - ngMock/$httpBackend: fail if a url is provided but is
undefined
(7551b8, #8442, #10934) - $route: don't process route change controllers and templates for
redirectTo
routes (7f4b35, #3332) - loader:
module.decorator
order of operations is now irrelevant (6a2ebd, #12382) - $sanitize: reduce stack height in IE <= 11 (45129c #14928)
- ngAnimate: make svg elements work with
classNameFilter
(81bf7e)
๐ Performance Improvements
- all: don't trigger digests after enter/leave of structural directives (f4fb6e, #15322)
- form, ngModel: change controllers to use prototype methods (9e24e7)
- select: don't prepend unknown option if already prepended (ba36bd)
- ngOptions: avoid calls to
element.value
(3b7f29) - $animate: listen for document visibility changes (d71dc2)
- injector: cache the results of the native class detection check (5ceb5d)
- $compile:
- ๐ $parse:
๐ฅ Breaking Changes
- 0๏ธโฃ feat($compile): set preAssignBindingsEnabled to false by default (bcd0d4):
0๏ธโฃ Previously,
$compileProvider.preAssignBindingsEnabled
was set to true by default. This means bindings were pre-assigned on component/directive controller instances (which made them available inside the constructors). In AngularJS 1.5+ the place to put the initialization logic relying on bindings being present is the controller's$onInit
method.To migrate follow the example below:
Before:
angular.module('myApp', []) .component('myComponent', { bindings: {value: '<'}, controller: function() { this.doubleValue = this.value * 2; } });
After:
angular.module('myApp', []) .component('myComponent', { bindings: {value: '<'}, controller: function() { this.$onInit = function() { this.doubleValue = this.value * 2; }; } });
If you don't have time to migrate the code at the moment, you can flip the setting back to true:
angular.module('myApp', []) .config(function($compileProvider) { $compileProvider.preAssignBindingsEnabled(true); }) .component('myComponent', { bindings: {value: '<'}, controller: function() { this.doubleValue = this.value * 2; } });
Don't do this if you're writing a library, though, as you shouldn't change ๐ง global configuration then.
- fix(input[radio]): use strict comparison when evaluating checked-ness (5ac7da):
When using input[radio], the checked status is now determined by doing a strict comparison between the value of the input and the ngModel.$viewValue. Previously, this was a non-strict comparison (==).
This means in the following examples the radio is no longer checked:
<!-- this.selected = 0 --> <input type="radio" ng-model="$ctrl.selected" value="0" > <!-- this.selected = 0; this.value = false; --> <input type="radio" ng-model="$ctrl.selected" ng-value="$ctrl.value" >
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):
The programmatic API for
ngModelOptions
has changed. You must now read options via thengModelController.$options.getOption(name)
method, rather than accessing the option directly as a property of thengModelContoller.$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:
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
andaB
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:
/* 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:
/* 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: 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:
// All five versions used to be equivalent. <div ng-style={background_color: 'blue'}></div> <div ng-style={'background:color': 'blue'}></div> <div ng-style={'background-color': 'blue'}></div> <div ng-style={'background--color': 'blue'}></div> <div ng-style={backgroundColor: 'blue'}></div>
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:
// Previous five versions are no longer equivalent but these two still are. <div ng-style={'background-color': 'blue'}></div> <div ng-style={backgroundColor: 'blue'}></div>
JS:
// Previous five versions are no longer equivalent but these two still are. elem.css('background-color', 'blue'); elem.css('backgroundColor', 'blue'); // Previous five versions are no longer equivalent but these two still are. var bgColor = elem.css('background-color'); var bgColor = elem.css('backgroundColor');
- 7ceb5f: 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:
input[checked="checked"] { ... }
JS:
elem1.attr('checked', 'checked'); elem2.attr('checked', false);
After:
CSS:
input:checked { ... }
JS:
elem1.prop('checked', true); elem2.prop('checked', false);
- 3faf45:
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:
elem.attr(booleanAttrName, '');
After:
elem.attr(booleanAttrName, false);
or:
elem.attr(booleanAttrName, null);
- 4e3624:
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:
elem.attr(attributeName, null);
After:
elem.attr(attributeName, "null");
- d882fd:
return [] for .val() on
<select multiple>
with no selection
For the jqLite element representing a select element in the multiple variant with no options chosen the .val() getter used to return null and now returns an empty array.
To migrate the code follow the example below:
Before:
HTML:
<select multiple> <option>value 1</option> <option>value 2</option> </select>
JavaScript:
var value = $element.val(); if (value) { /* do something */ }
After:
HTML:
<select multiple> <option>value 1</option> <option>value 2</option> </select>
JavaScript:
var value = $element.val(); if (value.length > 0) { /* do something */ }
ngModel
due to:- 7bc71a: 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 valueundefined
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 literalfalse
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 thanundefined
๐ would have been seen as passing andundefined
would have been seen as pending.- 9e24e7: 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
$scope.$watch('something', myNgModelCtrl.$render)
will no longer work because the
$render
method is passed without any context. This must now be replaced with$scope.$watch('something', function() { myNgModelCtrl.$render(); })
or possibly by using
Function.prototype.bind
orangular.bind
.aria/ngModel
due to:- 975a61:
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 theirNgModelController
that checks forvalue === false
. Unless 0๏ธโฃ overwritten, the default$isEmpty()
method will be used, which treatsundefined
,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 therequired
validator (e.g. "empty" + "required" --> "invalid").Before:
var template = '<my-checkbox role="checkbox" ng-model="value"></my-checkbox>'; 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:
var template = '<my-checkbox role="checkbox" ng-model="value"></my-checkbox>'; 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
-- 0๏ธโฃ If you want to have a custom
$isEmpty()
method, you need to overwrite the default. For example:.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:
remove deprecated callback methods:
success()/error()
๐
$http
's deprecated custom callback methods -success()
anderror()
- have been removed. You can use the standardthen()
/catch()
promise methods instead, but note that the method signatures and return values are different.success(fn)
can be replaced withthen(fn)
, anderror(fn)
can be replaced with eitherthen(null, fn)
orcatch(fn)
.Before:
$http(...). success(function onSuccess(data, status, headers, config) { // Handle success ... }). error(function onError(data, status, headers, config) { // Handle error ... });
After:
$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)
, theonError()
callback will only handle errors/rejections produced by the$http()
call. If theonSuccess()
callback produces an error/rejection, it won't be handled byonError()
and might go unnoticed. In contrast, when using$http(...).then(onSuccess).catch(onError)
,onError()
will handle errors/rejections produced by both$http()
andonSuccess()
.- fb6634:
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 thejsonpCallbackParam
property of the config object, or app-wide via 0๏ธโฃ the$http.defaults.jsonpCallbackParam
property, which is"callback"
by default.Before this change:
$http.json('trusted/url?callback=JSON_CALLBACK'); $http.json('other/trusted/url', {params: {cb:'JSON_CALLBACK'}});
After this change:
$http.json('trusted/url'); $http.json('other/trusted/url', {jsonpCallbackParam:'cb'});
- 6476af: 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:
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:var promise = $http.jsonp($sce.trustAsResourceUrl(url));
- 4f6f2b:
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: treat thrown errors as regular rejections
Previously, throwing an error from a promise's
onFulfilled
oronRejection
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
'stransformRequest/Response
functions or a route'sredirectTo
function as well as functions specified in a route'sresolve
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: 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: 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.
0๏ธโฃ Previously this would not fallback to default content:
<some-component> </some-component>
Now the whitespace between the opening and closing tags is treated as empty. To force the previous behaviour simply add a comment:
<some-component><!-- --> </some-component>
$compile
due to:- 13c252: correctly merge consecutive text nodes on IE11
Note: Everything described below affects IE11 only.
๐ Previously, consecutive text nodes would not get merged if they had no parent. They will now, which might have unexpected side effects in the following cases:
Passing an array or jqLite/jQuery collection of parent-less text nodes to
$compile
directly:// 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]; }));
0๏ธโฃ 2. Using multi-slot transclusion with non-consecutive, default-content text nodes (that form interpolated expressions when merged):
// Assuming the following component: .component('someThing', { template: '<ng-transclude><!-- Default content goes here --></ng-transclude>' transclude: { ignored: 'veryImportantContent' } })
<!-- And assuming the following view: --> <some-thing> {{ <very-important-content>Nooot</very-important-content> 'foo'}} </some-thing> <!-- Before: --> <some-thing> <ng-transclude> {{ <-- Two separate 'foo'}} <-- text nodes </ng-transclude> </some-thing> <!-- After: --> <some-thing> <ng-transclude> foo <-- The text nodes were merged into `{{'foo'}}`, which was then interpolated </ng-transclude> </some-thing> <!-- To (visually) get the old behavior, wrap top-level text nodes on --> <!-- multi-slot transclusion directives into `<span>` elements; e.g.: --> <some-thing> <span>{{</span> <very-important-content>Nooot</very-important-content> <span>'foo'}}</span> </some-thing> <!-- Result: --> <some-thing> <ng-transclude> <span>{{</span> <-- Two separate <span>'foo'}}</span> <-- nodes </ng-transclude> </some-thing>
- b89c21:
move check for interpolation of
on-"event"
attributes to compile time
Using interpolation in any on* event attributes (e.g.
<button onclick="{{myVar}}">
) will now throw the "nodomevents" error at compile time. Previously the nodomevents was thrown at link time. The new behavior makes it consistent with the "selmulti" error. The breaking change should be rare, as it relates to incorrect API use that should not make it to production apps in the first place.- 04cad4:
secure
link[href]
as aRESOURCE_URL
in$sce
link[href]
attributes are now protected via$sce
, which prevents interpolated โ values that fail theRESOURCE_URL
context tests from being used in interpolation.๐ For example if the application is running at
https://docs.angularjs.org
then the following will fail:<link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet">
0๏ธโฃ By default,
RESOURCE_URL
safe URLs are only allowed from the same domain and protocol as the application document.To use URLs from other domains and/or protocols, you may either whitelist them or wrap it into a trusted value by calling
$sce.trustAsResourceUrl(url)
.- 97bbf8: don't trim white-space in attributes
White-space in attributes is no longer trimmed automatically. This includes leading and trailing white-space, and attributes that are purely white-space.
To migrate, attributes that require trimming must now be trimmed manually.
A common cases where stray white-space can cause problems is when attribute values are compared, for example in an $observer:
Before:
$attrs.$observe('myAttr', function(newVal) { if (newVal === 'false') ... });
To migrate, the attribute value should be trimmed manually:
$attrs.$observe('myAttr', function(newVal) { if (newVal.trim() === 'false') ... });
๐ Note that
$parse
trims expressions automatically, so attributes with expressions (e.g. directive bindings) are unlikely to be affected by stray white-space.ngRoute
due to:- c13c66:
allow
ngView
to be included in an asynchronously loaded template
In cases where
ngView
was loaded asynchronously,$route
(and its dependencies; e.g.$location
) might also have been instantiated asynchronously. After this change,$route
(and its dependencies) 0๏ธโฃ will - by default - be instantiated early on.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 0๏ธโฃ 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 unexpected requests in tests), by using
$routeProvider.eagerInstantiationEnabled(false)
.- 7f4b35:
don't process route change controllers and templates for
redirectTo
routes
The $route service no longer instantiates controllers nor calls resolves or template functions for routes that have a
redirectTo
unless theredirectTo
is a function that returnsundefined
.- e98656:
implement
resolveRedirectTo
Previously, if
redirectTo
was a function that threw an Error, execution was aborted without firing a$routeChangeError
event. Now, if aredirectTo
function throws an Error, a$routeChangeError
event will be fired.ngMock
due to:- 267ee9:
trigger digest in
$httpBackend.verifyNoOutstandingRequest()
Calling
$httpBackend.verifyNoOutstandingRequest()
will trigger a digest. This will ensure that requests fired asynchronously will also be detected (without the need to manually trigger a digest). โ This is not expected to affect the majority of test-suites. Most of the time, a digest is (directly or indirectly) triggered anyway, before callingverifyNoOutstandingRequest()
. โ In the unlikely case that a test needs to verify the timing of a request with respect to the digest ๐คก cycle, you should rely on other means, such as mocking and/or spying.- 7551b8:
fail if a url is provided but is
undefined
It is no longer valid to explicitly pass
undefined
as theurl
argument to any of the$httpBackend.when...()
and$httpBackend.expect...()
methods.While this argument is optional, it must have a defined value if it is provided.
Previously passing an explicit
undefined
value was ignored but this โ lead to invalid tests passing unexpectedly.ngAria
due to:- ad41ba:
bind to
keydown
instead ofkeypress
inngClick
If you were explicitly setting the value of the
bindKeypress
flag, you need to change your code to ๐ usebindKeydown
instead.Before:
$ariaProvider.config({bindKeypress: xyz})
After:$ariaProvider.config({bindKeydown: xyz})
Note: If the element already has any of the
ngKeydown
/ngKeyup
/ngKeypress
directives,ngAria
will not bind to thekeydown
event, since it assumes that the developer has already taken care of keyboard interaction for that element.Although it is not expected to affect many applications, it might be desirable to keep the previous behavior of binding to the
keypress
event instead of thekeydown
. In that case, you need to manually use thengKeypress
directive (in addition tongClick
).Before:
<div ng-click="onClick()"> I respond to `click` and `keypress` (not `keydown`) </div>
After:
<div ng-click="onClick()" ng-keypress="onClick()"> I respond to `click` and `keypress` (not `keydown`) </div> <!-- OR --> <div ng-click="onClick()"> I respond to `click` and `keydown` (not `keypress`) </div>
โ Finally, it is possible that this change affects your unit or end-to-end tests. If you are currently expecting your custom buttons to automatically respond to the
keypress
event (due tongAria
), โ you need to change the tests to triggerkeydown
events instead.- 9978de: don't add roles to native control elements
ngAria will no longer add the "role" attribute to native control elements (textarea, button, select, summary, details, a, and input). Previously, "role" was not added to input, but all others in the list.
0๏ธโฃ This should not affect accessibility, because native inputs are accessible by default, but it might affect applications that relied on the "role" attribute being present (e.g. for styling or as directive attributes).
$resource
due to:- acb545: pass all extra, owned properties as params
All owned properties of the
params
object that are not used to replace URL params, will be passed to$http
asconfig.params
(to be used as query parameters in the URL), even ifObject.prototype
has a property with the same name. E.g.:Before:
var Foo = $resource('/foo/:id'); Foo.get({id: 42, bar: 'baz', toString: 'hmm'}); // URL: /foo/42?bar=baz // Note that `toString` is _not_ included in the query, // because `Object.prototype.toString` is defined :(
After:
var Foo = $resource('/foo/:id'); Foo.get({id: 42, bar: 'baz', toString: 'hmm'}); // URL: /foo/42?bar=baz&toString=hmm // Note that `toString` _is_ included in the query, as expected :)
- 2456ab: add semicolon to whitelist of delimiters to unencode in URL params
Although it shouldn't matter in practice (since both the encoded and the unencoded
;
character would โ be interpreted identically by the server), this change could break some tests: For example, where$httpBackend
was set up to expect an encoded;
character, but the request is made to the URL with an unencoded;
character.select
due to:- f02b70:
support values of any type added with
ngValue
<option>
elements added to<select ng-model>
viangValue
now add their values in hash form, i.e.<option ng-value="myString">
becomes<option ng-value="myString" value="string:myString">
.๐ This is done to support binding options with values of any type to selects.
This should rarely affect applications, as the values of options are usually not relevant to the โ application logic, but it's possible that option values are checked in tests.
- e8c2e1:
don't register options when select has no
ngModel
Option elements will no longer set their value attribute from their text value when their select element has no ngModel associated. Setting the value is only needed for the select directive to match model values and options. If no ngModel is present, the select directive doesn't need it.
This should not affect many applications as the behavior was undocumented and not part of a public API. It also has no effect on the usual HTML5 behavior that sets the select value to the option text if the option does not provide a value attribute.
ngBind
due to:- fa80a6: use same string representation as $interpolate
ngBind
now uses the same logic as $interpolate (i.e. {{myString}}) when binding, which means values other than strings are now transformed as following:- null / undefined become empty string
- with an object's custom toString() function, except if the object is a Date, Array, or Number
- otherwise with JSON.stringify
Previously, ngBind would always use toString().
The following examples show the different output:
$scope.myPlainObject = {a: 1, b: 2}; $scope.myCustomObject = {a: 1, b: 2, toString: function() {return 'a+b';}};
Plain Object:
<!-- Before: --> <span ng-bind="myPlainObject">[object Object]</span> <!-- After: --> <span ng-bind="myPlainObject">{'a':1,'b':2}</span>
Object with custom toString():
<!-- Before: --> <span ng-bind="myCustomObject">[object Object]</span> <!-- After: --> <span ng-bind="myCustomObject">a+b</span>
If you want the output of
toString()
, you can use it directly on the value in ngBind:<span ng-bind="myObject.toString()">[object Object]</span>
$interpolate
due to:- a5fd2e:
use custom
toString()
function if present
When converting values to strings, interpolation now uses a custom toString() function on objects that are not Number, Array or Date (custom means that the
toString
function is not the same asObject.prototype.toString
). Otherwise, interpolation uses JSON.stringify() as usual.Should you have a custom toString() function but still want the output of JSON.stringify(), migrate as shown in the following examples:
Before:
<span>{{myObject}}</span>
After - use the
json
filter to stringify the object:<span>{{myObject | json}}</span>
loader
due to:- 6a2ebd: module.decorator order of operations is now irrelevant
module.decorator
declarations are now processed as part of themodule.config
queue and may result in providers being decorated in a different order ifmodule.config
blocks are also used to decorate providers via$provide.decorator
.For example, consider the following declaration order in which 'theFactory' is decorated by both a
module.decorator
and a$provide.decorator
:angular .module('theApp', []) .factory('theFactory', theFactoryFn) .config(function($provide) { $provide.decorator('theFactory', provideDecoratorFn); }) .decorator('theFactory', moduleDecoratorFn);
Prior to this fix, 'theFactory' provider would be decorated in the following order:
- moduleDecoratorFn
- provideDecoratorFn
The result of this fix changes the order in which 'theFactory' is decorated because now
module.decorator
declarations are processed in the same order asmodule.config
declarations:- provideDecoratorFn
- moduleDecoratorFn
$location
due to:- aa077e: default hashPrefix to '!'
The hash-prefix for
$location
hash-bang URLs has changed from the empty string "" to the bang "!". If your application does not use HTML5 mode ๐ป or is being run on browsers that do not support HTML5 mode, and you have not specified your own hash-prefix then client side URLs will now contain a "!" prefix. For example, rather thanmydomain.com/#/a/b/c
will becomemydomain.com/#!/a/b/c
.๐ง If you actually wanted to have no hash-prefix then you should configure ๐ง this by adding a configuration block to you application:
appModule.config(['$locationProvider', function($locationProvider) { $locationProvider.hashPrefix(''); }]);
input[type=range]
due to:- 913016:
add support for binding to
input[type=range]
Due to the way that
input[type=range]
elements behave this feature modifies the behavior of such elements when bound tongModel
:- Like
input[type=number]
, it requires the model to be a Number, and will set the model to a Number - ๐ it supports setting the min/max values only via the min/max attributes
- ๐ป it follows the browser behavior of never allowing an invalid value. That means, when the browser
converts an invalid value (empty:
null
,undefined
,false
..., out of bounds: greater than max, less than min) to a valid value, the input will in turn set the model to this new valid value via$setViewValue
.- this means a range input will never be required and never have a non-Number model value, once the ngModel directive is initialized.
- this behavior is supported when the model changes and when the min/max attributes change in a way that prompts the browser to update the input value.
- ๐ป browsers that do not support
input[type=range]
(IE9) handle the input like a number input (with validation etc.)
input[type=number]
due to:- e1da4be:
add support for
step
toinput[type=number]
Number inputs that use
ngModel
and specify astep
constraint (viastep
/ngStep
attributes) will now have a new validator (step
), which will verify that the current value is valid under thestep
constraint (according to the spec). Previously, thestep
constraint was ignored byngModel
, treating values as valid even when there was a step-mismatch.โช If you want to restore the previous behavior (use the
step
attribute while disabling step validation), you can overwrite the built-instep
validator with a custom directive. For example:// For all `input` elements... .directive('input', function() { return { restrict: 'E', require: '?ngModel', link: function (scope, elem, attrs, ngModelCtrl) { // ...that are of type "number" and have `ngModel`... if ((attrs.type === 'number') && ngModelCtrl) { // ...remove the `step` validator. delete ngModelCtrl.$validators.step; } } }; })
- ngModelOptions: allow options to be inherited from ancestor
-
v1.6.0-rc.2 Changes
November 24, 2016 -
v1.6.0-rc.1 Changes
November 21, 2016๐ New Features
- ngModelOptions: allow options to be inherited from ancestor
ngModelOptions
(296cfc #10922) - $compile: set
preAssignBindingsEnabled
to false by default (bcd0d4 #15352)
๐ Bug Fixes
- ngModelOptions: handle debounce of
updateOn
triggers that are not in debounce list (789790) - ngMock/$controller: respect
$compileProvider.preAssignBindingsEnabled()
(7d9a79) - $location: throw if the path starts with double (back)slashes (4aa953)
- core: do not auto-bootstrap when loaded from an extension. (0ff10e)
- input[radio]: use strict comparison when evaluating checked-ness (5ac7da #15288)
โช Reverts
- ngModelOptions: allow options to be inherited from ancestor ngModelOptions (fb0225)
๐ Performance Improvements
- ngOptions: avoid calls to
element.value
(3b7f29)
๐ฅ Breaking Changes
- 0๏ธโฃ feat($compile): set preAssignBindingsEnabled to false by default (bcd0d4):
Previously,
$compileProvider.preAssignBindingsEnabled
was 0๏ธโฃ set to true by default. This means bindings were pre-assigned in component constructors. In AngularJS 1.5+ the place to put the initialization logic relying on bindings being present is the controller$onInit
method.To migrate follow the example below:
Before:
angular.module('myApp', []) .component('myComponent', { bindings: {value: '<'}, controller: function() { this.doubleValue = this.value * 2; } });
After:
angular.module('myApp', []) .component('myComponent', { bindings: {value: '<'}, controller: function() { this.$onInit = function() { this.doubleValue = this.value * 2; }; } });
If you don't have time to migrate the code at the moment, you can flip the setting back to true:
angular.module('myApp', []) .config(function($compileProvider) { $compileProvider.preAssignBindingsEnabled(true); }) .component('myComponent', { bindings: {value: '<'}, controller: function() { this.doubleValue = this.value * 2; } });
Don't do this if you're writing a library, though, as you shouldn't change ๐ง global configuration then.
- ๐ fix(input[radio]): use strict comparison when evaluating checked-ness (5ac7da):
When using input[radio], the checked status is now determined by doing a strict comparison between the value of the input and the ngModel.$viewValue. Previously, this was a non-strict comparison (==).
This means in the following examples the radio is no longer checked:
<!-- this.selected = 0 --> <input type="radio" ng-model="$ctrl.selected" value="0" > <!-- this.selected = 0; this.value = false; --> <input type="radio" ng-model="$ctrl.selected" ng-value="$ctrl.value" >
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):
The programmatic API for
ngModelOptions
has changed. You must now read options via thengModelController.$options.getOption(name)
method, rather than accessing the option directly as a property of thengModelContoller.$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');
- ngModelOptions: allow options to be inherited from ancestor
-
v1.6.0-rc.0 Changes
October 26, 2016Major notes
๐ Please read the Sandbox Removal Blog Post.
๐ Bug Fixes
- input: fix
step
validation forinput[type=number]
/input[type=range]
(081d06 #15257) - jqLite:
- ๐ $parse:
- ngModel: treat synchronous validators as boolean always (7bc71a #14734)
- $q: treat thrown errors as regular rejections (e13eea #3174 #15213)
- ngTransclude: use fallback content if only whitespace is provided (32aa7e #15077)
- $location: prevent infinite digest with IDN URLs in Edge (705afc #15217)
- $compile:
- don't throw tplrt error when there is a whitespace around a top-level comment (76d3da #15108)
- disallow linking the same element more than once (1e1fbc)
- lower the $sce context for src on video, audio, and track. (ad9a99)
- correctly merge consecutive text nodes on IE11 (13c252 #14924)
- secure
link[href]
as aRESOURCE_URL
s in$sce
. (04cad4 #14687) - don't add leading white-space in attributes for a specific merge case (305ba1)
- don't trim white-space in attributes (97bbf8 #5513 #5597)
- move check for interpolation of on-event attributes to compile time (b89c21 #13267)
- clean up
@
-binding observers when re-assigning bindings (586e2a #15268) - set attribute value even if
ngAttr*
contains no interpolation (3fe3da #15133) bindToController
should work withoutcontrollerAs
(16dcce #15088)- do not overwrite values set in
$onInit()
for<
-bound literals (a1bdff #15118) - avoid calling
$onChanges()
twice forNaN
initial values (7d7efb)
- select:
- select, ngOptions: make the handling of unknown / empty options consistent (2785ad)
- ngValue: set the element's value property in addition to the value attribute (e6afca #14031)
- aria/ngModel: do not overwrite the default
$isEmpty()
method for checkboxes (975a61 #14621) - $resource:
- $http:
- ngMock: trigger digest in
$httpBackend.verifyNoOutstandingRequest()
(267ee9 #13506) - ngAria:
- ngBind: use same string representation as
$interpolate
(fa80a6) - ngMock/$httpBackend: fail if a url is provided but is
undefined
(7551b8 #8442 #10934) - $route: don't process route change controllers and templates for
redirectTo
routes (7f4b35 #3332) - loader:
module.decorator
order of operations is now irrelevant (6a2ebd #12382) - $sanitize: reduce stack height in IE <= 11 (45129c #14928)
- ngAnimate: make svg elements work with
classNameFilter
(81bf7e)
๐ New Features
- jqLite:
- implement
jqLite(f)
as an alias tojqLite(document).ready(f)
(369fb7) - don't throw for elements with missing
getAttribute
(4e6c14) - don't get/set properties when getting/setting boolean attributes (7ceb5f, #14126)
- don't remove a boolean attribute for
.attr(attrName, '')
(3faf45) - remove the attribute for
.attr(attribute, null)
(4e3624) - return
[]
for.val()
on<select multiple>
with no selection (d882fd)
- implement
- $compile:
- $http:
- $anchorScroll: convert numeric hash targets to string (9062ba #14680)
- ngModelOptions: allow options to be inherited from ancestor
ngModelOptions
(87a2ff #10922) - input:
- ngSwitch: allow multiple case matches via optional attribute
ngSwitchWhenSeparator
(0b221 #3410 #3516) - ngRoute: allow
ngView
to be included in an asynchronously loaded template (c13c66 #1213) - select: support values of any type added with
ngValue
(f02b70 #9842) - $interpolate: use custom
toString()
function if present (a5fd2e #7317 #11406) - $route: implement
resolveRedirectTo
(e98656 #5150) - $q: report promises with non rejection callback (c9dffd #13653 #7992)
- $resource: pass
status
/statusText
to success callbacks (e3a378 #8341 #8841) - $location:
- $controller: throw when requested controller is not registered (eacfe4 #14980)
๐ Performance Improvements
- form, ngModel: change controllers to use prototype methods (9e24e7)
- select: don't prepend unknown option if already prepended (ba36bd)
- $animate: listen for document visibility changes (d71dc2)
- injector: cache the results of the native class detection check (5ceb5d)
- ๐ $parse: Inline constants (bd7d5f)
- $compile:
- ๐ $parse: remove Angular expression sandbox (1547c7 #15094)
๐ฅ Breaking Changes
jqLite due to:
- fc0c11: 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
andaB
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:
/* 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:
/* 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: 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:
// All five versions used to be equivalent. <div ng-style={background_color: 'blue'}></div> <div ng-style={'background:color': 'blue'}></div> <div ng-style={'background-color': 'blue'}></div> <div ng-style={'background--color': 'blue'}></div> <div ng-style={backgroundColor: 'blue'}></div>
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:
// Previous five versions are no longer equivalent but these two still are. <div ng-style={'background-color': 'blue'}></div> <div ng-style={backgroundColor: 'blue'}></div>
JS:
// Previous five versions are no longer equivalent but these two still are. elem.css('background-color', 'blue'); elem.css('backgroundColor', 'blue'); // Previous five versions are no longer equivalent but these two still are. var bgColor = elem.css('background-color'); var bgColor = elem.css('backgroundColor');
- 7ceb5f: 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:
input[checked="checked"] { ... }
JS:
elem1.attr('checked', 'checked'); elem2.attr('checked', false);
After:
CSS:
input:checked { ... }
JS:
elem1.prop('checked', true); elem2.prop('checked', false);
- 3faf45: 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:
elem.attr(booleanAttrName, '');
After:
elem.attr(booleanAttrName, false);
or:
elem.attr(booleanAttrName, null);
- 4e3624: 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:
elem.attr(attributeName, null);
After:
elem.attr(attributeName, "null");
- d882fd: return [] for .val() on
<select multiple>
with no selection
For the jqLite element representing a select element in the multiple variant with no options chosen the .val() getter used to return null and now returns an empty array.
To migrate the code follow the example below:
Before:
HTML:
<select multiple> <option>value 1</option> <option>value 2</option> </select>
JavaScript:
var value = $element.val(); if (value) { /* do something */ }
After:
HTML:
<select multiple> <option>value 1</option> <option>value 2</option> </select>
JavaScript:
var value = $element.val(); if (value.length > 0) { /* do something */ }
ngModel
due to:- 7bc71a: 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 valueundefined
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 literalfalse
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 thanundefined
๐ would have been seen as passing andundefined
would have been seen as pending.- 9e24e7: 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
$scope.$watch('something', myNgModelCtrl.$render)
will no longer work because the
$render
method is passed without any context. This must now be replaced with$scope.$watch('something', function() { myNgModelCtrl.$render(); })
or possibly by using
Function.prototype.bind
orangular.bind
.aria/ngModel
due to:- 975a61: 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 theirNgModelController
that checks forvalue === false
. Unless 0๏ธโฃ overwritten, the default$isEmpty()
method will be used, which treatsundefined
,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 therequired
validator (e.g. "empty" + "required" --> "invalid").Before:
var template = '<my-checkbox role="checkbox" ng-model="value"></my-checkbox>'; 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:
var template = '<my-checkbox role="checkbox" ng-model="value"></my-checkbox>'; 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
-- 0๏ธโฃ If you want to have a custom
$isEmpty()
method, you need to overwrite the default. For example:.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: remove deprecated callback methods:
success()/error()
๐
$http
's deprecated custom callback methods -success()
anderror()
- have been removed. You can use the standardthen()
/catch()
promise methods instead, but note that the method signatures and return values are different.success(fn)
can be replaced withthen(fn)
, anderror(fn)
can be replaced with eitherthen(null, fn)
orcatch(fn)
.Before:
$http(...). success(function onSuccess(data, status, headers, config) { // Handle success ... }). error(function onError(data, status, headers, config) { // Handle error ... });
After:
$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)
, theonError()
callback will only handle errors/rejections produced by the$http()
call. If theonSuccess()
callback produces an error/rejection, it won't be handled byonError()
and might go unnoticed. In contrast, when using$http(...).then(onSuccess).catch(onError)
,onError()
will handle errors/rejections produced by both$http()
andonSuccess()
.- fb6634: 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 thejsonpCallbackParam
property of the config object, or app-wide via 0๏ธโฃ the$http.defaults.jsonpCallbackParam
property, which is"callback"
by default.Before this change:
$http.json('trusted/url?callback=JSON_CALLBACK'); $http.json('other/trusted/url', {params: {cb:'JSON_CALLBACK'}});
After this change:
$http.json('trusted/url'); $http.json('other/trusted/url', {jsonpCallbackParam:'cb'});
- 6476af: 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:
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:var promise = $http.jsonp($sce.trustAsResourceUrl(url));
- 4f6f2b: 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: treat thrown errors as regular rejections
Previously, throwing an error from a promise's
onFulfilled
oronRejection
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
'stransformRequest/Response
functions or a route'sredirectTo
function as well as functions specified in a route'sresolve
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: 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: 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.
ngModelOptions
due to:- 87a2ff: allow options to be inherited from ancestor
ngModelOptions
0๏ธโฃ Previously, if a setting was not applied on
ngModelOptions
, then it would default to undefined. Now the setting will be inherited from the nearest ngModelOptions ancestor.It is possible that an
ngModelOptions
directive that does not set a property, has an ancestor ngModelOptions that does set this property to a value other thanundefined
. This would cause thengModel
and input controls below thisngModelOptions
๐ directive to display different behaviour. This is fixed by explicitly setting the property in thengModelOptions
to prevent it from inheriting from the ancestor.For example if you had the following HTML:
<form ng-model-options="{updateOn: 'blur'}"> <input ng-model="..." ng-model-options="{allowInvalid: true}"> </form>
โก๏ธ Then before this change the input would update on the default event not blur. โก๏ธ After this change the input will inherit the option to update on blur. If you want the original behaviour then you will need to specify the option on the input as well:
<form ng-model-options="{updateOn: 'blur'}"> <input ng-model="..." ng-model-options="{updateOn: 'default', allowInvalid: true}"> </form>
The programmatic API for
ngModelOptions
has changed. You must now read options via thegetOption
method, rather than accessing the option directly as a property of the 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.$compile
due to:- 13c252: correctly merge consecutive text nodes on IE11
Note: Everything described below affects IE11 only.
๐ Previously, consecutive text nodes would not get merged if they had no parent. They will now, which might have unexpected side effects in the following cases:
Passing an array or jqLite/jQuery collection of parent-less text nodes to
$compile
directly:// 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]; }));
0๏ธโฃ 2. Using multi-slot transclusion with non-consecutive, default-content text nodes (that form interpolated expressions when merged):
// Assuming the following component: .component('someThing', { template: '<ng-transclude><!-- Default content goes here --></ng-transclude>' transclude: { ignored: 'veryImportantContent' } })
<!-- And assuming the following view: --> <some-thing> {{ <very-important-content>Nooot</very-important-content> 'foo'}} </some-thing> <!-- Before: --> <some-thing> <ng-transclude> {{ <-- Two separate 'foo'}} <-- text nodes </ng-transclude> </some-thing> <!-- After: --> <some-thing> <ng-transclude> foo <-- The text nodes were merged into `{{'foo'}}`, which was then interpolated </ng-transclude> </some-thing> <!-- To (visually) get the old behavior, wrap top-level text nodes on --> <!-- multi-slot transclusion directives into `<span>` elements; e.g.: --> <some-thing> <span>{{</span> <very-important-content>Nooot</very-important-content> <span>'foo'}}</span> </some-thing> <!-- Result: --> <some-thing> <ng-transclude> <span>{{</span> <-- Two separate <span>'foo'}}</span> <-- nodes </ng-transclude> </some-thing>
- b89c21: move check for interpolation of on-event attributes to compile time
Using interpolation in any on* event attributes (e.g.
<button onclick="{{myVar}}">
) will now throw the "nodomevents" error at compile time. Previously the nodomevents was thrown at link time. The new behavior makes it consistent with the "selmulti" error. The breaking change should be rare, as it relates to incorrect API use that should not make it to production apps in the first place.- 04cad4: secure
link[href]
as aRESOURCE_URL
in$sce
link[href]
attributes are now protected via$sce
, which prevents interpolated โ values that fail theRESOURCE_URL
context tests from being used in interpolation.๐ For example if the application is running at
https://docs.angularjs.org
then the following will fail:<link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet">
0๏ธโฃ By default,
RESOURCE_URL
safe URLs are only allowed from the same domain and protocol as the application document.To use URLs from other domains and/or protocols, you may either whitelist them or wrap it into a trusted value by calling
$sce.trustAsResourceUrl(url)
.- 97bbf8: don't trim white-space in attributes
White-space in attributes is no longer trimmed automatically. This includes leading and trailing white-space, and attributes that are purely white-space.
To migrate, attributes that require trimming must now be trimmed manually.
A common cases where stray white-space can cause problems is when attribute values are compared, for example in an $observer:
Before:
$attrs.$observe('myAttr', function(newVal) { if (newVal === 'false') ... });
To migrate, the attribute value should be trimmed manually:
$attrs.$observe('myAttr', function(newVal) { if (newVal.trim() === 'false') ... });
๐ Note that
$parse
trims expressions automatically, so attributes with expressions (e.g. directive bindings) are unlikely to be affected by stray white-space.ngRoute
due to:- c13c66: allow
ngView
to be included in an asynchronously loaded template
In cases where
ngView
was loaded asynchronously,$route
(and its dependencies; e.g.$location
) might also have been instantiated asynchronously. After this change,$route
(and its dependencies) 0๏ธโฃ will - by default - be instantiated early on.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 0๏ธโฃ 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 unexpected requests in tests), by using
$routeProvider.eagerInstantiationEnabled(false)
.- 7f4b35: don't process route change controllers and templates for
redirectTo
routes
The $route service no longer instantiates controllers nor calls resolves or template functions for routes that have a
redirectTo
unless theredirectTo
is a function that returnsundefined
.- e98656: implement
resolveRedirectTo
Previously, if
redirectTo
was a function that threw an Error, execution was aborted without firing a$routeChangeError
event. Now, if aredirectTo
function throws an Error, a$routeChangeError
event will be fired.ngMock
due to:- 267ee9: trigger digest in
$httpBackend.verifyNoOutstandingRequest()
Calling
$httpBackend.verifyNoOutstandingRequest()
will trigger a digest. This will ensure that requests fired asynchronously will also be detected (without the need to manually trigger a digest). โ This is not expected to affect the majority of test-suites. Most of the time, a digest is (directly or indirectly) triggered anyway, before callingverifyNoOutstandingRequest()
. โ In the unlikely case that a test needs to verify the timing of a request with respect to the digest ๐คก cycle, you should rely on other means, such as mocking and/or spying.- 7551b8: fail if a url is provided but is
undefined
It is no longer valid to explicitly pass
undefined
as theurl
argument to any of the$httpBackend.when...()
and$httpBackend.expect...()
methods.While this argument is optional, it must have a defined value if it is provided.
Previously passing an explicit
undefined
value was ignored but this โ lead to invalid tests passing unexpectedly.ngAria
due to:- ad41ba: bind to
keydown
instead ofkeypress
inngClick
If you were explicitly setting the value of the
bindKeypress
flag, you need to change your code to ๐ usebindKeydown
instead.Before:
$ariaProvider.config({bindKeypress: xyz})
After:$ariaProvider.config({bindKeydown: xyz})
Note: If the element already has any of the
ngKeydown
/ngKeyup
/ngKeypress
directives,ngAria
will not bind to thekeydown
event, since it assumes that the developer has already taken care of keyboard interaction for that element.Although it is not expected to affect many applications, it might be desirable to keep the previous behavior of binding to the
keypress
event instead of thekeydown
. In that case, you need to manually use thengKeypress
directive (in addition tongClick
).Before:
<div ng-click="onClick()"> I respond to `click` and `keypress` (not `keydown`) </div>
After:
<div ng-click="onClick()" ng-keypress="onClick()"> I respond to `click` and `keypress` (not `keydown`) </div> <!-- OR --> <div ng-click="onClick()"> I respond to `click` and `keydown` (not `keypress`) </div>
โ Finally, it is possible that this change affects your unit or end-to-end tests. If you are currently expecting your custom buttons to automatically respond to the
keypress
event (due tongAria
), โ you need to change the tests to triggerkeydown
events instead.- 9978de: don't add roles to native control elements
ngAria will no longer add the "role" attribute to native control elements (textarea, button, select, summary, details, a, and input). Previously, "role" was not added to input, but all others in the list.
0๏ธโฃ This should not affect accessibility, because native inputs are accessible by default, but it might affect applications that relied on the "role" attribute being present (e.g. for styling or as directive attributes).
$resource
due to:- acb545: pass all extra, owned properties as params
All owned properties of the
params
object that are not used to replace URL params, will be passed to$http
asconfig.params
(to be used as query parameters in the URL), even ifObject.prototype
has a property with the same name. E.g.:Before:
var Foo = $resource('/foo/:id'); Foo.get({id: 42, bar: 'baz', toString: 'hmm'}); // URL: /foo/42?bar=baz // Note that `toString` is _not_ included in the query, // because `Object.prototype.toString` is defined :(
After:
var Foo = $resource('/foo/:id'); Foo.get({id: 42, bar: 'baz', toString: 'hmm'}); // URL: /foo/42?bar=baz&toString=hmm // Note that `toString` _is_ included in the query, as expected :)
- 2456ab: add semicolon to whitelist of delimiters to unencode in URL params
Although it shouldn't matter in practice (since both the encoded and the unencoded
;
character would โ be interpreted identically by the server), this change could break some tests: For example, where$httpBackend
was set up to expect an encoded;
character, but the request is made to the URL with an unencoded;
character.select
due to:- f02b70: support values of any type added with ngValue
<option>
elements added to<select ng-model>
viangValue
now add their values in hash form, i.e.<option ng-value="myString">
becomes<option ng-value="myString" value="string:myString">
.๐ This is done to support binding options with values of any type to selects.
This should rarely affect applications, as the values of options are usually not relevant to the โ application logic, but it's possible that option values are checked in tests.
- e8c2e1: don't register options when select has no ngModel
Option elements will no longer set their value attribute from their text value when their select element has no ngModel associated. Setting the value is only needed for the select directive to match model values and options. If no ngModel is present, the select directive doesn't need it.
This should not affect many applications as the behavior was undocumented and not part of a public API. It also has no effect on the usual HTML5 behavior that sets the select value to the option text if the option does not provide a value attribute.
ngBind
due to:- fa80a6: use same string representation as $interpolate
ngBind
now uses the same logic as $interpolate (i.e. {{myString}}) when binding, which means values other than strings are now transformed as following:- null / undefined become empty string
- with an object's custom toString() function, except if the object is a Date, Array, or Number
- otherwise with JSON.stringify
Previously, ngBind would always use toString().
The following examples show the different output:
$scope.myPlainObject = {a: 1, b: 2}; $scope.myCustomObject = {a: 1, b: 2, toString: function() {return 'a+b';}};
Plain Object:
<!-- Before: --> <span ng-bind="myPlainObject">[object Object]</span> <!-- After: --> <span ng-bind="myPlainObject">{'a':1,'b':2}</span>
Object with custom toString():
<!-- Before: --> <span ng-bind="myCustomObject">[object Object]</span> <!-- After: --> <span ng-bind="myCustomObject">a+b</span>
If you want the output of
toString()
, you can use it directly on the value in ngBind:<span ng-bind="myObject.toString()">[object Object]</span>
$interpolate
due to:- a5fd2e: use custom toString() function if present
When converting values to strings, interpolation now uses a custom toString() function on objects that are not Number, Array or Date (custom means that the
toString
function is not the same asObject.prototype.toString
). Otherwise, interpolation uses JSON.stringify() as usual.Should you have a custom toString() function but still want the output of JSON.stringify(), migrate as shown in the following examples:
Before:
<span>{{myObject}}</span>
After - use the
json
filter to stringify the object:<span>{{myObject | json}}</span>
loader
due to:- 6a2ebd: module.decorator order of operations is now irrelevant
module.decorator
declarations are now processed as part of themodule.config
queue and may result in providers being decorated in a different order ifmodule.config
blocks are also used to decorate providers via$provide.decorator
.For example, consider the following declaration order in which 'theFactory' is decorated by both a
module.decorator
and a$provide.decorator
:angular .module('theApp', []) .factory('theFactory', theFactoryFn) .config(function($provide) { $provide.decorator('theFactory', provideDecoratorFn); }) .decorator('theFactory', moduleDecoratorFn);
Prior to this fix, 'theFactory' provider would be decorated in the following order:
- moduleDecoratorFn
- provideDecoratorFn
The result of this fix changes the order in which 'theFactory' is decorated because now
module.decorator
declarations are processed in the same order asmodule.config
declarations:- provideDecoratorFn
- moduleDecoratorFn
$location
due to:- aa077e: default hashPrefix to '!'
The hash-prefix for
$location
hash-bang URLs has changed from the empty string "" to the bang "!". If your application does not use HTML5 mode ๐ป or is being run on browsers that do not support HTML5 mode, and you have not specified your own hash-prefix then client side URLs will now contain a "!" prefix. For example, rather thanmydomain.com/#/a/b/c
will becomemydomain.com/#!/a/b/c
.๐ง If you actually wanted to have no hash-prefix then you should configure ๐ง this by adding a configuration block to you application:
appModule.config(['$locationProvider', function($locationProvider) { $locationProvider.hashPrefix(''); }]);
input[type=range]
due to:- 913016: add support for binding to
input[type=range]
Due to the way that
input[type=range]
elements behave this feature modifies the behavior of such elements when bound tongModel
:- Like
input[type=number]
, it requires the model to be a Number, and will set the model to a Number - ๐ it supports setting the min/max values only via the min/max attributes
- ๐ป it follows the browser behavior of never allowing an invalid value. That means, when the browser
converts an invalid value (empty:
null
,undefined
,false
..., out of bounds: greater than max, less than min) to a valid value, the input will in turn set the model to this new valid value via$setViewValue
.- this means a range input will never be required and never have a non-Number model value, once the ngModel directive is initialized.
- this behavior is supported when the model changes and when the min/max attributes change in a way that prompts the browser to update the input value.
- ๐ป browsers that do not support
input[type=range]
(IE9) handle the input like a number input (with validation etc.)
input[type=number]
due to:- e1da4be: add support for
step
toinput[type=number]
Number inputs that use
ngModel
and specify astep
constraint (viastep
/ngStep
attributes) will now have a new validator (step
), which will verify that the current value is valid under thestep
constraint (according to the spec). Previously, thestep
constraint was ignored byngModel
, treating values as valid even when there was a step-mismatch.โช If you want to restore the previous behavior (use the
step
attribute while disabling step validation), you can overwrite the built-instep
validator with a custom directive. For example:// For all `input` elements... .directive('input', function() { return { restrict: 'E', require: '?ngModel', link: function (scope, elem, attrs, ngModelCtrl) { // ...that are of type "number" and have `ngModel`... if ((attrs.type === 'number') && ngModelCtrl) { // ...remove the `step` validator. delete ngModelCtrl.$validators.step; } } }; })
- input: fix
-
v1.5.11 Changes
January 13, 2017๐ Bug Fixes
- $compile: allow the usage of "$" in isolate scope property alias (e75fbc, #15586, #15594)
- angularInit: allow auto-bootstrapping from inline script (41aa91, #15567, #15571)
- $resource: delete
$cancelRequest()
intoJSON()
(4f3858, #15244) - $$cookieReader: correctly handle forbidden access to
document.cookie
(6933cf, #15523, #15532)
-
v1.5.10 Changes
December 15, 2016๐ Bug Fixes
- $compile:
- don't throw tplrt error when there is whitespace around a top-level comment (12752f, #15108)
- clean up
@
-binding observers when re-assigning bindings (f3cb6e, #15268) - set attribute value even if
ngAttr*
contains no interpolation (229799, #15133) bindToController
should work withoutcontrollerAs
(944989, #15088)- do not overwrite values set in
$onInit()
for<
-bound literals (07e1ba, #15118) - avoid calling
$onChanges()
twice forNaN
initial values (0cf5be)
- $location: prevent infinite digest with IDN urls in Edge (4bf892, #15217)
- $rootScope: correctly handle adding/removing watchers during
$digest
(a9708d, #15422) - $sce: fix
adjustMatcher
to replace multiple*
and**
(78eecb) - jqLite: silently ignore
after()
if element has no parent (77ed85, #15331) - input[radio]: use non-strict comparison for checkedness (593a50)
- select, ngOptions:
- ngClassOdd/Even: add/remove the correct classes when expression/
$index
change simultaneously (e3d020) - $sanitize: reduce stack height in IE <= 11 (862dc2, #14928)
- ngMock/$controller: respect
$compileProvider.preAssignBindingsEnabled()
(75c83f)
๐ New Features
- bootstrap: do not bootstrap from unknown schemes with a different origin (bdeb33, #15428)
- $anchorScroll: convert numeric hash targets to string (a52640, #14680)
- $compile:
- $controller: throw when requested controller is not registered (9ae793, #14980)
- $location: add support for selectively rewriting links based on attribute (a4a222)
- $resource: pass
status
/statusText
to success callbacks (a8da25, #8341, #8841) - ngSwitch: allow multiple case matches via optional attribute
ngSwitchWhenSeparator
(0e1651, #3410, #3516)
๐ Performance Improvements
- $compile: