71d19f120a
In commits 9679e58ec4e9d9e4b743..3dd42cea688a7b6f7789, some properties and methods names including the terms whitelist/blacklist were deprecated in favor of new ones not including the terms. This commit fixes some typos in docs related to these changes and adds links to the new properties/methods in the changelog for easier access. Fixes #17088
4045 lines
151 KiB
Plaintext
4045 lines
151 KiB
Plaintext
@ngdoc overview
|
||
@name Migrating from Previous Versions
|
||
@sortOrder 550
|
||
@description
|
||
|
||
# Migrating an App to a newer version
|
||
|
||
Minor version releases in AngularJS introduce several breaking changes that may require changes to your
|
||
application's source code; for instance from 1.0 to 1.2 and from 1.2 to 1.3.
|
||
|
||
Although we try to avoid breaking changes, there are some cases where it is unavoidable:
|
||
|
||
* AngularJS has undergone thorough security reviews to make applications safer by default,
|
||
which drives many of these changes.
|
||
* Several new features, especially animations, would not be possible without a few changes.
|
||
* Finally, some outstanding bugs were best fixed by changing an existing API.
|
||
|
||
## Migrating from 1.7 to 1.8
|
||
|
||
Generally updating to 1.8.0 from 1.7.x should be a straightforward process and is highly recommended.
|
||
AngularJS 1.8 is a breaking change release from 1.7 to mitigate a security issue.
|
||
|
||
JqLite no longer turns XHTML-like strings like `<div /><span />` to sibling elements when not in XHTML
|
||
mode: `<div></div><span></span>`.
|
||
Instead it will leave the elements alone. In non-XHTML mode the browser will convert these to nested
|
||
elements: `<div><span></span></div>`.
|
||
|
||
This is a security fix to avoid an XSS vulnerability if a new jqLite element is created from a
|
||
user-controlled HTML string. If you must have this functionality and understand the risk involved
|
||
then it is posible to restore the original behavior by calling
|
||
|
||
```js
|
||
angular.UNSAFE_restoreLegacyJqLiteXHTMLReplacement();
|
||
```
|
||
|
||
But you should adjust your code for this change and remove your use of this function as soon as
|
||
possible.
|
||
|
||
Note that this only patches jqLite. If you use jQuery 3.5.0 or newer, please read the
|
||
[jQuery 3.5 upgrade guide](https://jquery.com/upgrade-guide/3.5/) for more details about the workarounds.
|
||
|
||
|
||
## Migrating from 1.6 to 1.7
|
||
|
||
AngularJS 1.7 contains bug fixes and features to AngularJS core and its external modules, some of
|
||
which contain breaking changes. However, most of these address internal behavior and not APIs, and
|
||
should not affect many applications.
|
||
Additionally, we have removed some long-deprecated modules and APIs.
|
||
|
||
The most notable changes are:
|
||
|
||
- `$resource` has now support for request and requestError interceptors
|
||
|
||
- Several deprecated features have been removed:
|
||
- the `$controllerProvider.allowGlobals()` flag
|
||
- the `$compileProvider.preAssignBindingsEnabled()` flag
|
||
- the `angular.lowercase` and `angular.uppercase` methods
|
||
- the `$cookieStore` service from the `ngCookies` module
|
||
- the `ngClick` override directive and corresponding services from the `ngTouch` module
|
||
- the complete `ngScenario` module
|
||
|
||
Please note that feature development (without breaking changes) has happened in parallel on the
|
||
1.6.x branch, so 1.7 doesn't contain many new features, but you may still benefit from those
|
||
features that were added (with possible BCs), bugfixes, and a few smaller performance improvements.
|
||
|
||
|
||
<br />
|
||
Below is the full list of breaking changes:
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.6to1.7-ng-directives"></a>
|
||
### Core: _Directives_
|
||
|
||
|
||
#### **form**
|
||
|
||
**Due to [223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**,
|
||
forms will now set `$submitted` on child forms when they are submitted.
|
||
For example:
|
||
```
|
||
<form name="parentform" ng-submit="$ctrl.submit()">
|
||
<ng-form name="childform">
|
||
<input type="text" name="input" ng-model="my.model" />
|
||
</ng-form>
|
||
<input type="submit" />
|
||
</form>
|
||
```
|
||
|
||
Submitting this form will set `$submitted` on "parentform" and "childform".
|
||
Previously, it was only set on "parentform".
|
||
|
||
This change was introduced because mixing `form` and `ngForm` does not create
|
||
logically separate forms, but rather something like input groups.
|
||
Therefore, child forms should inherit the submission state from their parent form.
|
||
|
||
|
||
#### **input[radio]** and **input[checkbox]**
|
||
|
||
**Due to [656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**,
|
||
`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event.
|
||
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
|
||
happens.
|
||
|
||
Two scenarios might need migration:
|
||
|
||
- Custom click events:
|
||
|
||
Before this change, custom click event listeners on radio / checkbox would be called after the
|
||
input element and `ngModel` had been updated, unless they were specifically registered before
|
||
the built-in click handlers.
|
||
After this change, they are called before the input is updated, and can call
|
||
`event.preventDefault()` to prevent the input from updating.
|
||
|
||
If an app uses a click event listener that expects `ngModel` to be updated when it is called, it now
|
||
needs to register a change event listener instead.
|
||
|
||
- Triggering click events:
|
||
|
||
Conventional trigger functions:
|
||
|
||
The change event might not be fired when the input element is not attached to the document. This
|
||
can happen in **tests** that compile input elements and trigger click events on them. Depending on
|
||
the browser (Chrome and Safari) and the trigger method, the change event will not be fired when the
|
||
input isn't attached to the document.
|
||
|
||
Before:
|
||
|
||
```js
|
||
it('should update the model', inject(function($compile, $rootScope) {
|
||
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
|
||
|
||
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
|
||
expect($rootScope.checkbox).toBe(true);
|
||
});
|
||
```
|
||
|
||
With this patch, `$rootScope.checkbox` might not be true, because the click event hasn't triggered
|
||
the change event. To make the test, work append `inputElm` to the app's `$rootElement`, and the
|
||
`$rootElement` to the `$document`.
|
||
|
||
After:
|
||
|
||
```js
|
||
it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
|
||
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
|
||
|
||
$rootElement.append(inputElm);
|
||
$document.append($rootElement);
|
||
|
||
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
|
||
expect($rootScope.checkbox).toBe(true);
|
||
});
|
||
```
|
||
|
||
|
||
#### **input\[number\]**
|
||
|
||
**Due to [aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**,
|
||
`input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against
|
||
the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`.
|
||
|
||
This affects apps that use `$parsers` or `$formatters` to transform the input / model value.
|
||
|
||
If you rely on the `$modelValue` validation, you can overwrite the `min`/`max` validator from a
|
||
custom directive, as seen in the following example directive definition object:
|
||
|
||
```js
|
||
{
|
||
restrict: 'A',
|
||
require: 'ngModel',
|
||
link: function(scope, element, attrs, ctrl) {
|
||
var maxValidator = ctrl.$validators.max;
|
||
|
||
ctrl.$validators.max = function(modelValue, viewValue) {
|
||
return maxValidator(modelValue, modelValue);
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
#### **ngModel, input**
|
||
|
||
**Due to [74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**,
|
||
*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month",
|
||
"time", "datetime-local", "week", no longer set `ngModelController.$error[inputType]`, and
|
||
the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" no longer set `ngModelController.$error.number` and the `ng-invalid-number` class.
|
||
|
||
Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and
|
||
`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers
|
||
and custom parsers easier.
|
||
|
||
|
||
#### **ngModelOptions**
|
||
|
||
**Due to [55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**,
|
||
the 'default' key in 'debounce' now only debounces the default event, i.e. the event that is added
|
||
as an update trigger by the different input directives automatically.
|
||
|
||
Previously, it also applied to other update triggers defined in 'updateOn' that
|
||
did not have a corresponding key in the 'debounce'.
|
||
|
||
This behavior is now supported via a special wildcard / catch-all key: '*'.
|
||
|
||
See the following example:
|
||
|
||
Pre-1.7:
|
||
'mouseup' is also debounced by 500 milliseconds because 'default' is applied:
|
||
```html
|
||
ng-model-options="{
|
||
updateOn: 'default blur mouseup',
|
||
debounce: { 'default': 500, 'blur': 0 }
|
||
}"
|
||
```
|
||
|
||
1.7:
|
||
The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value:
|
||
```html
|
||
ng-model-options="{
|
||
updateOn: 'default blur mouseup',
|
||
debounce: { '*': 500, 'blur': 0 }
|
||
}"
|
||
```
|
||
|
||
In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced:
|
||
```html
|
||
ng-model-options="{
|
||
updateOn: 'default blur mouseup',
|
||
debounce: { 'default': 500 }
|
||
}
|
||
```
|
||
|
||
|
||
#### **ngStyle**
|
||
|
||
**Due to [15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**,
|
||
the use of deep-watching in `ngStyle` has changed. Previously, `ngStyle` would trigger styles to be
|
||
re-applied whenever nested state changed. Now, only changes to direct properties of the watched
|
||
object will trigger changes.
|
||
|
||
|
||
<a name="migrate1.6to1.7-ng-services"></a>
|
||
### Core: _Services_
|
||
|
||
|
||
#### **$compile**
|
||
|
||
**Due to [38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**,
|
||
directive bindings are no longer available in the constructor.
|
||
|
||
Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported.
|
||
The flag controlled whether bindings were available inside the controller
|
||
constructor or only in the `$onInit` hook. The bindings are now no longer
|
||
available in the constructor.
|
||
|
||
To migrate your code:
|
||
|
||
1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you
|
||
don't have to do anything to migrate.
|
||
|
||
2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you
|
||
can remove that statement - since AngularJS 1.6.0 this is the default so your
|
||
app should still work even in AngularJS 1.6 after such removal. Afterwards,
|
||
migrating to AngularJS 1.7.0 shouldn't require any further action.
|
||
|
||
3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need
|
||
to first migrate your code so that the flag can be flipped to `false`. The
|
||
instructions on how to do that are available in the "Migrating from 1.5 to 1.6"
|
||
guide: https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6
|
||
Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)`
|
||
statement.
|
||
|
||
<hr />
|
||
|
||
**Due to [6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**,
|
||
the `xlink:href` security context for SVG's `a` and `image` elements has been lowered.
|
||
|
||
In the unlikely case that an app relied on `RESOURCE_URL` trusted list for the
|
||
purpose of binding to the `xlink:href` property of SVG's `<a>` or `<image>`
|
||
elements and if the values do not pass the regular URL sanitization, they will
|
||
break.
|
||
|
||
To fix this you need to ensure that the values used for binding to the affected
|
||
`xlink:href` contexts are considered safe URLs, e.g. by trusting them in
|
||
`$compileProvider`'s `aHrefSanitizationWhitelist` (called `aHrefSanitizationTrustedUrlList` form
|
||
1.8.1 onwards) (for `<a>` elements) or `imgSrcSanitizationWhitelist` (called
|
||
`imgSrcSanitizationTrustedUrlList` from 1.8.1 onwards) (for `<image>` elements).
|
||
|
||
<hr />
|
||
|
||
**Due to [fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**,
|
||
deep-watching is no longer used in literal one-way bindings.
|
||
|
||
Previously, when a literal value was passed into a directive/component via
|
||
one-way binding it would be watched with a deep watcher.
|
||
|
||
For example, for `<my-component input="[a]">`, a new instance of the array
|
||
would be passed into the directive/component (and trigger `$onChanges`) not
|
||
only if `a` changed but also if any sub property of `a` changed such as
|
||
`a.b` or `a.b.c.d.e` etc.
|
||
|
||
This also means a new but equal value for `a` would NOT trigger such a
|
||
change.
|
||
|
||
Now, literal values use an input-based watch similar to other directive/component
|
||
one-way bindings. In this context inputs are the non-constant parts of the
|
||
literal. In the example above, the input would be `a`. Changes are only
|
||
triggered, when the inputs to the literal change.
|
||
|
||
<hr />
|
||
|
||
**Due to [1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**,
|
||
`base[href]` was added to the list of `RESOURCE_URL` context attributes.
|
||
|
||
Previously, `<base href="{{ $ctrl.baseUrl }}" />` would not require `baseUrl` to
|
||
be trusted as a `RESOURCE_URL`. Now, `baseUrl` will be sent to `$sce`'s
|
||
`RESOURCE_URL` checks. By default, it will break unless `baseUrl` is of the same
|
||
origin as the application document.
|
||
|
||
Refer to the
|
||
[`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce)
|
||
for more info on how to trust a value in a `RESOURCE_URL` context.
|
||
|
||
Also, concatenation in trusted contexts is not allowed, which means that the
|
||
following won't work: `<base href="/something/{{ $ctrl.partialPath }}" />`.
|
||
|
||
Either construct complex values in a controller (recommended):
|
||
|
||
```js
|
||
this.baseUrl = '/something/' + this.partialPath;
|
||
```
|
||
```html
|
||
<base href="{{ $ctrl.baseUrl }}" />
|
||
```
|
||
|
||
Or use string concatenation in the interpolation expression (not recommended
|
||
except for the simplest of cases):
|
||
|
||
```html
|
||
<base href="{{ '/something/' + $ctrl.partialPath }}" />
|
||
```
|
||
|
||
|
||
#### **$rootScope**
|
||
|
||
**Due to ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570))**,
|
||
the arguments of `$watchGroup` callbacks have changed.
|
||
|
||
Previously, when using `$watchGroup`, the entries in `newValues` and
|
||
`oldValues` represented the *most recent change of each entry*.
|
||
|
||
Now, the entries in `oldValues` will always equal the `newValues` of the previous
|
||
call of the listener. This means comparing the entries in `newValues` and
|
||
`oldValues` can be used to determine which individual expressions changed.
|
||
|
||
For example `$scope.$watchGroup(['a', 'b'], fn)` would previously:
|
||
|
||
| Action | newValue | oldValue |
|
||
|----------|------------|------------|
|
||
| (init) | [undefined, undefined] | [undefined, undefined] |
|
||
| `a=1` | [1, undefined] | [undefined, undefined] |
|
||
| `a=2` | [2, undefined] | [1, undefined] |
|
||
| `b=3` | [2, 3] | [1, undefined] |
|
||
|
||
|
||
Now the `oldValue` will always equal the previous `newValue`:
|
||
|
||
| Action | newValue | oldValue |
|
||
|----------|------------|------------|
|
||
| (init) | [undefined, undefined] | [undefined, undefined] |
|
||
| `a=1` | [1, undefined] | [undefined, undefined] |
|
||
| `a=2` | [2, undefined] | [1, undefined] |
|
||
| `b=3` | [2, 3] | [2, undefined] |
|
||
|
||
Note the last call now shows `a === 2` in the `oldValues` array.
|
||
|
||
This also makes the `oldValue` of one-time watchers more clear. Previously,
|
||
the `oldValue` of a one-time watcher would remain `undefined` forever. For
|
||
example `$scope.$watchGroup(['a', '::b'], fn)` would previously:
|
||
|
||
| Action | newValue | oldValue |
|
||
|----------|------------|------------|
|
||
| (init) | [undefined, undefined] | [undefined, undefined] |
|
||
| `a=1` | [1, undefined] | [undefined, undefined] |
|
||
| `b=2` | [1, 2] | [undefined, undefined] |
|
||
| `a=b=3` | [3, 2] | [1, undefined] |
|
||
|
||
Where now the `oldValue` will always equal the previous `newValue`:
|
||
|
||
| Action | newValue | oldValue |
|
||
|----------|------------|------------|
|
||
| (init) | [undefined, undefined] | [undefined, undefined] |
|
||
| `a=1` | [1, undefined] | [undefined, undefined] |
|
||
| `b=2` | [1, 2] | [1, undefined] |
|
||
| `a=b=3` | [3, 2] | [1, 2] |
|
||
|
||
|
||
#### **$interval**
|
||
|
||
**Due to [a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**,
|
||
`$interval.cancel()` will throw an error if called with a promise that was not generated by
|
||
`$interval()`. Previously, it would silently do nothing.
|
||
|
||
Before:
|
||
```js
|
||
var promise = $interval(doSomething, 1000, 5).then(doSomethingElse);
|
||
$interval.cancel(promise); // No error; interval NOT canceled.
|
||
```
|
||
|
||
After:
|
||
```js
|
||
var promise = $interval(doSomething, 1000, 5).then(doSomethingElse);
|
||
$interval.cancel(promise); // Throws error.
|
||
```
|
||
|
||
Correct usage:
|
||
```js
|
||
var promise = $interval(doSomething, 1000, 5);
|
||
var newPromise = promise.then(doSomethingElse);
|
||
$interval.cancel(promise); // Interval canceled.
|
||
```
|
||
|
||
|
||
#### **$timeout**
|
||
|
||
**Due to [336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**,
|
||
`$timeout.cancel()` will throw an error if called with a promise that was not generated by
|
||
`$timeout()`. Previously, it would silently do nothing.
|
||
|
||
Before:
|
||
```js
|
||
var promise = $timeout(doSomething, 1000).then(doSomethingElse);
|
||
$timeout.cancel(promise); // No error; timeout NOT canceled.
|
||
```
|
||
|
||
After:
|
||
```js
|
||
var promise = $timeout(doSomething, 1000).then(doSomethingElse);
|
||
$timeout.cancel(promise); // Throws error.
|
||
```
|
||
|
||
Correct usage:
|
||
```js
|
||
var promise = $timeout(doSomething, 1000);
|
||
var newPromise = promise.then(doSomethingElse);
|
||
$timeout.cancel(promise); // Timeout canceled.
|
||
```
|
||
|
||
|
||
#### **$cookies**
|
||
|
||
**Due to [73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**,
|
||
the `$cookieStore`service has been removed. Migrate to the `$cookies` service. Note that
|
||
for object values you need to use the `putObject` & `getObject` methods, as
|
||
`get`/`put` will not correctly save/retrieve the object values.
|
||
|
||
Before:
|
||
```js
|
||
$cookieStore.put('name', {key: 'value'});
|
||
$cookieStore.get('name'); // {key: 'value'}
|
||
$cookieStore.remove('name');
|
||
```
|
||
|
||
|
||
#### **$templateRequest**
|
||
|
||
**Due to [c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**,
|
||
the `tpload` error namespace has changed. Previously, the `tpload` error was namespaced to
|
||
`$compile`. If you have code that matches errors of the form `[$compile:tpload]` it will no longer
|
||
run. You should change the code to match `[$templateRequest:tpload]`.
|
||
|
||
<hr />
|
||
|
||
**Due to ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**,
|
||
`$templateRequest()` now returns the result of `$templateCache.put()` when making a server request
|
||
for a template. Previously, it would return the content of the response directly.
|
||
|
||
This means that if you are decorating `$templateCache.put()` to manipulate the template, you will
|
||
now get this manipulated result also on the first `$templateRequest()` call rather than only on
|
||
subsequent calls (when the template is retrieved from the cache).
|
||
|
||
In practice, this should not affect any apps, as it is unlikely that they rely on the template being
|
||
different in the first and subsequent calls.
|
||
|
||
|
||
#### **$animate**
|
||
|
||
**Due to [16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**,
|
||
`$animate.cancel(runner)` now rejects the underlying promise and calls the `catch()` handler on the
|
||
runner returned by `$animate` functions (`enter`, `leave`, `move`, `addClass`, `removeClass`,
|
||
`setClass`, `animate`).
|
||
Previously, it would resolve the promise as if the animation had ended successfully.
|
||
|
||
Example:
|
||
|
||
```js
|
||
var runner = $animate.addClass('red');
|
||
runner.then(function() { console.log('success')});
|
||
runner.catch(function() { console.log('cancelled')});
|
||
|
||
runner.cancel();
|
||
```
|
||
|
||
Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'.
|
||
To migrate, add a `catch()` handler to your animation runners.
|
||
|
||
|
||
#### **$controller**
|
||
|
||
**Due to [e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**,
|
||
the option to instantiate controllers from constructors on the global `window` object
|
||
has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()`
|
||
method that could enable this behavior, has been removed.
|
||
|
||
This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope
|
||
is considered bad practice. To migrate, remove the call to `$controllerProvider.allowGlobals()` in
|
||
the config, and register your controller via the Module API or the `$controllerProvider`, e.g.:
|
||
|
||
```js
|
||
angular.module('myModule', []).controller('myController', function() {...});
|
||
|
||
// or
|
||
|
||
angular.module('myModule', []).config(function($controllerProvider) {
|
||
$controllerProvider.register('myController', function() {...});
|
||
});
|
||
```
|
||
|
||
|
||
#### **$sce**
|
||
|
||
**Due to [1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**,
|
||
if you use `attrs.$set` for URL attributes (`a[href]` and `img[src]`) there will no
|
||
longer be any automated sanitization of the value. This is in line with other
|
||
programmatic operations, such as writing to the `innerHTML` of an element.
|
||
|
||
If you are programmatically writing URL values to attributes from untrusted
|
||
input, then you must sanitize it yourself. You could write your own sanitizer or copy
|
||
the private `$$sanitizeUri` service.
|
||
|
||
Note that values that have been passed through the `$interpolate` service within the
|
||
`URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize
|
||
these values again.
|
||
|
||
<hr/>
|
||
|
||
**Due to [1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**,
|
||
binding {@link ng.$sce#trustAs trustAs()} and the short versions
|
||
({@link ng.$sce#trustAsResourceUrl trustAsResourceUrl()} et al.) to {@link ng.ngSrc},
|
||
{@link ng.ngSrcset}, and {@link ng.ngHref} will now raise an infinite digest error:
|
||
|
||
```js
|
||
$scope.imgThumbFn = function(id) {
|
||
return $sce.trustAsResourceUrl(someService.someUrl(id));
|
||
};
|
||
```
|
||
|
||
```html
|
||
<img ng-src="{{ imgThumbFn(imgId) }}" />
|
||
```
|
||
|
||
This is because {@link ng.$interpolate} is now responsible for sanitizing
|
||
the attribute value, and its watcher receives a new object from `trustAs()`
|
||
on every digest.
|
||
To migrate, compute the trusted value only when the input value changes:
|
||
|
||
```js
|
||
$scope.$watch('imgId', function(id) {
|
||
$scope.imgThumb = $sce.trustAsResourceUrl(someService.someUrl(id));
|
||
});
|
||
```
|
||
|
||
```html
|
||
<img ng-src="{{ imgThumb }}" />
|
||
```
|
||
|
||
|
||
<a name="migrate1.6to1.7-ng-filters"></a>
|
||
### Core: _Filters_
|
||
|
||
|
||
#### **orderBy**
|
||
|
||
**Due to [1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**,
|
||
when using `orderBy` to sort arrays containing `null` values, the `null` values
|
||
will be considered "greater than" all other values, except for `undefined`.
|
||
Previously, they were sorted as strings. This will result in different (but more
|
||
intuitive) sorting order.
|
||
|
||
Before:
|
||
```js
|
||
orderByFilter(['a', undefined, 'o', null, 'z']);
|
||
//--> 'a', null, 'o', 'z', undefined
|
||
```
|
||
|
||
After:
|
||
```js
|
||
orderByFilter(['a', undefined, 'o', null, 'z']);
|
||
//--> 'a', 'o', 'z', null, undefined
|
||
```
|
||
|
||
|
||
<a name="migrate1.6to1.7-ng-misc"></a>
|
||
### Core: _Miscellaneous_
|
||
|
||
|
||
#### **jqLite**
|
||
|
||
**Due to [b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**,
|
||
`removeData()` no longer removes event handlers.
|
||
|
||
Before this commit `removeData()` invoked on an element removed its event
|
||
handlers as well. If you want to trigger a full cleanup of an element, change:
|
||
|
||
```js
|
||
elem.removeData();
|
||
```
|
||
|
||
to:
|
||
|
||
```js
|
||
angular.element.cleanData(elem);
|
||
```
|
||
|
||
In most cases, though, cleaning up after an element is supposed to be done
|
||
only when it's removed from the DOM as well; in such cases the following:
|
||
|
||
```js
|
||
elem.remove();
|
||
```
|
||
|
||
will remove event handlers as well.
|
||
|
||
|
||
#### **Helpers**
|
||
|
||
**Due to [1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**,
|
||
the helper functions `angular.lowercase` and `angular.uppercase` have been removed.
|
||
|
||
These functions have been deprecated since 1.5.0. They are internally
|
||
used, but should not be exposed as they contain special locale handling
|
||
(for Turkish) to maintain internal consistency regardless of user-set locale.
|
||
|
||
Developers should generally use the built-in methods `toLowerCase` and `toUpperCase`
|
||
or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases.
|
||
|
||
<hr />
|
||
|
||
**Due to [e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**,
|
||
`angular.isArray()` now supports Array subclasses.
|
||
|
||
Previously, `angular.isArray()` was an alias for `Array.isArray()`.
|
||
Therefore, objects that prototypally inherit from `Array` where not
|
||
considered arrays. Now such objects are considered arrays too.
|
||
|
||
This change affects several other methods that use `angular.isArray()`
|
||
under the hood, such as `angular.copy()`, `angular.equals()`,
|
||
`angular.forEach()`, and `angular.merge()`.
|
||
|
||
This in turn affects how dirty checking treats objects that prototypally
|
||
inherit from `Array` (e.g. MobX observable arrays). AngularJS will now
|
||
be able to handle these objects better when copying or watching.
|
||
|
||
|
||
<a name="migrate1.6to1.7-ngAria"></a>
|
||
### ngAria
|
||
|
||
**Due to [6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**,
|
||
`ngAria` no longer sets `aria-*` attributes on `input[type="hidden"]` with `ngModel`.
|
||
This can affect apps that test for the presence of ARIA attributes on hidden inputs.
|
||
To migrate, remove these assertions.
|
||
In actual apps, this should not have a user-facing effect, as the previous behavior
|
||
was incorrect, and the new behavior is correct for accessibility.
|
||
|
||
|
||
<a name="migrate1.6to1.7-ngResource"></a>
|
||
### ngResource
|
||
|
||
|
||
#### **$resource**
|
||
|
||
**Due to [ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**,
|
||
the behavior of interceptors and success/error callbacks has changed.
|
||
|
||
If you are not using `success` or `error` callbacks with `$resource`,
|
||
your app should not be affected by this change.
|
||
|
||
If you are using `success` or `error` callbacks (with or without
|
||
response interceptors), one (subtle) difference is that throwing an
|
||
error inside the callbacks will not propagate to the returned
|
||
`$promise`. Therefore, you should try to use the promises whenever
|
||
possible. E.g.:
|
||
|
||
```js
|
||
// Avoid
|
||
User.query(function onSuccess(users) { throw new Error(); }).
|
||
$promise.
|
||
catch(function onError() { /* Will not be called. */ });
|
||
|
||
// Prefer
|
||
User.query().
|
||
$promise.
|
||
then(function onSuccess(users) { throw new Error(); }).
|
||
catch(function onError() { /* Will be called. */ });
|
||
```
|
||
|
||
Finally, if you are using `success` or `error` callbacks with response
|
||
interceptors, the callbacks will now always run _after_ the interceptors
|
||
(and wait for them to resolve in case they return a promise).
|
||
Previously, the `error` callback was called before the `responseError`
|
||
interceptor and the `success` callback was synchronously called after
|
||
the `response` interceptor. E.g.:
|
||
|
||
```js
|
||
var User = $resource('/api/users/:id', {id: '@id'}, {
|
||
get: {
|
||
method: 'get',
|
||
interceptor: {
|
||
response: function(response) {
|
||
console.log('responseInterceptor-1');
|
||
return $timeout(1000).then(function() {
|
||
console.log('responseInterceptor-2');
|
||
return response.resource;
|
||
});
|
||
},
|
||
responseError: function(response) {
|
||
console.log('responseErrorInterceptor-1');
|
||
return $timeout(1000).then(function() {
|
||
console.log('responseErrorInterceptor-2');
|
||
return $q.reject('Ooops!');
|
||
});
|
||
}
|
||
}
|
||
}
|
||
});
|
||
var onSuccess = function(value) { console.log('successCallback', value); };
|
||
var onError = function(error) { console.log('errorCallback', error); };
|
||
|
||
// Assuming the following call is successful...
|
||
User.get({id: 1}, onSuccess, onError);
|
||
// Old behavior:
|
||
// responseInterceptor-1
|
||
// successCallback, {/* Promise object */}
|
||
// responseInterceptor-2
|
||
// New behavior:
|
||
// responseInterceptor-1
|
||
// responseInterceptor-2
|
||
// successCallback, {/* User object */}
|
||
|
||
// Assuming the following call returns an error...
|
||
User.get({id: 2}, onSuccess, onError);
|
||
// Old behavior:
|
||
// errorCallback, {/* Response object */}
|
||
// responseErrorInterceptor-1
|
||
// responseErrorInterceptor-2
|
||
// New behavior:
|
||
// responseErrorInterceptor-1
|
||
// responseErrorInterceptor-2
|
||
// errorCallback, Ooops!
|
||
```
|
||
|
||
<hr />
|
||
|
||
**Due to [240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**,
|
||
`$http` will be called asynchronously from `$resource` methods
|
||
(regardless if a `request`/`requestError` interceptor has been defined).
|
||
|
||
Previously, calling a `$resource` method would synchronously call
|
||
`$http`.
|
||
|
||
This is not expected to affect applications at runtime, since the
|
||
overall operation is asynchronous already, but may affect assertions in
|
||
tests. For example, if you want to assert that `$http` has been called
|
||
with specific arguments as a result of a `$resource` call, you now need
|
||
to run a `$digest` first, to ensure the (possibly empty) request
|
||
interceptor promise has been resolved.
|
||
|
||
Before:
|
||
```js
|
||
it('...', function() {
|
||
$httpBackend.expectGET('/api/things').respond(...);
|
||
var Things = $resource('/api/things');
|
||
Things.query();
|
||
|
||
expect($http).toHaveBeenCalledWith(...);
|
||
});
|
||
```
|
||
|
||
After:
|
||
```js
|
||
it('...', function() {
|
||
$httpBackend.expectGET('/api/things').respond(...);
|
||
var Things = $resource('/api/things');
|
||
Things.query();
|
||
$rootScope.$digest();
|
||
|
||
expect($http).toHaveBeenCalledWith(...);
|
||
});
|
||
```
|
||
|
||
|
||
<a name="migrate1.6to1.7-ngScenario"></a>
|
||
### ngScenario
|
||
|
||
**Due to[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**,
|
||
the angular scenario runner end-to-end test framework has been
|
||
removed from the project and will no longer be available on npm
|
||
or bower starting with 1.7.0.
|
||
It has been deprecated and removed from the documentation since 2014.
|
||
Applications that still use it should migrate to
|
||
[Protractor](http://www.protractortest.org).
|
||
Technically, it should also be possible to continue using an
|
||
older version of the scenario runner, as the underlying APIs have
|
||
not changed. However, we do not guarantee future compatibility.
|
||
|
||
|
||
<a name="migrate1.6to1.7-ngTouch"></a>
|
||
### ngTouch
|
||
|
||
**Due to [11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**,
|
||
the `ngClick` directive of the `ngTouch` module has been removed, and with it the
|
||
corresponding `$touchProvider` and `$touch` service.
|
||
|
||
If you have included `ngTouch` v1.5.0 or higher in your application, and have not
|
||
changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch`
|
||
service, then there are no migration steps for your code. Otherwise you must remove references to
|
||
the provider and service.
|
||
|
||
The `ngClick` override directive had been deprecated and by default disabled since v1.5.0,
|
||
because of buggy behavior in edge cases, and a general trend to avoid special touch based
|
||
overrides of click events. In modern browsers, it should not be necessary to use a touch override
|
||
library:
|
||
|
||
- Chrome, Firefox, Edge, and Safari remove the 300ms delay when
|
||
`<meta name="viewport" content="width=device-width">` is set.
|
||
- Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the
|
||
`touch-action` css property is set to `manipulation`.
|
||
|
||
You can find out more in these articles:
|
||
https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away
|
||
https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8
|
||
https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/
|
||
|
||
|
||
|
||
## Migrating from 1.5 to 1.6
|
||
|
||
AngularJS 1.6 fixes numerous bugs and adds new features, both in core and in external modules.
|
||
In addition, it includes several security and performance improvements in commonly used services,
|
||
such as `$compile`, `$injector`, `$parse`, `$animate`, and directives, such as `input`, `ngModel`
|
||
and `select`.
|
||
|
||
|
||
The most notable changes are:
|
||
|
||
- Aligning jqLite with the latest version of jQuery (3.x).
|
||
- Implementing long awaited features, such as support for inputs of type `range` and the ability to
|
||
bind to any type of values using `ngRepeat` with `select`.
|
||
- Disabling (by default) the pre-assignment of bindings on controller instances, which helps with
|
||
support for native ES6 classes.
|
||
- Changing the default `$location` hash-prefix to `'!'`, as the previous empty string default was
|
||
unconventional and confusing.
|
||
- Reporting possibly unhandled promise rejections that would otherwise go unnoticed.
|
||
|
||
Another major change is the removal of the **Expression Sandbox**. This should not require changes
|
||
to your application (and may give it a small performance boost), but we strongly recommend reading
|
||
the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/09/angular-16-expression-sandbox-removal.html)
|
||
to understand the implications behind the removal and whether any action is required on your part.
|
||
|
||
|
||
<br />
|
||
You may also notice that this release comes with a longer-than-usual list of breaking changes. Don't
|
||
let this dishearten you though, since most of them are pretty minor - often not expected to affect
|
||
real applications. These breaking changes were necessary in order to:
|
||
|
||
- Align with breaking changes in jQuery 3.
|
||
- Fix bugs that we wouldn't be able to fix otherwise.
|
||
- Introduce new features, performance improvements and security fixes.
|
||
- Make the behavior of existing features more consistent and predictable.
|
||
|
||
|
||
<br />
|
||
To give you a heads-up, here is a brief summary of the breaking changes that are expected to have
|
||
the highest impact. Make sure you look them up in the full list below or check out the corresponding
|
||
commits for more info.
|
||
|
||
- **$location** now uses `'!'` as the default hash-prefix for hash-bang URLs, instead of the empty
|
||
string. ([Details](guide/migration#commit-aa077e8))
|
||
|
||
- **$compile** will (by default) not pre-assign bindings on component/directive controller
|
||
instances. ([Details](guide/migration#commit-bcd0d4))
|
||
|
||
- **http** imposes additional restrictions to **JSONP** requests for security reasons
|
||
(see [details](guide/migration#migrate1.5to1.6-ng-services-$http) below):
|
||
- The request URL now needs to be trusted as a resource URL.
|
||
- You can no longer use the `JSON_CALLBACK` placeholder for specifying the query parameter for the
|
||
callback.
|
||
|
||
|
||
|
||
- **jqLite** is more aligned to jQuery 3, which required the following changes
|
||
(see [details](guide/migration#migrate1.5to1.6-ng-misc-jqLite) below):
|
||
- Keys passed to `.data()` and `.css()` are now camelCased in the same way as the jQuery methods
|
||
do.
|
||
- Getting/setting boolean attributes no longer takes the corresponding properties into account.
|
||
- Setting boolean attributes to empty string no longer removes the attribute.
|
||
- Calling `.val()` on a multiple select will always return an array, even if no option is
|
||
selected.
|
||
|
||
|
||
|
||
- **input[type=radio]** now uses strict comparison (`===`) to determine its "checked" status.
|
||
([Details](guide/migration#commit-5ac7da))
|
||
|
||
- The improved support for **input[type=range]** means that the behaviour of range inputs (when
|
||
bound to `ngModel`) has changed. ([Details](guide/migration#commit-913016))
|
||
|
||
- **ngTransclude** now treats whitespace-only transclusion content as empty and uses the fallback
|
||
content instead. ([Details](guide/migration#commit-32aa7e))
|
||
|
||
- **ngAria/ngModel** no longer overrides the default `$inEmpty()` method for custom
|
||
`checkbox`-shaped controls. ([Details](guide/migration#commit-975a61))
|
||
|
||
|
||
<br />
|
||
Below is the full list of breaking changes:
|
||
|
||
- Core:
|
||
- [Directives](guide/migration#migrate1.5to1.6-ng-directives)
|
||
- [form](guide/migration#migrate1.5to1.6-ng-directives-form)
|
||
- [input[number]](guide/migration#migrate1.5to1.6-ng-directives-input[number])
|
||
- [input[radio]](guide/migration#migrate1.5to1.6-ng-directives-input[radio])
|
||
- [input[range]](guide/migration#migrate1.5to1.6-ng-directives-input[range])
|
||
- [ngBind](guide/migration#migrate1.5to1.6-ng-directives-ngBind)
|
||
- [ngModel](guide/migration#migrate1.5to1.6-ng-directives-ngModel)
|
||
- [ngModelOptions](guide/migration#migrate1.5to1.6-ng-directives-ngModelOptions)
|
||
- [ngTransclude](guide/migration#migrate1.5to1.6-ng-directives-ngTransclude)
|
||
- [select](guide/migration#migrate1.5to1.6-ng-directives-select)
|
||
- [Services](guide/migration#migrate1.5to1.6-ng-services)
|
||
- [$compile](guide/migration#migrate1.5to1.6-ng-services-$compile)
|
||
- [$http](guide/migration#migrate1.5to1.6-ng-services-$http)
|
||
- [$interpolate](guide/migration#migrate1.5to1.6-ng-services-$interpolate)
|
||
- [$location](guide/migration#migrate1.5to1.6-ng-services-$location)
|
||
- [$q](guide/migration#migrate1.5to1.6-ng-services-$q)
|
||
- [Miscellaneous](guide/migration#migrate1.5to1.6-ng-misc)
|
||
- [jqLite](guide/migration#migrate1.5to1.6-ng-misc-jqLite)
|
||
- [decorator()](guide/migration#migrate1.5to1.6-ng-misc-decorator)
|
||
- Modules:
|
||
- [ngAria](guide/migration#migrate1.5to1.6-ngAria)
|
||
- [$aria](guide/migration#migrate1.5to1.6-ngAria-$aria)
|
||
- [ngClick](guide/migration#migrate1.5to1.6-ngAria-ngClick)
|
||
- [ngModel](guide/migration#migrate1.5to1.6-ngAria-ngModel)
|
||
- [ngMock](guide/migration#migrate1.5to1.6-ngMock)
|
||
- [$httpBackend](guide/migration#migrate1.5to1.6-ngMock-$httpBackend)
|
||
- [ngResource](guide/migration#migrate1.5to1.6-ngResource)
|
||
- [$resource](guide/migration#migrate1.5to1.6-ngResource-$resource)
|
||
- [ngRoute](guide/migration#migrate1.5to1.6-ngRoute)
|
||
- [$route](guide/migration#migrate1.5to1.6-ngRoute-$route)
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ng-directives"></a>
|
||
### Core: _Directives_
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-form"></a>
|
||
#### **form**:
|
||
|
||
<minor />
|
||
**Due to [9e24e7](https://github.com/angular/angular.js/commit/9e24e774a558143b3478536911a3a4c1714564ba)**,
|
||
`FormController` now defines its methods on its prototype, instead of on each instance. As a
|
||
consequence, `FormController` methods always need to be called in the correct context. For example
|
||
`$scope.$watch('something', myFormCtrl.$setDirty)` will no longer work, because the `$setDirty`
|
||
method is passed without any context. The code must now be changed to:
|
||
|
||
```js
|
||
$scope.$watch('something', function() {
|
||
myFormCtrl.$setDirty();
|
||
})
|
||
```
|
||
|
||
or you can use `Function.prototype.bind` or `angular.bind`.
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-input[number]"></a>
|
||
#### **input[type=number]**:
|
||
|
||
<minor />
|
||
**Due to [e1da4be](https://github.com/angular/angular.js/commit/e1da4bed8e291003d485a8ad346ab80bed8ae2e3)**,
|
||
number inputs that use `ngModel` and specify a `step` constraint (via `step`/`ngStep` attributes)
|
||
will now have a new validator (`step`), which will verify that the current value is valid under the
|
||
`step` constraint (according to the [spec](https://www.w3.org/TR/html5/forms.html#the-step-attribute)).
|
||
Previously, the `step` constraint was ignored by `ngModel`, 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-in `step` validator with a custom directive. For example:
|
||
|
||
```js
|
||
// 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;
|
||
}
|
||
}
|
||
};
|
||
})
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-input[radio]"></a>
|
||
#### **input[type=radio]**:
|
||
|
||
<major />
|
||
<a name="commit-5ac7da"></a>
|
||
**Due to [5ac7da](https://github.com/angular/angular.js/commit/5ac7daea72ec31cf337d1d21b13f0d17ff33994f)**,
|
||
the "checked" status of radio inputs is now determined by doing a strict comparison (`===`) between
|
||
the value of the input and the `ngModelController.$viewValue`. Previously, this was a non-strict
|
||
comparison (`==`).
|
||
|
||
This means in the following examples the radio is no longer checked:
|
||
|
||
```html
|
||
<!-- 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" />
|
||
```
|
||
|
||
If your code relied on the non-strict comparison, you need to convert the values so that they
|
||
continue to match with strict comparison.
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-input[range]"></a>
|
||
#### **input[type=range]**:
|
||
|
||
<major />
|
||
<a name="commit-913016"></a>
|
||
**Due to [913016](https://github.com/angular/angular.js/commit/9130166767c4792c5d32d08a918fc7becf32c9a6)**
|
||
and the built-in support for range inputs, the behavior of such elements when bound to `ngModel`
|
||
will be different than before:
|
||
|
||
- 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 have the required validation error 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).
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-ngBind"></a>
|
||
#### **ngBind**:
|
||
|
||
<minor />
|
||
**Due to [fa80a6](https://github.com/angular/angular.js/commit/fa80a61a05a3b49a2c770d5544cb8480907a18d3)**,
|
||
`ngBind` now uses the same logic as `$interpolate` (i.e. `{{ myObject }}`) when binding, which means
|
||
values other than strings are now transformed as follows:
|
||
- `null`/`undefined` become the empty string.
|
||
- If an object is not Array, Number or Date and has a custom `toString()` function, use that.
|
||
- Otherwise use `JSON.stringify()`.
|
||
|
||
Previously, `ngBind` would always use `toString()`. The following examples show the difference:
|
||
|
||
```js
|
||
$scope.myPlainObject = {a: 1, b: 2};
|
||
$scope.myCustomObject = {a: 1, b: 2, toString: function() { return 'a+b'; }};
|
||
```
|
||
|
||
Plain Object:
|
||
|
||
```html
|
||
<!-- Before: -->
|
||
<span ng-bind="myPlainObject">[object Object]</span>
|
||
|
||
<!-- After: -->
|
||
<span ng-bind="myPlainObject">{'a':1,'b':2}</span>
|
||
```
|
||
|
||
Object with custom `toString()`:
|
||
|
||
```html
|
||
<!-- 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 call it manually on the value in `ngBind`:
|
||
|
||
```html
|
||
<span ng-bind="myObject.toString()">[object Object]</span>
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-ngModel"></a>
|
||
#### **ngModel**:
|
||
|
||
<minor />
|
||
**Due to [9e24e7](https://github.com/angular/angular.js/commit/9e24e774a558143b3478536911a3a4c1714564ba)**,
|
||
`NgModelController` now defines its methods on its prototype, instead of on each instance. As a
|
||
consequence, `NgModelController` methods always need to be called in the correct context. For example
|
||
`$scope.$watch('something', myNgModelCtrl.$setDirty)` will no longer work, because the `$setDirty`
|
||
method is passed without any context. The code must now be changed to:
|
||
|
||
```js
|
||
$scope.$watch('something', function() {
|
||
myNgModelCtrl.$setDirty();
|
||
})
|
||
```
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b)**,
|
||
the values returned by synchronous validators are always treated as boolean. Previously, only a
|
||
literal `false` return value would cause the validation to fail. Now, _all_ falsy values will cause
|
||
the validation to fail, as one would naturally expect.
|
||
|
||
Specifically, the values `0`, `null`, `NaN` and `''` (the empty string) used to cause the validation
|
||
to pass and they will now cause it to fail. The value `undefined` was treated similarly to a pending
|
||
asynchronous validator, causing the validation to be pending. `undefined` is now also treated as
|
||
`false`.
|
||
|
||
If your synchronous validators are always returning boolean values (which should already be the case
|
||
for most applications anyway), then this change does not affect you. If not, make sure you always
|
||
return a boolean value (`true/false`) indicating whether the input is valid or not.
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-ngModelOptions"></a>
|
||
#### **ngModelOptions**:
|
||
|
||
<minor />
|
||
**Due to [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. 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.
|
||
|
||
This does not affect the usage in templates and only affects custom directives that might have been
|
||
reading options for their own purposes. If you were programmatically accessing the options, you need
|
||
to change your code as follows:
|
||
|
||
Before:
|
||
|
||
```js
|
||
var myOption = ngModelController.$options && ngModelController.$options['my-option'];
|
||
```
|
||
|
||
After:
|
||
|
||
```js
|
||
var myOption = ngModelController.$options.getOption('my-option');
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-ngTransclude"></a>
|
||
#### **ngTransclude**:
|
||
|
||
<major />
|
||
<a name="commit-32aa7e"></a>
|
||
**Due to [32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301)**,
|
||
if you only provide whitespace as the transclusion content, it will be assumed to be empty and the
|
||
fallback content will be used instead. Previously, whitespace only transclusion would be treated as
|
||
the transclusion being "not empty", which meant that fallback content was not used in that case.
|
||
|
||
If you actually want whitespace to appear as the transcluded content, then you can force it to be
|
||
used by adding an HTML comment to the whitespace:
|
||
|
||
```html
|
||
<my-component>
|
||
<!-- Use this as transclusion content even if empty. -->
|
||
</my-component>
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-directives-select"></a>
|
||
#### **select**:
|
||
|
||
<tests-only />
|
||
**Due to [f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622)**,
|
||
using `ngValue` on `<option>` elements inside a `<select ng-model>` will automatically set values on
|
||
them in hash form (used internally by `select` to map to the corresponding model value). I.e.
|
||
`<option ng-value="myString">` will become `<option ng-value="myString" value="string:myString">`.
|
||
|
||
This is necessary in order to support binding options with values of any type to selects and should
|
||
hardly affect any applications, as the values of options are usually not relevant to the
|
||
application logic. (Although, it may affect tests that check the `value` attribute of `<option>`
|
||
elements.)
|
||
|
||
<hr />
|
||
<tests-only />
|
||
**Due to [e8c2e1](https://github.com/angular/angular.js/commit/e8c2e119758e58e18fe43932d09a8ff9f506aa9d)**,
|
||
`<option>` elements will no longer have their value attribute set from their text value when their
|
||
`<select>` element doesn't have `ngModel` associated with it. Setting the value is only needed for
|
||
the select directive to match model values and options. If `ngModel` is not present, the `select`
|
||
directive doesn't need it.
|
||
|
||
This should not affect many applications as the behavior was undocumented and not part of the 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.
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ng-services"></a>
|
||
### Core: _Services_
|
||
|
||
<a name="migrate1.5to1.6-ng-services-$compile"></a>
|
||
#### **$compile**:
|
||
|
||
<major />
|
||
<a name="commit-bcd0d4"></a>
|
||
**Due to [bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858)**,
|
||
pre-assigning bindings on component/directive controller instances is disabled by default, which
|
||
means that they will no longer be available inside the constructors. It is still possible to turn it
|
||
back on, which should help during the migration. Pre-assigning bindings has been deprecated and will
|
||
be removed in a future version, so we strongly recommend migrating your applications to not rely on
|
||
it as soon as possible.
|
||
|
||
Initialization logic that relies on bindings being present should be put in the controller's
|
||
`$onInit()` method, which is guaranteed to always be called _after_ the bindings have been assigned.
|
||
|
||
Before:
|
||
|
||
```js
|
||
.component('myComponent', {
|
||
bindings: {value: '<'},
|
||
controller: function() {
|
||
// `this.value` might or might not be initialized,
|
||
// based on whether `preAssignBindingsEnabled` is true or false.
|
||
this.doubleValue = this.value * 2;
|
||
}
|
||
})
|
||
```
|
||
|
||
After:
|
||
|
||
```js
|
||
.component('myComponent', {
|
||
bindings: {value: '<'},
|
||
controller: function() {
|
||
this.$onInit = function() {
|
||
// `this.value` will always be initialized,
|
||
// regardless of the value of `preAssignBindingsEnabled`.
|
||
this.doubleValue = this.value * 2;
|
||
};
|
||
}
|
||
})
|
||
```
|
||
|
||
If you need to, you can re-enabled this feature with the following configuration block:
|
||
|
||
```js
|
||
.config(function($compileProvider) {
|
||
$compileProvider.preAssignBindingsEnabled(true);
|
||
})
|
||
```
|
||
|
||
**Note:**
|
||
This will re-enable the feature for the whole application, so only do it if you are in control of
|
||
the whole application. If you are writing a library, you need to change your code as shown above.
|
||
Furthermore, if your library also targets versions before 1.5 (which do not support the `$onInit()`
|
||
lifecycle hook), you may need to manually call `$onInit()` from your constructor:
|
||
|
||
```js
|
||
.directive('myComponent', function() {
|
||
return {
|
||
scope: {value: '<'},
|
||
controller: function() {
|
||
// Put initialization logic inside `$onInit()`
|
||
// to make sure bindings have been initialized.
|
||
this.$onInit = function() {
|
||
this.doubleValue = this.value * 2;
|
||
};
|
||
|
||
// Prior to v1.5, we need to call `$onInit()` manually.
|
||
// (Bindings will always be pre-assigned in these versions.)
|
||
if (angular.version.major === 1 && angular.version.minor < 5) {
|
||
this.$onInit();
|
||
}
|
||
}
|
||
};
|
||
})
|
||
```
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa)**,
|
||
`link[href]` attributes are now protected via `$sce`, which prevents interpolated values that fail
|
||
the `RESOURCE_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:
|
||
|
||
```html
|
||
<link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet" />
|
||
```
|
||
|
||
By default, only URLs with the same domain and protocol as the application document are considered
|
||
safe in the `RESOURCE_URL` context. To use URLs from other domains and/or protocols, you may either
|
||
add them to the trusted source URL list or wrap them into a trusted value by calling `$sce.trustAsResourceUrl(url)`.
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [97bbf8](https://github.com/angular/angular.js/commit/97bbf86a1979d099802f0d631c17c54b87563b40)**,
|
||
whitespace in attributes is no longer trimmed automatically. This includes leading and trailing
|
||
whitespace, and attributes that are purely whitespace. To migrate, attributes that require trimming
|
||
must now be trimmed manually. A common case where stray whitespace can cause problems is when
|
||
attribute values are compared, for example in `$observe`.
|
||
|
||
Before:
|
||
|
||
```js
|
||
$attrs.$observe('myAttr', function(newVal) {
|
||
if (newVal === 'some value') ...
|
||
});
|
||
```
|
||
|
||
After:
|
||
|
||
```js
|
||
$attrs.$observe('myAttr', function(newVal) {
|
||
if (newVal.trim() === 'some value') ...
|
||
});
|
||
```
|
||
|
||
Note that `$parse` trims expressions automatically, so attributes with expressions (e.g. directive
|
||
bindings) should not be affected by this change.
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [13c252](https://github.com/angular/angular.js/commit/13c2522baf7c8f616b2efcaab4bffd54c8736591)**,
|
||
on **IE11 only**, consecutive text nodes will always get merged. Previously, they would not get
|
||
merged if they had no parent. The new behavior, which fixes an IE11 bug affecting interpolation
|
||
under certain circumstances, might in some edge-cases have unexpected side effects that you should
|
||
be aware of. Please, check the commit message for more details.
|
||
|
||
<hr />
|
||
<tests-only />
|
||
**Due to [b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4)**,
|
||
using interpolation in any `on*` event attribute (e.g. `<button onclick="{{myVar}}">`) will now
|
||
throw the `nodomevents` error at compile time. Previously, the `nodomevents` was thrown at link
|
||
time. This change is not expected to affect any applications, as it is related to incorrect API use
|
||
that should not make it to production apps in the first place.
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-services-$http"></a>
|
||
#### **$http**:
|
||
|
||
<major />
|
||
<a name="commit-fb6634"></a>
|
||
**Due to [fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb)**,
|
||
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:
|
||
|
||
```js
|
||
$http.json('trusted/url?callback=JSON_CALLBACK');
|
||
$http.json('other/trusted/url', {params: {cb: 'JSON_CALLBACK'}});
|
||
```
|
||
|
||
After:
|
||
|
||
```js
|
||
$http.json('trusted/url');
|
||
$http.json('other/trusted/url', {jsonpCallbackParam: 'cb'});
|
||
```
|
||
|
||
<hr />
|
||
<minor />
|
||
<a name="commit-6476af"></a>
|
||
**Due to [6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4)**,
|
||
all JSONP requests now require the URL to be trusted as a resource URL. There are two approaches to
|
||
trust a URL:
|
||
|
||
1. **Setting trusted resource URLs with the `$sceDelegateProvider.resourceUrlWhitelist()` (called `trustedResourceUrlList()` from 1.8.1 onwards) 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?**'
|
||
]);
|
||
}]);
|
||
```
|
||
|
||
2. **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));
|
||
```
|
||
|
||
<hr />
|
||
<tests-only />
|
||
**Due to [4f6f2b](https://github.com/angular/angular.js/commit/4f6f2bce4ac93b85320e42e5023c09d099779b7d)**,
|
||
HTTP requests now update the outstanding request count synchronously. Previously, the request count
|
||
would not have been updated until the request to the server was actually in flight. Now the request
|
||
count is updated before any async interceptor is called.
|
||
|
||
The new behavior will also allow end-2-end tests to more correctly detect when AngularJS is stable,
|
||
but there is a chance it may change the observed behaviour in cases where an async request
|
||
interceptor is being used.
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014)**,
|
||
`$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()`.
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-services-$interpolate"></a>
|
||
#### **$interpolate**:
|
||
|
||
<minor />
|
||
**Due to [a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe)**,
|
||
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 as
|
||
`Object.prototype.toString`). Otherwise, interpolation uses `JSON.stringify()` as usual. If an
|
||
object has a custom `toString()` function, but you still want the output of `JSON.stringify()`, you
|
||
will need to manually convert to JSON (as shown below).
|
||
|
||
Before:
|
||
|
||
```html
|
||
<span>{{ myObject }}</span>
|
||
```
|
||
|
||
After:
|
||
|
||
```html
|
||
<span>{{ myObject | json }}</span>
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-services-$location"></a>
|
||
#### **$location**:
|
||
|
||
<major />
|
||
<a name="commit-aa077e8"></a>
|
||
**Due to [aa077e8](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)**,
|
||
the default hash-prefix used 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 than `mydomain.com/#/a/b/c` the URL will become
|
||
`mydomain.com/#!/a/b/c`.
|
||
|
||
If you actually want to have no hash-prefix, then you can restore the previous behavior by adding a
|
||
configuration block to you application:
|
||
|
||
```js
|
||
appModule.config(['$locationProvider', function($locationProvider) {
|
||
$locationProvider.hashPrefix('');
|
||
}]);
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-services-$q"></a>
|
||
#### **$q**:
|
||
|
||
<minor />
|
||
**Due to [e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9)**,
|
||
an error thrown from a promise's `onFulfilled` or `onRejection` handlers is treated exactly the same
|
||
as a regular rejection. Previously, it would also be passed to the `$exceptionHandler()` (in
|
||
addition to rejecting the promise with the error as reason).
|
||
|
||
The new behavior 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.
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [c9dffde](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0)**,
|
||
possibly unhandled rejected promises will be logged to the `$exceptionHandler`. Normally, that means
|
||
that an error will be logged to the console, but in tests `$exceptionHandler` will (by default)
|
||
re-throw any exceptions.
|
||
Tests that are affected by this change (e.g. tests that depend on specific order or number of
|
||
messages in `$exceptionHandler`) will need to handle rejected promises.
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ng-misc"></a>
|
||
### Core: _Miscellaneous_
|
||
|
||
<a name="migrate1.5to1.6-ng-misc-jqLite"></a>
|
||
#### **jqLite**:
|
||
|
||
<major />
|
||
**Due to [fc0c11](https://github.com/angular/angular.js/commit/fc0c11db845d53061430b7f05e773dcb3fb5b860)**,
|
||
jqLite will camelCase the keys passed to the `.data()` method, in the same way as jQuery 3+ does;
|
||
i.e. single hyphens followed by a lowercase letter will be converted to an uppercase letter.
|
||
Previously, keys passed to `.data()` were left untouched.
|
||
|
||
For example, with this change, the keys `a-b` and `aB` will now represent the same data piece;
|
||
writing to one of them will also be reflected when reading the value of the other one.
|
||
|
||
To migrate, you need to update your code as shown in the following examples:
|
||
|
||
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
|
||
```
|
||
|
||
<hr />
|
||
<major />
|
||
**Due to [73050c](https://github.com/angular/angular.js/commit/73050cdda04675bfa6705dc841ddbbb6919eb048)**,
|
||
the way jqLite camelCases keys passed to `.css()` is aligned with jQuery. Previously, when using
|
||
AngularJS without jQuery, `.css()` would camelCase keys more aggressively. Now, only a single hyphen
|
||
followed by a lowercase letter is getting transformed. This change also affects other APIs that rely
|
||
on the `.css()` method, such as `ngStyle`.
|
||
|
||
If you are using AngularJS with jQuery, your application is not affected by this change. If you are
|
||
not using jQuery, then you need to update your code as shown in the following examples:
|
||
|
||
Before:
|
||
|
||
```html
|
||
<!-- 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
|
||
// 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 -->
|
||
|
||
<!-- Only these two versions are still equivalent to the five shown above. -->
|
||
<div ng-style={'background-color': 'blue'}></div>
|
||
<div ng-style={backgroundColor: 'blue'}></div>
|
||
```
|
||
```js
|
||
// JS
|
||
|
||
// Only these two versions are still equivalent to the five shown above.
|
||
elem.css('background-color', 'blue');
|
||
elem.css('backgroundColor', 'blue');
|
||
|
||
// Only these two versions are still equivalent to the five shown above.
|
||
var bgColor = elem.css('background-color');
|
||
var bgColor = elem.css('backgroundColor');
|
||
```
|
||
|
||
<hr />
|
||
<major />
|
||
**Due to [7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**,
|
||
getting/setting boolean attributes will no longer take the corresponding properties into account.
|
||
Previously, all boolean attributes were reflected into the corresponding property when calling a
|
||
setter and from the corresponding property when calling a getter, even on elements that don't treat
|
||
those attributes in a special way. Now AngularJS doesn't do it by itself, but relies on browsers to
|
||
know when to reflect the property. Note that this browser-level conversion differs between browsers;
|
||
if you need to dynamically change the state of an element, you should modify the property, not the
|
||
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
|
||
description about a related change in jQuery 1.9.
|
||
|
||
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
|
||
|
||
Before:
|
||
|
||
```css
|
||
/* CSS */
|
||
|
||
input[checked="checked"] { ... }
|
||
```
|
||
```js
|
||
// JS
|
||
|
||
elem1.attr('checked', 'checked');
|
||
elem2.attr('checked', false);
|
||
```
|
||
|
||
After:
|
||
|
||
```css
|
||
/* CSS */
|
||
|
||
input:checked { ... }
|
||
```
|
||
```js
|
||
// JS
|
||
|
||
elem1.prop('checked', true);
|
||
elem2.prop('checked', false);
|
||
```
|
||
|
||
<hr />
|
||
<major />
|
||
**Due to [3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**,
|
||
calling `.attr(attrName, '')` (with `attrName` being a boolean attribute) will no longer remove the
|
||
attribute, but set it to its lowercase name as happens for every non-empty string. Previously,
|
||
calling `.attr(attrName, '')` would remove the boolean attribute.
|
||
|
||
If you want to remove a boolean attribute now, you have to call `.attr()` with `false` or `null`.
|
||
E.g.: `.attr(attrName, false)`
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)**,
|
||
calling `.attr(attrName, null)` will remove the attribute. Previously, it would set the
|
||
`attrName` attribute value to the string `'null'`. If you want to set the attribute value to the
|
||
string `'null'`, you have to explicitly call `.attr(attrName, 'null')`.
|
||
|
||
<hr />
|
||
<major />
|
||
**Due to [d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8)**,
|
||
calling the `.val()` getter on a jqLite element representing a `<select multiple>` element with no
|
||
options chosen will return an empty array. Previously, it would return `null`. If you relied on the
|
||
returned value being `null` or falsy, you need to change your code to check for a length of 0
|
||
instead:
|
||
|
||
Before:
|
||
|
||
```html
|
||
<select multiple>...</select>
|
||
```
|
||
```js
|
||
var value = $element.val();
|
||
if (value) { /* do something */ }
|
||
```
|
||
|
||
After:
|
||
|
||
```html
|
||
<select multiple>...</select>
|
||
```
|
||
```js
|
||
var value = $element.val();
|
||
if (value.length > 0) { /* do something */ }
|
||
```
|
||
|
||
|
||
<a name="migrate1.5to1.6-ng-misc-decorator"></a>
|
||
#### **decorator()**:
|
||
|
||
<minor />
|
||
**Due to [6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e)**,
|
||
`module.decorator` declarations are now processed as part of the `module.config` queue and may
|
||
result in providers being decorated in a different order if `module.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`:
|
||
|
||
```js
|
||
angular
|
||
.module('theApp', [])
|
||
.factory('theFactory', theFactoryFn)
|
||
.config(function($provide) {
|
||
$provide.decorator('theFactory', provideDecoratorFn);
|
||
})
|
||
.decorator('theFactory', moduleDecoratorFn);
|
||
```
|
||
|
||
Before this change, `'theFactory'` provider would be decorated in the following order:
|
||
1. `moduleDecoratorFn`
|
||
2. `provideDecoratorFn`
|
||
|
||
After this change, the order in which `'theFactory'` is decorated will be different, because now
|
||
`module.decorator` declarations are processed in the same order as `module.config` declarations:
|
||
1. `provideDecoratorFn`
|
||
2. `moduleDecoratorFn`
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ngAria"></a>
|
||
### ngAria
|
||
|
||
<a name="migrate1.5to1.6-ngAria-$aria"></a>
|
||
#### **$aria**:
|
||
|
||
<minor />
|
||
**Due to [ad41ba](https://github.com/angular/angular.js/commit/ad41baa1fdc057db3fe529ff87735b173b164b4c)**,
|
||
if you were explicitly setting the value of the `bindKeypress` flag, you need to change your code to
|
||
use `bindKeydown` instead.
|
||
|
||
Before: `$ariaProvider.config({bindKeypress: xyz})`<br />
|
||
After: `$ariaProvider.config({bindKeydown: xyz})`
|
||
|
||
|
||
<a name="migrate1.5to1.6-ngAria-ngClick"></a>
|
||
#### **ngClick**:
|
||
|
||
<minor />
|
||
**Due to [ad41ba](https://github.com/angular/angular.js/commit/ad41baa1fdc057db3fe529ff87735b173b164b4c)**,
|
||
`ngClick` will respond to the `keydown` keyboard event, instead of the `keypress`. Also, if the
|
||
element already has any of the `ngKeydown`/`ngKeyup`/`ngKeypress` directives, `ngAria` will _not_
|
||
bind to the `keydown` 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 the `keydown`.
|
||
In that case, you need to manually use the `ngKeypress` directive (in addition to `ngClick`).
|
||
|
||
Before:
|
||
|
||
```html
|
||
<div ng-click="onClick()">
|
||
I respond to `click` and `keypress` (not `keydown`)
|
||
</div>
|
||
```
|
||
|
||
After:
|
||
|
||
```html
|
||
<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 to `ngAria`),
|
||
you need to change the tests to trigger `keydown` events instead.
|
||
|
||
|
||
<a name="migrate1.5to1.6-ngAria-ngModel"></a>
|
||
#### **ngModel**:
|
||
|
||
<major />
|
||
<a name="commit-975a61"></a>
|
||
**Due to [975a61](https://github.com/angular/angular.js/commit/975a6170efceb2a5e6377c57329731c0636eb8c8)**,
|
||
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"). 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 = '<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:
|
||
|
||
```js
|
||
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
|
||
```
|
||
|
||
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;
|
||
};
|
||
}
|
||
};
|
||
})
|
||
```
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [9978de1](https://github.com/angular/angular.js/commit/9978de11b7295fec1a2f4cb8fbeb9b62b54cb711)**,
|
||
the `role` attribute will no longer be added 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.
|
||
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).
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ngMock"></a>
|
||
### ngMock
|
||
|
||
<a name="migrate1.5to1.6-ngMock-$httpBackend"></a>
|
||
#### **$httpBackend**:
|
||
|
||
<tests-only />
|
||
**Due to [267ee9](https://github.com/angular/angular.js/commit/267ee9c892b0eb40908700ee2435793f8c6c1c84)**,
|
||
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 calling `verifyNoOutstandingRequest()`. 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.
|
||
|
||
<hr />
|
||
<tests-only />
|
||
**Due to [7551b8](https://github.com/angular/angular.js/commit/7551b8975a91ee286cc2cf4af5e78f924533575e)**,
|
||
it is no longer valid to explicitly pass `undefined` as the `url` argument to any of the
|
||
`$httpBackend.when...()` and `$httpBackend.expect...()` methods. While this argument is optional, it
|
||
must have a defined value if provided. Previously passing an explicit `undefined` value was ignored,
|
||
but this lead to invalid tests passing unexpectedly.
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ngResource"></a>
|
||
### ngResource
|
||
|
||
<a name="migrate1.5to1.6-ngResource-$resource"></a>
|
||
#### **$resource**:
|
||
|
||
<minor />
|
||
**Due to [acb545](https://github.com/angular/angular.js/commit/acb545ec3ebf099db68561033645941c900973b5)**,
|
||
all own properties of the `params` object that are not used to replace URL params, will be passed to
|
||
`$http` as `config.params` (to be used as query parameters in the URL). Previously, parameters where
|
||
omitted if `Object.prototype` had a property with the same name. E.g.:
|
||
|
||
Before:
|
||
|
||
```js
|
||
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:
|
||
|
||
```js
|
||
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 :)
|
||
```
|
||
|
||
<hr />
|
||
<tests-only />
|
||
**Due to [2456ab](https://github.com/angular/angular.js/commit/2456ab63a613902d21c151445f9c697a76ab43b3)**,
|
||
semicolon has been added to the list of delimiters that are not encoded 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.
|
||
|
||
|
||
<br />
|
||
<a name="migrate1.5to1.6-ngRoute"></a>
|
||
### ngRoute
|
||
|
||
<a name="migrate1.5to1.6-ngRoute-$route"></a>
|
||
#### **$route**:
|
||
|
||
<tests-only />
|
||
**Due to [c13c66](https://github.com/angular/angular.js/commit/c13c666728c1a1485ef18e92d7cb35118ce39609)**,
|
||
`$route` (and its dependencies; e.g. `$location`) will - by default - be instantiated early on.
|
||
Previously, in cases where `ngView` was loaded asynchronously, `$route` (and its dependencies) might
|
||
also have been instantiated asynchronously.
|
||
|
||
Although this is not expected to have unwanted side-effects in normal application behavior, it may
|
||
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
|
||
request will be made for the default route's template. If not properly "trained", `$httpBackend`
|
||
will complain about this unexpected request. You can restore the previous behavior (and avoid
|
||
unexpected requests in tests), by using `$routeProvider.eagerInstantiationEnabled(false)`.
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [e98656](https://github.com/angular/angular.js/commit/e9865654b39c71be71034c38581a8c7bd16bc716)**,
|
||
if a `redirectTo` function throws an Error, a `$routeChangeError` event will be fired. Previously,
|
||
execution would be aborted without firing a `$routeChangeError` event.
|
||
|
||
<hr />
|
||
<minor />
|
||
**Due to [7f4b35](https://github.com/angular/angular.js/commit/7f4b356c2bebb87f0c26b57a20415b004b20bcd1)**,
|
||
the `$route` service will no longer instantiate controllers nor call `resolve` or
|
||
`template`/`templateUrl` functions for routes that successfully `redirectTo` other routes.
|
||
|
||
|
||
|
||
|
||
|
||
## Migrating from 1.4 to 1.5
|
||
|
||
AngularJS 1.5 takes a big step towards preparing developers for a smoother transition to Angular in
|
||
the future. Architecting your applications using components, multi-slot transclusion, one-way
|
||
bindings in isolate scopes, using lifecycle hooks in directive controllers and relying on native ES6
|
||
features (such as classes and arrow functions) are now all possible with AngularJS 1.5.
|
||
|
||
|
||
This release includes numerous bug and security fixes, as well as performance improvements to core
|
||
services, directives, filters and helper functions. Existing applications can start enjoying the
|
||
benefits of such changes in `$compile`, `$parse`, `$animate`, `$animateCss`, `$sanitize`, `ngOptions`,
|
||
`currencyFilter`, `numberFilter`, `copy()` (to name but a few) without any change in code.
|
||
|
||
New features have been added to more than a dozen services, directives and filters across 8 modules.
|
||
Among them, a few stand out:
|
||
|
||
* `angular.component()`: Introducing "components", a special sort of directive that are easy to
|
||
configure and promote best practices (plus can bring AngularJS applications closer to Angular's
|
||
style of architecture).
|
||
* Multi-slot transclusion: Enabling the design of more powerful and complex UI elements with a much
|
||
simpler configuration and reduced boilerplate.
|
||
* `$onInit` lifecycle hook: Introducing a new lifecycle hook for directive controllers, called after
|
||
all required controllers have been constructed. This enables access to required controllers from
|
||
a directive's controller, without having to rely on the linking function.
|
||
* `ngAnimateSwap`: A new directive in `ngAnimate`, making it super easy to create rotating
|
||
banner-like components.
|
||
* Testing helpers: New helper functions in `ngMock`, simplifying testing for animations, component
|
||
controllers and routing.
|
||
|
||
Also, notable is the improved support for ES6 features, such as classes and arrow functions. These
|
||
features are now more reliably detected and correctly handled within the core.
|
||
|
||
|
||
All this goodness doesn't come without a price, though. Below is a list of breaking changes (grouped
|
||
by module) that need to be taken into account while migrating from 1.4. Fortunately, the majority of
|
||
them should have a pretty low impact on most applications.
|
||
|
||
|
||
### Core
|
||
|
||
We tried to keep the breaking changes inside the core components to a bare minimum. Still, a few of
|
||
them were unavoidable.
|
||
|
||
#### Services (`$parse`)
|
||
|
||
Due to [0ea53503](https://github.com/angular/angular.js/commit/0ea535035a3a1a992948490c3533bffb83235052),
|
||
a new special property, `$locals`, will be available for accessing the locals from an expression.
|
||
This is a breaking change, only if a `$locals` property does already exist (and needs to be
|
||
referenced) either on the `scope` or on the `locals` object. Your expressions should be changed to
|
||
access such existing properties as `this.$locals` and `$locals.$locals` respectively.
|
||
|
||
|
||
#### Directives (`ngOptions`)
|
||
|
||
A fair amount of work has been put into the `ngOptions` directive, fixing bugs and corner-cases and
|
||
neutralizing browser quirks. A couple of breaking changes were made in the process:
|
||
|
||
Due to [b71d7c3f](https://github.com/angular/angular.js/commit/b71d7c3f3c04e65b02d88b33c22dd90ae3cdfc27),
|
||
falsy values (`''`, `0`, `false` and `null`) are properly recognized as option group identifiers for
|
||
options passed to `ngOptions`. Previously, all of these values were ignored and the option was not
|
||
assigned to any group. `undefined` is still interpreted as "no group".
|
||
If you have options with falsy group identifiers that should still not be assigned to any group,
|
||
then you must filter the values before passing them to `ngOptions`, converting falsy values to
|
||
`undefined`.
|
||
|
||
Due to [ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
|
||
`ngOptions` now explicitly requires `ngModel` on the same element, thus an error will be thrown if
|
||
`ngModel` is not found. Previously, `ngOptions` would silently fail, which could lead to
|
||
hard-to-debug errors.
|
||
This is not expected to have any significant impact on applications, since `ngOptions` didn't work
|
||
without `ngModel` before either. The main difference is that now it will fail with a more
|
||
informative error message.
|
||
|
||
|
||
#### Filters (`orderBy`)
|
||
|
||
Due to [2a85a634](https://github.com/angular/angular.js/commit/2a85a634f86c84f15b411ce009a3515fca7ba580),
|
||
passing a non-array-like value (other than `undefined` or `null`) through the `orderBy` filter will
|
||
throw an error. Previously, the input was returned unchanged, which could lead to hard-to-spot bugs
|
||
and was not consistent with other filters (e.g. `filter`).
|
||
Objects considered array-like include: arrays, array subclasses, strings, NodeLists,
|
||
jqLite/jQuery collections
|
||
|
||
#### Helper Functions:
|
||
|
||
The `angular.lowercase` and `angular.uppercase` functions have been **deprecated** and will be removed
|
||
in version 1.7.0. It is recommended to use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) and [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) functions instead.
|
||
|
||
|
||
### ngAria
|
||
|
||
Due to [d06431e](https://github.com/angular/angular.js/commit/d06431e5309bb0125588877451dc79b935808134),
|
||
the `ngAria`-enhanced directives (e.g. `ngModel`, `ngDisabled` etc) will not apply ARIA attributes
|
||
to native inputs, unless necessary. Previously, ARIA attributes were always applied to native
|
||
inputs, despite this being unnecessary in most cases.
|
||
In the context of `ngAria`, elements considered "native inputs" include:
|
||
`<a>`, `<button>`, `<details>`, `<input>`, `<select>`, `<summary>`, `<textarea>`
|
||
|
||
This change will not affect the accessibility of your applications (since native inputs are
|
||
accessible by default), but if you relied on ARIA attributes being present on native inputs (for
|
||
whatever reason), you'll have to add and update them manually.
|
||
|
||
Additionally, the `aria-multiline` attribute, which was previously added to elements with a `type`
|
||
or `role` of `textbox`, will not be added anymore, since there is no way `ngAria` can tell if the
|
||
textbox element is multiline or not.
|
||
If you relied on `aria-multiline="true"` being automatically added by `ngAria`, you need to apply it
|
||
yourself. E.g. change your code from `<div role="textbox" ng-model="..." ...>` to
|
||
`<div role="textbox" ng-model="..." ... aria-multiline="true">`.
|
||
|
||
|
||
### ngMessages (`ngMessage`)
|
||
|
||
Due to [4971ef12](https://github.com/angular/angular.js/commit/4971ef12d4c2c268cb8d26f90385dc96eba19db8),
|
||
the `ngMessage` directive is now compiled with a priority of 1, which means directives on the same
|
||
element as `ngMessage` with a priority lower than 1 will be applied when `ngMessage` calls its
|
||
`$transclude` function. Previously, they were applied during the initial compile phase and were
|
||
passed the comment element created by the transclusion of `ngMessage`.
|
||
If you have custom directives that relied on the previous behavior, you need to give them a priority
|
||
of 1 or greater.
|
||
|
||
|
||
### ngResource (`$resource`)
|
||
|
||
The `$resource` service underwent a minor internal refactoring to finally solve a long-standing bug
|
||
preventing requests from being cancelled using promises. Due to the nature of `$resource`'s
|
||
configuration, it was not possible to follow the `$http` convention. A new `$cancelRequest()` method
|
||
was introduced instead.
|
||
|
||
Due to [98528be3](https://github.com/angular/angular.js/commit/98528be311b48269ba0e15ba4e3e2ad9b89693a9),
|
||
using a promise as `timeout` in `$resource` is no longer supported and will log a warning. This is
|
||
hardly expected to affect the behavior of your application, since a promise as `timeout` didn't work
|
||
before either, but it will now warn you explicitly when trying to pass one.
|
||
If you need to be able to cancel pending requests, you can now use the new `$cancelRequest()` that
|
||
will be available on `$resource` instances.
|
||
|
||
|
||
### ngRoute (`ngView`)
|
||
|
||
Due to [983b0598](https://github.com/angular/angular.js/commit/983b0598121a8c5a3a51a30120e114d7e3085d4d),
|
||
a new property will be available on the scope of the route, allowing easy access to the route's
|
||
resolved values from the view's template. The default name for this property is `$resolve`. This is
|
||
a breaking change, only if a `$resolve` property is already available on the scope, in which case
|
||
the existing property will be hidden or overwritten.
|
||
To fix this, you should choose a custom name for this property, that does not collide with other
|
||
properties on the scope, by specifying the `resolveAs` property on the route.
|
||
|
||
|
||
### ngSanitize (`$sanitize`, `linky`)
|
||
|
||
The HTML sanitizer has been re-implemented using inert documents, increasing security, fixing some
|
||
corner-cases that were difficult to handle and reducing its size by about 20% (in terms of loc). In
|
||
order to make it more secure by default, a couple of breaking changes have been introduced:
|
||
|
||
Due to [181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
|
||
SVG support in `$sanitize` is now an opt-in feature (i.e. disabled by default), as it could make
|
||
an application vulnerable to click-hijacking attacks. If your application relies on it, you can
|
||
still turn it on with `$sanitizeProvider.enableSvg(true)`, but you extra precautions need to be
|
||
taken in order to keep your application secure. Read the documentation for more information about
|
||
the dangers and ways to mitigate them.
|
||
|
||
Due to [7a668cdd](https://github.com/angular/angular.js/commit/7a668cdd7d08a7016883eb3c671cbcd586223ae8),
|
||
the `$sanitize` service will now remove instances of the `<use>` tag from the content passed to it.
|
||
This element is used to import external SVG resources, which is a security risk as the `$sanitize`
|
||
service does not have access to the resource in order to sanitize it.
|
||
|
||
Similarly, due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
|
||
the `$sanitize` service will now also remove instances of the `usemap` attribute from any elements
|
||
passed to it. This attribute is used to reference another element by `name` or `id`. Since the
|
||
`name` and `id` attributes are already banned, a sanitized `usemap` attribute could only
|
||
reference unsanitized content, which is a security risk.
|
||
|
||
Due to [98c2db7f](https://github.com/angular/angular.js/commit/98c2db7f9c2d078a408576e722407d518c7ee10a),
|
||
passing a non-string value (other than `undefined` or `null`) through the `linky` filter will throw
|
||
an error. This is not expected to have any significant impact on applications, since the input was
|
||
always assumed to be of type 'string', so passing non-string values never worked correctly anyway.
|
||
The main difference is that now it will fail faster and with a more informative error message.
|
||
|
||
|
||
### ngTouch (`ngClick`)
|
||
|
||
Due to [0dfc1dfe](https://github.com/angular/angular.js/commit/0dfc1dfebf26af7f951f301c4e3848ac46f05d7f),
|
||
the `ngClick` override directive from the `ngTouch` module is **deprecated and disabled by default**.
|
||
This means that on touch-based devices, users might now experience a 300ms delay before a click
|
||
event is fired.
|
||
|
||
If you rely on this directive, you can still enable it using
|
||
`$touchProvider.ngClickOverrideEnabled()`:
|
||
|
||
```js
|
||
angular.module('myApp').config(function($touchProvider) {
|
||
$touchProvider.ngClickOverrideEnabled(true);
|
||
});
|
||
```
|
||
|
||
Going forward, we recommend using [FastClick](https://github.com/ftlabs/fastclick) or perhaps one of
|
||
the [AngularJS 3rd party touch-related modules](http://ngmodules.org/tags/touch) that provide similar
|
||
functionality.
|
||
|
||
Also note that modern browsers already remove the 300ms delay under some circumstances:
|
||
|
||
- **Chrome and Firefox for Android** remove the 300ms delay when the well-known
|
||
`<meta name="viewport" content="width=device-width">` is set.
|
||
- **Internet Explorer** removes the delay, when the `touch-action` css property is set to `none` or
|
||
`manipulation`.
|
||
- Since **iOS 8, Safari** removes the delay on so-called "slow taps".
|
||
|
||
For more info on the topic, you can take a look at this
|
||
[article by Telerik](http://developer.telerik.com/featured/300-ms-click-delay-ios-8/).
|
||
|
||
<div class="alert alert-warning">
|
||
**Note:** This change does **not** affect the `ngSwipe` directive.
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
## Migrating from 1.3 to 1.4
|
||
|
||
AngularJS 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
|
||
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions`, `ngPattern`, `pattern` and some fixes to core filters:
|
||
`limitTo` and `filter`.
|
||
|
||
The reason for the ngAnimate refactor was to fix timing issues and to expose new APIs to allow
|
||
for developers to construct more versatile animations. We now have access to `$animateCss`
|
||
and the many timing-oriented bugs were fixed which results in smoother animations.
|
||
If animation is something of interest, then please read over the breaking changes below for animations when
|
||
`ngAnimate` is used.
|
||
|
||
`ngMessages` has been upgraded to allow for dynamic message resolution. This handy feature allows for developers
|
||
to render error messages with ngMessages that are listed with a directive such as ngRepeat. A great usecase for this
|
||
involves pulling error message data from a server and then displaying that data via the mechanics of ngMessages. Be
|
||
sure to read the breaking change involved with `ngMessagesInclude` to upgrade your template code.
|
||
|
||
Other changes, such as the ordering of elements with ngRepeat and ngOptions and the way ngPattern and pattern directives
|
||
validate the regex, may also affect the behavior of your application. And be sure to also read up on the changes to `$cookies`.
|
||
The migration jump from 1.3 to 1.4 should be relatively straightforward otherwise.
|
||
|
||
|
||
|
||
|
||
### Animation (`ngAnimate`)
|
||
|
||
Animations in 1.4 have been refactored internally, but the API has stayed much the same. There are, however,
|
||
some breaking changes that need to be addressed when upgrading to 1.4.
|
||
|
||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||
JavaScript and CSS animations can no longer be run in
|
||
parallel. With earlier versions of ngAnimate, both CSS and JS animations
|
||
would be run together when multiple animations were detected. This
|
||
feature has been removed, however, the same effect, with even more
|
||
possibilities, can be achieved by injecting `$animateCss` into a
|
||
JavaScript-defined animation and creating custom CSS-based animations
|
||
from there.
|
||
|
||
By using `$animateCss` inside of a JavaScript animation in AngularJS 1.4, we can trigger custom CSS-based animations
|
||
directly from our JavaScript code.
|
||
|
||
```js
|
||
ngModule.animation('.slide-animation', ['$animateCss', function($animateCss) {
|
||
return {
|
||
enter: function(element, doneFn) {
|
||
// this will trigger a `.ng-enter` and `.ng-enter-active` CSS animation
|
||
var animation = $animateCss(element, {
|
||
event: 'enter'
|
||
// any other CSS-related properties
|
||
// addClass: 'some-class',
|
||
// removeClass: 'some-other-class',
|
||
// from: {},
|
||
// to: {}
|
||
});
|
||
|
||
// make sure to read the ngAnimate docs to understand how this works
|
||
animation.start().done(doneFn);
|
||
}
|
||
}
|
||
}]);
|
||
```
|
||
|
||
{@link ngAnimate.$animateCss Click here to learn how to use $animateCss in your animation code}
|
||
|
||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||
animation-related callbacks are now fired on `$animate.on` instead of directly being on the element.
|
||
|
||
```js
|
||
// < 1.4
|
||
element.on('$animate:before', function(e, data) {
|
||
if (data.event === 'enter') { ... }
|
||
});
|
||
element.off('$animate:before', fn);
|
||
|
||
// 1.4+
|
||
$animate.on('enter', element, function(data) {
|
||
//...
|
||
});
|
||
$animate.off('enter', element, fn);
|
||
```
|
||
|
||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||
the function params for `$animate.enabled()` when an element is used are now flipped. This fix allows
|
||
the function to act as a getter when a single element param is provided.
|
||
|
||
```js
|
||
// < 1.4
|
||
$animate.enabled(false, element);
|
||
|
||
// 1.4+
|
||
$animate.enabled(element, false);
|
||
```
|
||
|
||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||
in addition to disabling the children of the element, `$animate.enabled(element, false)` will now also
|
||
disable animations on the element itself.
|
||
|
||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||
there is no need to call `$scope.$apply` or `$scope.$digest` inside of a animation promise callback anymore
|
||
since the promise is resolved within a digest automatically. (Not to worry, any extra digests will not be
|
||
run unless the promise is used.)
|
||
|
||
```js
|
||
// < 1.4
|
||
$animate.enter(element).then(function() {
|
||
$scope.$apply(function() {
|
||
$scope.explode = true;
|
||
});
|
||
});
|
||
|
||
// 1.4+
|
||
$animate.enter(element).then(function() {
|
||
$scope.explode = true;
|
||
});
|
||
```
|
||
|
||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||
when an enter, leave or move animation is triggered then it will always end any pending or active parent
|
||
class based animations (animations triggered via ngClass) in order to ensure that any CSS styles are resolved in time.
|
||
|
||
|
||
|
||
|
||
### Forms (`ngMessages`, `ngOptions`, `select`, `ngPattern` and `pattern`, `form`)
|
||
|
||
#### ngMessages
|
||
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
|
||
and compatible with dynamic message data. The `ngMessage` directive now supports a new attribute
|
||
called `ng-message-exp` which will evaluate an expression and will keep track of that expression
|
||
as it changes in order to re-evaluate the listed messages.
|
||
|
||
[Click here to learn more about dynamic ng-messages](https://docs.angularjs.org/api/ngMessages#dynamic-messaging)
|
||
|
||
There is only one breaking change. Please consider the following when including remote
|
||
message templates via `ng-messages-include`:
|
||
|
||
Due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0),
|
||
the `ngMessagesInclude` attribute has now been removed and cannot be used in the same element containing
|
||
the `ngMessages` directive. Instead, `ngMessagesInclude` is to be used on its own element inline with
|
||
other inline messages situated as children within the `ngMessages` container directive.
|
||
|
||
```html
|
||
<!-- AngularJS 1.3.x -->
|
||
<div ng-messages="model.$error" ng-messages-include="remote.html">
|
||
<div ng-message="required">Your message is required</div>
|
||
</div>
|
||
|
||
<!-- AngularJS 1.4.x -->
|
||
<div ng-messages="model.$error">
|
||
<div ng-message="required">Your message is required</div>
|
||
<div ng-messages-include="remote.html"></div>
|
||
</div>
|
||
```
|
||
|
||
Depending on where the `ngMessagesInclude` directive is placed it will be prioritized inline with the other messages
|
||
before and after it.
|
||
|
||
Also due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0),
|
||
it is no longer possible to use interpolation inside the `ngMessages` attribute expression. This technique
|
||
is generally not recommended, and can easily break when a directive implementation changes. In cases
|
||
where a simple expression is not possible, you can delegate accessing the object to a function:
|
||
|
||
```html
|
||
<div ng-messages="ctrl.form['field_{{$index}}'].$error">...</div>
|
||
```
|
||
would become
|
||
```html
|
||
<div ng-messages="ctrl.getMessages($index)">...</div>
|
||
```
|
||
where `ctrl.getMessages()`
|
||
```javascript
|
||
ctrl.getMessages = function($index) {
|
||
return ctrl.form['field_' + $index].$error;
|
||
}
|
||
```
|
||
|
||
#### ngOptions
|
||
|
||
The `ngOptions` directive has also been refactored and as a result some long-standing bugs
|
||
have been fixed. The breaking changes are comparatively minor and should not affect most applications.
|
||
|
||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||
when `ngOptions` renders the option values within the DOM, the resulting HTML code is different.
|
||
Normally this should not affect your application at all, however, if your code relies on inspecting
|
||
the value property of `<option>` elements (that `ngOptions` generates) then be sure
|
||
to [read the details](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef).
|
||
|
||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||
when iterating over an object's properties using the `(key, value) in obj` syntax
|
||
the order of the elements used to be sorted alphabetically. This was an artificial
|
||
attempt to create a deterministic ordering since browsers don't guarantee the order.
|
||
But in practice this is not what people want and so this change iterates over properties
|
||
in the order they are returned by Object.keys(obj), which is almost always the order
|
||
in which the properties were defined.
|
||
|
||
Also due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||
setting the ngOptions attribute expression after the element is compiled, will no longer trigger the ngOptions behavior.
|
||
This worked previously because the ngOptions logic was part of the select directive, while
|
||
it is now implemented in the ngOptions directive itself.
|
||
|
||
#### select
|
||
|
||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
|
||
values to determine which option is selected. This means non-string scope values (such as `Number` or `Boolean`)
|
||
will not be matched against equivalent option strings (such as the strings `"123"`, `"true"` or `"false"`).
|
||
|
||
In AngularJS 1.3.x, setting `scope.x = 200` would select the option with the value 200 in the following `select`:
|
||
|
||
```
|
||
<select ng-model="x">
|
||
<option value="100">100</option>
|
||
<option value="200">200</option>
|
||
</select>
|
||
```
|
||
|
||
In AngularJS 1.4.x, the 'unknown option' will be selected.
|
||
|
||
To remedy this, you can initialize the model as a string: `scope.x = '200'`, or if you want to
|
||
keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`:
|
||
|
||
```js
|
||
ngModelCtrl.$parsers.push(function(value) {
|
||
return parseInt(value, 10); // Convert option value to number
|
||
});
|
||
|
||
ngModelCtrl.$formatters.push(function(value) {
|
||
return value.toString(); // Convert scope value to string
|
||
});
|
||
```
|
||
|
||
#### ngPattern and pattern
|
||
|
||
Due to [0e001084](https://github.com/angular/angular.js/commit/0e001084ffff8674efad289d37cb16cc4e46b50a),
|
||
The `ngPattern` and `pattern` directives will validate the regex
|
||
against the `$viewValue` of `ngModel`, i.e. the value of the model
|
||
before the $parsers are applied. Previously, the `$modelValue`
|
||
(the result of the $parsers) was validated.
|
||
|
||
This fixes issues where `input[date]` and `input[number]` cannot
|
||
be validated because the `$viewValue` string is parsed into
|
||
`Date` and `Number` respectively (starting with AngularJS 1.3).
|
||
It also brings the directives in line with HTML5 constraint
|
||
validation, which validates against the input value.
|
||
|
||
This change is unlikely to cause applications to fail, because even
|
||
in AngularJS 1.2, the value that was validated by pattern could have
|
||
been manipulated by the $parsers, as all validation was done
|
||
inside this pipeline.
|
||
|
||
If you rely on the pattern being validated against the `$modelValue`,
|
||
you must create your own validator directive that overwrites
|
||
the built-in pattern validator:
|
||
|
||
```
|
||
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
|
||
return {
|
||
restrict: 'A',
|
||
require: '?ngModel',
|
||
priority: 1,
|
||
compile: function() {
|
||
var regexp, patternExp;
|
||
|
||
return {
|
||
pre: function(scope, elm, attr, ctrl) {
|
||
if (!ctrl) return;
|
||
|
||
attr.$observe('pattern', function(regex) {
|
||
/**
|
||
* The built-in directive will call our overwritten validator
|
||
* (see below). We just need to update the regex.
|
||
* The preLink fn guaranetees our observer is called first.
|
||
*/
|
||
if (isString(regex) && regex.length > 0) {
|
||
regex = new RegExp('^' + regex + '$');
|
||
}
|
||
|
||
if (regex && !regex.test) {
|
||
//The built-in validator will throw at this point
|
||
return;
|
||
}
|
||
|
||
regexp = regex || undefined;
|
||
});
|
||
|
||
},
|
||
post: function(scope, elm, attr, ctrl) {
|
||
if (!ctrl) return;
|
||
|
||
regexp, patternExp = attr.ngPattern || attr.pattern;
|
||
|
||
//The postLink fn guarantees we overwrite the built-in pattern validator
|
||
ctrl.$validators.pattern = function(value) {
|
||
return ctrl.$isEmpty(value) ||
|
||
isUndefined(regexp) ||
|
||
regexp.test(value);
|
||
};
|
||
}
|
||
};
|
||
}
|
||
};
|
||
});
|
||
```
|
||
|
||
|
||
#### form
|
||
|
||
Due to [94533e57](https://github.com/angular/angular.js/commit/94533e570673e6b2eb92073955541fa289aabe02),
|
||
the `name` attribute of `form` elements can now only contain characters that can be evaluated as part
|
||
of an AngularJS expression. This is because AngularJS uses the value of `name` as an assignable expression
|
||
to set the form on the `$scope`. For example, `name="myForm"` assigns the form to `$scope.myForm` and
|
||
`name="myObj.myForm"` assigns it to `$scope.myObj.myForm`.
|
||
|
||
Previously, it was possible to also use names such `name="my:name"`, because AngularJS used a special setter
|
||
function for the form name. Now the general, more robust `$parse` setter is used.
|
||
|
||
The easiest way to migrate your code is therefore to remove all special characters from the `name` attribute.
|
||
|
||
If you need to keep the special characters, you can use the following directive, which will replace
|
||
the `name` with a value that can be evaluated as an expression in the compile function, and then
|
||
re-set the original name in the postLink function. This ensures that (1), the form is published on
|
||
the scope, and (2), the form has the original name, which might be important if you are doing server-side
|
||
form submission.
|
||
|
||
```js
|
||
angular.module('myApp').directive('form', function() {
|
||
return {
|
||
restrict: 'E',
|
||
priority: 1000,
|
||
compile: function(element, attrs) {
|
||
var unsupportedCharacter = ':'; // change accordingly
|
||
var originalName = attrs.name;
|
||
if (attrs.name && attrs.name.indexOf(unsupportedCharacter) > 0) {
|
||
attrs.$set('name', 'this["' + originalName + '"]');
|
||
}
|
||
|
||
return postLinkFunction(scope, element) {
|
||
// Don't trigger $observers
|
||
element.setAttribute('name', originalName);
|
||
}
|
||
}
|
||
};
|
||
});
|
||
```
|
||
|
||
### Templating (`ngRepeat`, `$compile`, `ngInclude`)
|
||
|
||
#### ngRepeat
|
||
|
||
Due to [c260e738](https://github.com/angular/angular.js/commit/c260e7386391877625eda086480de73e8a0ba921),
|
||
previously, the order of items when using ngRepeat to iterate over object properties was guaranteed to be consistent
|
||
by sorting the keys into alphabetic order.
|
||
|
||
Now, the order of the items is browser dependent based on the order returned
|
||
from iterating over the object using the `for key in obj` syntax.
|
||
|
||
It seems that browsers generally follow the strategy of providing
|
||
keys in the order in which they were defined, although there are exceptions
|
||
when keys are deleted and reinstated. See
|
||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
|
||
|
||
The best approach is to convert Objects into Arrays by a filter such as
|
||
https://github.com/petebacondarwin/angular-toArrayFilter
|
||
or some other mechanism, and then sort them manually in the order you need.
|
||
|
||
|
||
#### $compile
|
||
|
||
Due to [6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9efff503d17eb3cbeb666d422),
|
||
previously, '&' expressions would always set up a function in the isolate scope. Now, if the binding
|
||
is marked as optional and the attribute is not specified, no function will be added to the isolate scope.
|
||
|
||
Due to [62d514b](https://github.com/angular/angular.js/commit/62d514b06937cc7dd86e973ea11165c88343b42d),
|
||
returning an object from a controller constructor function will now override the scope. Views that use the
|
||
controllerAs method will no longer get the this reference, but the returned object.
|
||
|
||
#### ngInclude
|
||
Due to [3c6e8ce044446735eb2e70d0061db8c6db050289](https://github.com/angular/angular.js/commit/3c6e8ce044446735eb2e70d0061db8c6db050289), the `src` attribute of ngInclude no longer accepts an
|
||
expression that returns the result of `$sce.trustAsResourceUrl`. This will now cause an infinite digest:
|
||
|
||
Before:
|
||
```html
|
||
<div ng-include="findTemplate('https://example.com/templates/myTemplate.html')"></div>
|
||
```
|
||
|
||
```js
|
||
$scope.findTemplate = function(templateName) {
|
||
return $sce.trustAsResourceUrl(templateName);
|
||
};
|
||
```
|
||
|
||
To migrate, either cache the result of `trustAsResourceUrl()`, or put the template url in the trusted resource
|
||
URL list in the `config()` function:
|
||
|
||
After:
|
||
|
||
```js
|
||
var templateCache = {};
|
||
$scope.findTemplate = function(templateName) {
|
||
if (!templateCache[templateName]) {
|
||
templateCache[templateName] = $sce.trustAsResourceUrl(templateName);
|
||
}
|
||
|
||
return templateCache[templateName];
|
||
};
|
||
|
||
// Alternatively, use `$sceDelegateProvider.resourceUrlWhitelist()` (called
|
||
// `trustedResourceUrlList()` from 1.8.1 onwards), which means you don't
|
||
// have to use `$sce.trustAsResourceUrl()` at all:
|
||
|
||
angular.module('myApp', []).config(function($sceDelegateProvider) {
|
||
$sceDelegateProvider.resourceUrlWhitelist(['self', 'https://example.com/templates/**'])
|
||
});
|
||
```
|
||
|
||
|
||
### Cookies (`ngCookies`)
|
||
|
||
Due to [38fbe3ee](https://github.com/angular/angular.js/commit/38fbe3ee8370fc449b82d80df07b5c2ed2cd5fbe),
|
||
`$cookies` will no longer expose properties that represent the current browser cookie
|
||
values. `$cookies` no longer polls the browser for changes to the cookies and ***no longer copies
|
||
cookie values onto the `$cookies` object***.
|
||
|
||
This was changed because the polling is expensive and caused issues with the `$cookies` properties
|
||
not synchronizing correctly with the actual browser cookie values (The reason the polling
|
||
was originally added was to allow communication between different tabs,
|
||
but there are better ways to do this today, for example `localStorage`.)
|
||
|
||
The new API on `$cookies` is as follows:
|
||
|
||
* `get`
|
||
* `put`
|
||
* `getObject`
|
||
* `putObject`
|
||
* `getAll`
|
||
* `remove`
|
||
|
||
You must explicitly use the methods above in order to access cookie data. This also means that
|
||
you can no longer watch the properties on `$cookies` to detect changes
|
||
that occur on the browsers cookies.
|
||
|
||
This feature is generally only needed if a 3rd party library was programmatically
|
||
changing the cookies at runtime. If you rely on this then you must either write code that
|
||
can react to the 3rd party library making the changes to cookies or implement your own polling
|
||
mechanism.
|
||
|
||
**DEPRECATION NOTICE**
|
||
|
||
`$cookieStore` is now deprecated as all the useful logic
|
||
has been moved to `$cookies`, to which `$cookieStore` now simply
|
||
delegates calls.
|
||
|
||
|
||
### Server Requests (`$http`)
|
||
|
||
Due to [5da1256](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369),
|
||
`transformRequest` functions can no longer modify request headers.
|
||
|
||
Before this commit `transformRequest` could modify request headers, ex.:
|
||
|
||
```javascript
|
||
function requestTransform(data, headers) {
|
||
headers = angular.extend(headers(), {
|
||
'X-MY_HEADER': 'abcd'
|
||
});
|
||
}
|
||
return angular.toJson(data);
|
||
}
|
||
```
|
||
|
||
This behavior was unintended and undocumented, so the change should affect very few applications. If one
|
||
needs to dynamically add / remove headers it should be done in a header function, for example:
|
||
|
||
```javascript
|
||
$http.get(url, {
|
||
headers: {
|
||
'X-MY_HEADER': function(config) {
|
||
return 'abcd'; //you've got access to a request config object to specify header value dynamically
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
|
||
### Filters (`filter`, `limitTo`)
|
||
|
||
#### `filter` filter
|
||
Due to [cea8e751](https://github.com/angular/angular.js/commit/cea8e75144e6910b806b63a6ec2a6d118316fddd),
|
||
the `filter` filter will throw an error when used with a non-array. Beforehand it would silently
|
||
return an empty array.
|
||
|
||
If necessary, this can be worked around by converting an object to an array,
|
||
using a filter such as https://github.com/petebacondarwin/angular-toArrayFilter.
|
||
|
||
#### `limitTo` filter
|
||
Due to [a3c3bf33](https://github.com/angular/angular.js/commit/a3c3bf3332e5685dc319c46faef882cb6ac246e1),
|
||
the limitTo filter has changed behavior when the provided limit value is invalid.
|
||
Now, instead of returning empty object/array, it returns unchanged input.
|
||
|
||
|
||
|
||
|
||
## Migrating from 1.2 to 1.3
|
||
|
||
### Controllers
|
||
|
||
Due to [3f2232b5](https://github.com/angular/angular.js/commit/3f2232b5a181512fac23775b1df4a6ebda67d018),
|
||
`$controller` will no longer look for controllers on `window`.
|
||
The old behavior of looking on `window` for controllers was originally intended
|
||
for use in examples, demos, and toy apps. We found that allowing global controller
|
||
functions encouraged poor practices, so we resolved to disable this behavior by
|
||
default.
|
||
|
||
To migrate, register your controllers with modules rather than exposing them
|
||
as globals:
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
function MyController() {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
angular.module('myApp', []).controller('MyController', [function() {
|
||
// ...
|
||
}]);
|
||
```
|
||
|
||
Although it's not recommended, you can re-enable the old behavior like this:
|
||
|
||
```javascript
|
||
angular.module('myModule').config(['$controllerProvider', function($controllerProvider) {
|
||
// this option might be handy for migrating old apps, but please don't use it
|
||
// in new ones!
|
||
$controllerProvider.allowGlobals();
|
||
}]);
|
||
```
|
||
|
||
### AngularJS Expression Parsing (`$parse` + `$interpolate`)
|
||
|
||
- due to [77ada4c8](https://github.com/angular/angular.js/commit/77ada4c82d6b8fc6d977c26f3cdb48c2f5fbe5a5),
|
||
|
||
You can no longer invoke .bind, .call or .apply on a function in AngularJS expressions.
|
||
This is to disallow changing the behaviour of existing functions
|
||
in an unforeseen fashion.
|
||
|
||
- due to [6081f207](https://github.com/angular/angular.js/commit/6081f20769e64a800ee8075c168412b21f026d99),
|
||
|
||
The (deprecated) __proto__ property does not work inside AngularJS expressions
|
||
anymore.
|
||
|
||
|
||
- due to [48fa3aad](https://github.com/angular/angular.js/commit/48fa3aadd546036c7e69f71046f659ab1de244c6),
|
||
|
||
This prevents the use of __{define,lookup}{Getter,Setter}__ inside AngularJS
|
||
expressions. If you really need them for some reason, please wrap/bind them to make them
|
||
less dangerous, then make them available through the scope object.
|
||
|
||
|
||
- due to [528be29d](https://github.com/angular/angular.js/commit/528be29d1662122a34e204dd607e1c0bd9c16bbc),
|
||
|
||
This prevents the use of `Object` inside AngularJS expressions.
|
||
If you need Object.keys, make it accessible in the scope.
|
||
|
||
|
||
- due to [bdfc9c02](https://github.com/angular/angular.js/commit/bdfc9c02d021e08babfbc966a007c71b4946d69d),
|
||
values 'f', '0', 'false', 'no', 'n', '[]' are no longer
|
||
treated as falsy. Only JavaScript falsy values are now treated as falsy by the
|
||
expression parser; there are six of them: false, null, undefined, NaN, 0 and "".
|
||
|
||
|
||
- due to [fa6e411d](https://github.com/angular/angular.js/commit/fa6e411da26824a5bae55f37ce7dbb859653276d),
|
||
promise unwrapping has been removed. It has been deprecated since 1.2.0-rc.3.
|
||
It can no longer be turned on.
|
||
Two methods have been removed:
|
||
* `$parseProvider.unwrapPromises`
|
||
* `$parseProvider.logPromiseWarnings`
|
||
|
||
|
||
- **$interpolate:** due to [88c2193c](https://github.com/angular/angular.js/commit/88c2193c71954b9e7e7e4bdf636a2b168d36300d),
|
||
the function returned by `$interpolate`
|
||
no longer has a `.parts` array set on it.
|
||
|
||
Instead it has two arrays:
|
||
* `.expressions`, an array of the expressions in the
|
||
interpolated text. The expressions are parsed with
|
||
`$parse`, with an extra layer converting them to strings
|
||
when computed
|
||
* `.separators`, an array of strings representing the
|
||
separations between interpolations in the text.
|
||
This array is **always** 1 item longer than the
|
||
`.expressions` array for easy merging with it
|
||
|
||
|
||
|
||
|
||
### Miscellaneous AngularJS helpers
|
||
|
||
- **Angular.copy:** due to [b59b04f9](https://github.com/angular/angular.js/commit/b59b04f98a0b59eead53f6a53391ce1bbcbe9b57),
|
||
|
||
This changes `angular.copy` so that it applies the prototype of the original
|
||
object to the copied object. Previously, `angular.copy` would copy properties
|
||
of the original object's prototype chain directly onto the copied object.
|
||
|
||
This means that if you iterate over only the copied object's `hasOwnProperty`
|
||
properties, it will no longer contain the properties from the prototype.
|
||
This is actually much more reasonable behaviour and it is unlikely that
|
||
applications are actually relying on this.
|
||
|
||
If this behaviour is relied upon, in an app, then one should simply iterate
|
||
over all the properties on the object (and its inherited properties) and
|
||
not filter them with `hasOwnProperty`.
|
||
|
||
**Be aware that this change also uses a feature that is not compatible with
|
||
IE8.** If you need this to work on IE8 then you would need to provide a polyfill
|
||
for `Object.create` and `Object.getPrototypeOf`.
|
||
|
||
|
||
- **forEach:** due to [55991e33](https://github.com/angular/angular.js/commit/55991e33af6fece07ea347a059da061b76fc95f5),
|
||
forEach will iterate only over the initial number of items in
|
||
the array. So if items are added to the array during the iteration, these won't
|
||
be iterated over during the initial forEach call.
|
||
|
||
This change also makes our forEach behave more like Array#forEach.
|
||
|
||
|
||
- **angular.toJson:** due to [c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251),
|
||
`toJson()` will no longer strip properties starting with a single `$`. If you relied on
|
||
`toJson()`'s stripping these types of properties before, you will have to do it manually now.
|
||
It will still strip properties starting with `$$` though.
|
||
|
||
|
||
|
||
|
||
### jqLite / JQuery
|
||
|
||
- **jqLite:** due to [a196c8bc](https://github.com/angular/angular.js/commit/a196c8bca82a28c08896d31f1863cf4ecd11401c),
|
||
previously it was possible to set jqLite data on Text/Comment
|
||
nodes, but now that is allowed only on Element and Document nodes just like in
|
||
jQuery. We don't expect that app code actually depends on this accidental feature.
|
||
|
||
|
||
- **jqLite:** due to [d71dbb1a](https://github.com/angular/angular.js/commit/d71dbb1ae50f174680533492ce4c7db3ff74df00),
|
||
the jQuery `detach()` method does not trigger the `$destroy` event.
|
||
If you want to destroy AngularJS data attached to the element, use `remove()`.
|
||
|
||
|
||
|
||
|
||
|
||
### AngularJS HTML Compiler (`$compile`)
|
||
|
||
|
||
- due to [2ee29c5d](https://github.com/angular/angular.js/commit/2ee29c5da81ffacdc1cabb438f5d125d5e116cb9),
|
||
|
||
The isolated scope of a component directive no longer leaks into the template
|
||
that contains the instance of the directive. This means that you can no longer
|
||
access the isolated scope from attributes on the element where the isolated
|
||
directive is defined.
|
||
|
||
See https://github.com/angular/angular.js/issues/10236 for an example.
|
||
|
||
- due to [2cde927e](https://github.com/angular/angular.js/commit/2cde927e58c8d1588569d94a797e43cdfbcedaf9),
|
||
|
||
|
||
Requesting isolate scope and any other scope on a single element is an error.
|
||
Before this change, the compiler let two directives request a child scope
|
||
and an isolate scope if the compiler applied them in the order of non-isolate
|
||
scope directive followed by isolate scope directive.
|
||
|
||
Now the compiler will error regardless of the order.
|
||
|
||
If you find that your code is now throwing a `$compile:multidir` error,
|
||
check that you do not have directives on the same element that are trying
|
||
to request both an isolate and a non-isolate scope and fix your code.
|
||
|
||
|
||
- due to [eec6394a](https://github.com/angular/angular.js/commit/eec6394a342fb92fba5270eee11c83f1d895e9fb), The `replace` flag for defining directives that
|
||
replace the element that they are on will be removed in the next major AngularJS version.
|
||
This feature has difficult semantics (e.g. how attributes are merged) and leads to more
|
||
problems compared to what it solves. Also, with Web Components it is normal to have
|
||
custom elements in the DOM.
|
||
|
||
|
||
- due to [299b220f](https://github.com/angular/angular.js/commit/299b220f5e05e1d4e26bfd58d0b2fd7329ca76b1),
|
||
calling `attr.$observe` no longer returns the observer function, but a
|
||
deregistration function instead. To migrate the code follow the example below:
|
||
|
||
Before:
|
||
|
||
directive('directiveName', function() {
|
||
return {
|
||
link: function(scope, elm, attr) {
|
||
var observer = attr.$observe('someAttr', function(value) {
|
||
console.log(value);
|
||
});
|
||
}
|
||
};
|
||
});
|
||
|
||
After:
|
||
|
||
directive('directiveName', function() {
|
||
return {
|
||
link: function(scope, elm, attr) {
|
||
var observer = function(value) {
|
||
console.log(value);
|
||
};
|
||
|
||
attr.$observe('someAttr', observer);
|
||
}
|
||
};
|
||
});
|
||
|
||
- due to [531a8de7](https://github.com/angular/angular.js/commit/531a8de72c439d8ddd064874bf364c00cedabb11),
|
||
`$observe` no longer registers on undefined attributes. For example, if you were using `$observe` on
|
||
an absent optional attribute to set a default value, the following would not work anymore:
|
||
|
||
```html
|
||
<my-dir></my-dir>
|
||
```
|
||
|
||
```js
|
||
// Link function for directive myDir
|
||
link: function(scope, element, attr) {
|
||
attr.$observe('myAttr', function(newVal) {
|
||
scope.myValue = newVal ? newVal : 'myDefaultValue';
|
||
})
|
||
}
|
||
```
|
||
|
||
Instead, check if the attribute is set before registering the observer:
|
||
|
||
```js
|
||
link: function(scope, element, attr) {
|
||
if (attr.myAttr) {
|
||
// register the observer
|
||
} else {
|
||
// set the default
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
### Forms, Inputs and ngModel
|
||
|
||
- due to [1be9bb9d](https://github.com/angular/angular.js/commit/1be9bb9d3527e0758350c4f7417a4228d8571440),
|
||
|
||
|
||
If an expression is used on ng-pattern (such as `ng-pattern="exp"`) or on the
|
||
pattern attribute (something like on `pattern="{{ exp }}"`) and the expression
|
||
itself evaluates to a string then the validator will not parse the string as a
|
||
literal regular expression object (a value like `/abc/i`). Instead, the entire
|
||
string will be created as the regular expression to test against. This means
|
||
that any expression flags will not be placed on the RegExp object. To get around
|
||
this limitation, use a regular expression object as the value for the expression.
|
||
|
||
//before
|
||
$scope.exp = '/abc/i';
|
||
|
||
//after
|
||
$scope.exp = /abc/i;
|
||
|
||
|
||
- **ngModelOptions:** due to [adfc322b](https://github.com/angular/angular.js/commit/adfc322b04a58158fb9697e5b99aab9ca63c80bb),
|
||
|
||
|
||
This commit changes the API on `NgModelController`, both semantically and
|
||
in terms of adding and renaming methods.
|
||
|
||
* `$setViewValue(value)` -
|
||
This method still changes the `$viewValue` but does not immediately commit this
|
||
change through to the `$modelValue` as it did previously.
|
||
Now the value is committed only when a trigger specified in an associated
|
||
`ngModelOptions` directive occurs. If `ngModelOptions` also has a `debounce` delay
|
||
specified for the trigger then the change will also be debounced before being
|
||
committed.
|
||
In most cases this should not have a significant impact on how `NgModelController`
|
||
is used: If `updateOn` includes `default` then `$setViewValue` will trigger
|
||
a (potentially debounced) commit immediately.
|
||
* `$cancelUpdate()` - is renamed to `$rollbackViewValue()` and has the same meaning,
|
||
which is to revert the current `$viewValue` back to the `$lastCommittedViewValue`,
|
||
to cancel any pending debounced updates and to re-render the input.
|
||
|
||
To migrate code that used `$cancelUpdate()` follow the example below:
|
||
|
||
Before:
|
||
|
||
```js
|
||
$scope.resetWithCancel = function (e) {
|
||
if (e.keyCode === 27) {
|
||
$scope.myForm.myInput1.$cancelUpdate();
|
||
$scope.myValue = '';
|
||
}
|
||
};
|
||
```
|
||
|
||
After:
|
||
|
||
```js
|
||
$scope.resetWithCancel = function (e) {
|
||
if (e.keyCode === 27) {
|
||
$scope.myForm.myInput1.$rollbackViewValue();
|
||
$scope.myValue = '';
|
||
}
|
||
}
|
||
```
|
||
|
||
- types date, time, datetime-local, month, week now always
|
||
require a `Date` object as model ([46bd6dc8](https://github.com/angular/angular.js/commit/46bd6dc88de252886d75426efc2ce8107a5134e9),
|
||
[#5864](https://github.com/angular/angular.js/issues/5864))
|
||
|
||
|
||
- {@link input[checkbox] `input[checkbox]`} now supports constant expressions in `ngTrueValue` and
|
||
`ngFalseValue`, making it now possible to e.g. use boolean and integer values. Previously, these attributes would
|
||
always be treated as strings, whereas they are now parsed as expressions, and will throw if an expression
|
||
is non-constant. To convert non-constant strings into constant expressions, simply wrap them in an
|
||
extra pair of quotes, like so:
|
||
|
||
`<input type="checkbox" ng-model="..." ng-true-value="'truthyValue'">`
|
||
|
||
See [c90cefe1614](https://github.com/angular/angular.js/commit/c90cefe16142d973a123e945fc9058e8a874c357)
|
||
|
||
|
||
### Scopes and Digests (`$scope`)
|
||
|
||
- due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
|
||
Scope#$id is now of type number rather than string. Since the
|
||
id is primarily being used for debugging purposes this change should not affect
|
||
anyone.
|
||
|
||
|
||
- due to [82f45aee](https://github.com/angular/angular.js/commit/82f45aee5bd84d1cc53fb2e8f645d2263cdaacbc),
|
||
[#7445](https://github.com/angular/angular.js/issues/7445),
|
||
[#7523](https://github.com/angular/angular.js/issues/7523)
|
||
`$broadcast` and `$emit` will now reset the `currentScope` property of the event to
|
||
null once the event finished propagating. If any code depends on asynchronously accessing their
|
||
`currentScope` property, it should be migrated to use `targetScope` instead. All of these cases
|
||
should be considered programming bugs.
|
||
|
||
|
||
|
||
|
||
|
||
### Server Requests (`$http`, `$resource`)
|
||
- **$http:** due to [ad4336f9](https://github.com/angular/angular.js/commit/ad4336f9359a073e272930f8f9bcd36587a8648f),
|
||
|
||
|
||
Previously, it was possible to register a response interceptor like so:
|
||
|
||
```js
|
||
// register the interceptor as a service
|
||
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
|
||
return function(promise) {
|
||
return promise.then(function(response) {
|
||
// do something on success
|
||
return response;
|
||
}, function(response) {
|
||
// do something on error
|
||
if (canRecover(response)) {
|
||
return responseOrNewPromise
|
||
}
|
||
return $q.reject(response);
|
||
});
|
||
}
|
||
});
|
||
|
||
$httpProvider.responseInterceptors.push('myHttpInterceptor');
|
||
```
|
||
|
||
Now, one must use the newer API introduced in v1.1.4 (4ae46814), like so:
|
||
|
||
```js
|
||
$provide.factory('myHttpInterceptor', function($q) {
|
||
return {
|
||
response: function(response) {
|
||
// do something on success
|
||
return response;
|
||
},
|
||
responseError: function(response) {
|
||
// do something on error
|
||
if (canRecover(response)) {
|
||
return responseOrNewPromise
|
||
}
|
||
return $q.reject(response);
|
||
}
|
||
};
|
||
});
|
||
|
||
$httpProvider.interceptors.push('myHttpInterceptor');
|
||
```
|
||
|
||
More details on the new interceptors API (which has been around as of v1.1.4) can be found at
|
||
{@link $http#interceptors interceptors}
|
||
|
||
|
||
|
||
- **$httpBackend:** due to [6680b7b9](https://github.com/angular/angular.js/commit/6680b7b97c0326a80bdccaf0a35031e4af641e0e), the JSONP behavior for erroneous and empty responses changed:
|
||
Previously, a JSONP response was regarded as erroneous if it was empty. Now AngularJS is listening to the
|
||
correct events to detect errors, i.e. even empty responses can be successful.
|
||
|
||
|
||
- **$resource:** due to [d3c50c84](https://github.com/angular/angular.js/commit/d3c50c845671f0f8bcc3f7842df9e2fb1d1b1c40),
|
||
|
||
If you expected `$resource` to strip these types of properties before,
|
||
you will have to manually do this yourself now.
|
||
|
||
|
||
|
||
|
||
|
||
### Modules and Injector (`$inject`)
|
||
|
||
- due to [c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
|
||
|
||
Previously, config blocks would be able to control behaviour of provider registration, due to being
|
||
invoked prior to provider registration. Now, provider registration always occurs prior to configuration
|
||
for a given module, and therefore config blocks are not able to have any control over a providers
|
||
registration.
|
||
|
||
**Example**:
|
||
|
||
Previously, the following:
|
||
|
||
```js
|
||
angular.module('foo', [])
|
||
.provider('$rootProvider', function() {
|
||
this.$get = function() { ... }
|
||
})
|
||
.config(function($rootProvider) {
|
||
$rootProvider.dependentMode = "B";
|
||
})
|
||
.provider('$dependentProvider', function($rootProvider) {
|
||
if ($rootProvider.dependentMode === "A") {
|
||
this.$get = function() {
|
||
// Special mode!
|
||
}
|
||
} else {
|
||
this.$get = function() {
|
||
// something else
|
||
}
|
||
}
|
||
});
|
||
```
|
||
|
||
would have "worked", meaning behaviour of the config block between the registration of "$rootProvider"
|
||
and "$dependentProvider" would have actually accomplished something and changed the behaviour of the
|
||
app. This is no longer possible within a single module.
|
||
|
||
|
||
### Filters (`orderBy`)
|
||
|
||
- due to [a097aa95](https://github.com/angular/angular.js/commit/a097aa95b7c78beab6d1b7d521c25f7d9d7843d9),
|
||
`orderBy` now treats `null` values (which in JavaScript have type `object`) as having a string
|
||
representation of `'null'`.
|
||
|
||
|
||
### Animation (`ngAnimate`)
|
||
|
||
|
||
- due to [1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
|
||
`$animate` will no longer default the after parameter to the last element of the parent
|
||
container. Instead, when after is not specified, the new element will be inserted as the
|
||
first child of the parent container.
|
||
|
||
To update existing code, change all instances of `$animate.enter()` or `$animate.move()` from:
|
||
|
||
`$animate.enter(element, parent);`
|
||
|
||
to:
|
||
|
||
`$animate.enter(element, parent, angular.element(parent[0].lastChild));`
|
||
|
||
|
||
|
||
- due to [1bebe36a](https://github.com/angular/angular.js/commit/1bebe36aa938890d61188762ed618b1b5e193634),
|
||
|
||
Any class-based animation code that makes use of transitions
|
||
and uses the setup CSS classes (such as class-add and class-remove) must now
|
||
provide an empty transition value to ensure that its styling is applied right
|
||
away. In other words if your animation code is expecting any styling to be
|
||
applied that is defined in the setup class then it will not be applied
|
||
"instantly" unless a `transition:0s none` value is present in the styling
|
||
for that CSS class. This situation is only the case if a transition is already
|
||
present on the base CSS class once the animation kicks off.
|
||
|
||
Before:
|
||
|
||
.animated.my-class-add {
|
||
opacity:0;
|
||
transition:0.5s linear all;
|
||
}
|
||
.animated.my-class-add.my-class-add-active {
|
||
opacity:1;
|
||
}
|
||
|
||
After:
|
||
|
||
.animated.my-class-add {
|
||
transition:0s linear all;
|
||
opacity:0;
|
||
}
|
||
.animated.my-class-add.my-class-add-active {
|
||
transition:0.5s linear all;
|
||
opacity:1;
|
||
}
|
||
|
||
Please view the documentation for ngAnimate for more info.
|
||
|
||
|
||
### Testing
|
||
|
||
- due to [85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5), some deprecated features of
|
||
Protractor tests no longer work.
|
||
|
||
`by.binding(descriptor)` no longer allows using the surrounding interpolation
|
||
markers in the descriptor (the default interpolation markers are `{{}}`).
|
||
Previously, these were optional.
|
||
|
||
Before:
|
||
|
||
var el = element(by.binding('{{foo}}'));
|
||
|
||
After:
|
||
|
||
var el = element(by.binding('foo'));
|
||
|
||
Prefixes `ng_` and `x-ng-` are no longer allowed for models. Use `ng-model`.
|
||
|
||
`by.repeater` cannot find elements by row and column which are not children of
|
||
the row. For example, if your template is
|
||
|
||
<div ng-repeat="foo in foos">{{foo.name}}</div>
|
||
|
||
Before:
|
||
|
||
var el = element(by.repeater('foo in foos').row(2).column('foo.name'))
|
||
|
||
After:
|
||
|
||
You may either enclose `{{foo.name}}` in a child element
|
||
|
||
<div ng-repeat="foo in foos"><span>{{foo.name}}</span></div>
|
||
|
||
or simply use:
|
||
|
||
var el = element(by.repeater('foo in foos').row(2))
|
||
|
||
|
||
### Internet Explorer 8
|
||
|
||
- due to [eaa1d00b](https://github.com/angular/angular.js/commit/eaa1d00b24008f590b95ad099241b4003688cdda),
|
||
As communicated before, IE8 is no longer supported.
|
||
|
||
|
||
|
||
|
||
|
||
## Migrating from 1.0 to 1.2
|
||
|
||
|
||
<div class="alert alert-warning">
|
||
<p>**Note:** AngularJS versions 1.1.x are considered "experimental" with breaking changes between minor releases.
|
||
Version 1.2 is the result of several versions on the 1.1 branch, and has a stable API.</p>
|
||
|
||
<p>If you have an application on 1.1 and want to migrate it to 1.2, everything in the guide
|
||
below should still apply, but you may want to consult the
|
||
[changelog](https://github.com/angular/angular.js/blob/master/CHANGELOG.md) as well.</p>
|
||
</div>
|
||
|
||
<ul class="nav nav-list">
|
||
<li class="nav-header">Summary of Breaking Changes</li>
|
||
<li>{@link guide/migration#ngroute-has-been-moved-into-its-own-module ngRoute has been moved into its own module}</li>
|
||
<li>{@link guide/migration#templates-no-longer-automatically-unwrap-promises Templates no longer automatically unwrap promises}</li>
|
||
<li>{@link guide/migration#syntax-for-named-wildcard-parameters-changed-in-route- Syntax for named wildcard parameters changed in <code>$route</code>}</li>
|
||
<li>{@link guide/migration#you-can-only-bind-one-expression-to-src-ng-src-or-action- You can only bind one expression to <code>*[src]</code>, <code>*[ng-src]</code> or <code>action</code>}</li>
|
||
<li>{@link guide/migration#interpolations-inside-dom-event-handlers-are-now-disallowed Interpolations inside DOM event handlers are now disallowed}</li>
|
||
<li>{@link guide/migration#directives-cannot-end-with-start-or-end Directives cannot end with -start or -end}</li>
|
||
<li>{@link guide/migration#in-q-promise-always-has-been-renamed-promise-finally In $q, promise.always has been renamed promise.finally}</li>
|
||
<li>{@link guide/migration#ngmobile-is-now-ngtouch ngMobile is now ngTouch}</li>
|
||
<li>{@link guide/migration#resource-then-has-been-removed resource.$then has been removed}</li>
|
||
<li>{@link guide/migration#resource-methods-return-the-promise Resource methods return the promise}</li>
|
||
<li>{@link guide/migration#resource-promises-are-resolved-with-the-resource-instance Resource promises are resolved with the resource instance}</li>
|
||
<li>{@link guide/migration#-location-search-supports-multiple-keys $location.search supports multiple keys}</li>
|
||
<li>{@link guide/migration#ngbindhtmlunsafe-has-been-removed-and-replaced-by-ngbindhtml ngBindHtmlUnsafe has been removed and replaced by ngBindHtml}</li>
|
||
<li>{@link guide/migration#form-names-that-are-expressions-are-evaluated Form names that are expressions are evaluated}</li>
|
||
<li>{@link guide/migration#hasownproperty-disallowed-as-an-input-name hasOwnProperty disallowed as an input name}</li>
|
||
<li>{@link guide/migration#directives-order-of-postlink-functions-reversed Directives: Order of postLink functions reversed}</li>
|
||
<li>{@link guide/migration#directive-priority Directive priority}</li>
|
||
<li>{@link guide/migration#ngscenario ngScenario}</li>
|
||
<li>{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}</li>
|
||
<li>{@link guide/migration#urls-are-now-sanitized-against-a-trusted-uri-matcher URLs are now sanitized against a trusted URI matcher}</li>
|
||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
|
||
<li>{@link guide/migration#underscore-prefixed-suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
|
||
<li>{@link guide/migration#you-cannot-bind-to-select-multiple- You cannot bind to select[multiple]}</li>
|
||
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
|
||
<li>{@link guide/migration#services-can-now-return-functions Services can now return functions}</li>
|
||
</ul>
|
||
|
||
|
||
### ngRoute has been moved into its own module
|
||
|
||
Just like `ngResource`, `ngRoute` is now its own module.
|
||
|
||
Applications that use `$route`, `ngView`, and/or `$routeParams` will now need to load an
|
||
`angular-route.js` file and have their application's module dependency on the `ngRoute` module.
|
||
|
||
Before:
|
||
|
||
```html
|
||
<script src="angular.js"></script>
|
||
```
|
||
|
||
```javascript
|
||
var myApp = angular.module('myApp', ['someOtherModule']);
|
||
```
|
||
|
||
After:
|
||
|
||
```html
|
||
<script src="angular.js"></script>
|
||
<script src="angular-route.js"></script>
|
||
```
|
||
|
||
```javascript
|
||
var myApp = angular.module('myApp', ['ngRoute', 'someOtherModule']);
|
||
```
|
||
|
||
See [5599b55b](https://github.com/angular/angular.js/commit/5599b55b04788c2e327d7551a4a699d75516dd21).
|
||
|
||
|
||
### Templates no longer automatically unwrap promises
|
||
|
||
`$parse` and templates in general will no longer automatically unwrap promises.
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
$scope.foo = $http({method: 'GET', url: '/someUrl'});
|
||
```
|
||
|
||
```html
|
||
<p>{{foo}}</p>
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
$http({method: 'GET', url: '/someUrl'})
|
||
.success(function(data) {
|
||
$scope.foo = data;
|
||
});
|
||
```
|
||
|
||
```html
|
||
<p>{{foo}}</p>
|
||
```
|
||
|
||
This feature has been deprecated. If absolutely needed, it can be reenabled for now via the
|
||
`$parseProvider.unwrapPromises(true)` API.
|
||
|
||
See [5dc35b52](https://github.com/angular/angular.js/commit/5dc35b527b3c99f6544b8cb52e93c6510d3ac577),
|
||
[b6a37d11](https://github.com/angular/angular.js/commit/b6a37d112b3e1478f4d14a5f82faabf700443748).
|
||
|
||
|
||
### Syntax for named wildcard parameters changed in `$route`
|
||
|
||
To migrate the code, follow the example below. Here, `*highlight` becomes `:highlight*`
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
$routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
|
||
{controller: noop, templateUrl: 'Chapter.html'});
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
$routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
|
||
{controller: noop, templateUrl: 'Chapter.html'});
|
||
```
|
||
|
||
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
|
||
|
||
|
||
### You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
|
||
|
||
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
|
||
`src` or `action` attribute of elements.
|
||
|
||
This is one of several improvements to security introduces by AngularJS 1.2.
|
||
|
||
Concatenating expressions makes it hard to understand whether some combination of concatenated
|
||
values are unsafe to use and potentially subject to XSS vulnerabilities. To simplify the task of
|
||
auditing for XSS issues, we now require that a single expression be used for `*[src/ng-src]`
|
||
bindings such as bindings for `iframe[src]`, `object[src]`, etc. In addition, this requirement is
|
||
enforced for `form` tags with `action` attributes.
|
||
|
||
<table class="table table-bordered code-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Examples</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code><img src="{{a}}/{{b}}"></code></td>
|
||
<td class="success">ok</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code><iframe src="{{a}}/{{b}}"></iframe></code></td>
|
||
<td class="error">bad</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code><iframe src="{{a}}"></iframe></code></td>
|
||
<td class="success">ok</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
To migrate your code, you can combine multiple expressions using a method attached to your scope.
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
scope.baseUrl = 'page';
|
||
scope.a = 1;
|
||
scope.b = 2;
|
||
```
|
||
|
||
```html
|
||
<!-- Are a and b properly escaped here? Is baseUrl controlled by user? -->
|
||
<iframe src="{{baseUrl}}?a={{a}&b={{b}}">
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
var baseUrl = "page";
|
||
scope.getIframeSrc = function() {
|
||
|
||
// One should think about their particular case and sanitize accordingly
|
||
var qs = ["a", "b"].map(function(value, name) {
|
||
return encodeURIComponent(name) + "=" +
|
||
encodeURIComponent(value);
|
||
}).join("&");
|
||
|
||
// `baseUrl` isn't exposed to a user's control, so we don't have to worry about escaping it.
|
||
return baseUrl + "?" + qs;
|
||
};
|
||
```
|
||
|
||
```html
|
||
<iframe src="{{getIframeSrc()}}">
|
||
```
|
||
|
||
See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262bb43f26d47245f6c2739).
|
||
|
||
|
||
### Interpolations inside DOM event handlers are now disallowed
|
||
|
||
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers
|
||
means that the interpolated value is a JS string that is evaluated. Storing or generating such
|
||
strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other
|
||
AngularJS specific event handlers evaluate AngularJS expressions in non-window (Scope) context which
|
||
makes them much safer.
|
||
|
||
To migrate the code follow the example below:
|
||
|
||
Before:
|
||
|
||
```
|
||
JS: scope.foo = 'alert(1)';
|
||
HTML: <div onclick="{{foo}}">
|
||
```
|
||
|
||
After:
|
||
|
||
```
|
||
JS: scope.foo = function() { alert(1); }
|
||
HTML: <div ng-click="foo()">
|
||
```
|
||
|
||
See [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56).
|
||
|
||
|
||
### Directives cannot end with -start or -end
|
||
|
||
This change was necessary to enable multi-element directives. The best fix is to rename existing
|
||
directives so that they don't end with these suffixes.
|
||
|
||
See [e46100f7](https://github.com/angular/angular.js/commit/e46100f7097d9a8f174bdb9e15d4c6098395c3f2).
|
||
|
||
|
||
### In $q, promise.always has been renamed promise.finally
|
||
|
||
The reason for this change is to align `$q` with the [Q promise
|
||
library](https://github.com/kriskowal/q), despite the fact that this makes it a bit more difficult
|
||
to use with non-ES5 browsers, like IE8.
|
||
|
||
`finally` also goes well together with the `catch` API that was added to `$q` recently and is part
|
||
of the [DOM promises standard](http://dom.spec.whatwg.org/).
|
||
|
||
To migrate the code follow the example below.
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
$http.get('/foo').always(doSomething);
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
$http.get('/foo').finally(doSomething);
|
||
```
|
||
|
||
Or for IE8-compatible code:
|
||
|
||
```javascript
|
||
$http.get('/foo')['finally'](doSomething);
|
||
```
|
||
|
||
See [f078762d](https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c).
|
||
|
||
|
||
### ngMobile is now ngTouch
|
||
|
||
Many touch-enabled devices are not mobile devices, so we decided to rename this module to better
|
||
reflect its concerns.
|
||
|
||
To migrate, replace all references to `ngMobile` with `ngTouch` and `angular-mobile.js` with
|
||
`angular-touch.js`.
|
||
|
||
See [94ec84e7](https://github.com/angular/angular.js/commit/94ec84e7b9c89358dc00e4039009af9e287bbd05).
|
||
|
||
|
||
### resource.$then has been removed
|
||
|
||
Resource instances do not have a `$then` function anymore. Use the `$promise.then` instead.
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
Resource.query().$then(callback);
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
Resource.query().$promise.then(callback);
|
||
```
|
||
|
||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||
|
||
|
||
### Resource methods return the promise
|
||
|
||
Methods of a resource instance return the promise rather than the instance itself.
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
resource.$save().chaining = true;
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
resource.$save();
|
||
resource.chaining = true;
|
||
```
|
||
|
||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||
|
||
|
||
### Resource promises are resolved with the resource instance
|
||
|
||
On success, the resource promise is resolved with the resource instance rather than HTTP response object.
|
||
|
||
Use interceptor API to access the HTTP response object.
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
Resource.query().$then(function(response) {...});
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
var Resource = $resource('/url', {}, {
|
||
get: {
|
||
method: 'get',
|
||
interceptor: {
|
||
response: function(response) {
|
||
// expose response
|
||
return response;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
```
|
||
|
||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||
|
||
|
||
### $location.search supports multiple keys
|
||
|
||
{@link ng.$location#search `$location.search`} now supports multiple keys with the
|
||
same value provided that the values are stored in an array.
|
||
|
||
Before this change:
|
||
|
||
* `parseKeyValue` only took the last key overwriting all the previous keys.
|
||
* `toKeyValue` joined the keys together in a comma delimited string.
|
||
|
||
This was deemed buggy behavior. If your server relied on this behavior then either the server
|
||
should be fixed, or a simple serialization of the array should be done on the client before
|
||
passing it to `$location`.
|
||
|
||
See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe).
|
||
|
||
|
||
### ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||
|
||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
|
||
`$sanitize` before being innerHTML'd. If the `$sanitize` service isn't available (`ngSanitize`
|
||
module is not loaded) and the bound expression evaluates to a value that is not trusted an
|
||
exception is thrown.
|
||
|
||
When using this directive you can either include `ngSanitize` in your module's dependencies (See the
|
||
example at the {@link ngBindHtml} reference) or use the {@link $sce} service to set the value as
|
||
trusted.
|
||
|
||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||
|
||
|
||
### Form names that are expressions are evaluated
|
||
|
||
If you have form names that will evaluate as an expression:
|
||
|
||
```
|
||
<form name="ctrl.form">
|
||
```
|
||
|
||
And if you are accessing the form from your controller:
|
||
|
||
Before:
|
||
|
||
```javascript
|
||
function($scope) {
|
||
$scope['ctrl.form'] // form controller instance
|
||
}
|
||
```
|
||
|
||
After:
|
||
|
||
```javascript
|
||
function($scope) {
|
||
$scope.ctrl.form // form controller instance
|
||
}
|
||
```
|
||
|
||
This makes it possible to access a form from a controller using the new "controller as" syntax.
|
||
Supporting the previous behavior offers no benefit.
|
||
|
||
See [8ea802a1](https://github.com/angular/angular.js/commit/8ea802a1d23ad8ecacab892a3a451a308d9c39d7).
|
||
|
||
|
||
### hasOwnProperty disallowed as an input name
|
||
|
||
Inputs with name equal to `hasOwnProperty` are not allowed inside form or ngForm directives.
|
||
|
||
Before, inputs whose name was "hasOwnProperty" were quietly ignored and not added to the scope.
|
||
Now a badname exception is thrown. Using "hasOwnProperty" for an input name would be very unusual
|
||
and bad practice. To migrate, change your input name.
|
||
|
||
See [7a586e5c](https://github.com/angular/angular.js/commit/7a586e5c19f3d1ecc3fefef084ce992072ee7f60).
|
||
|
||
|
||
### Directives: Order of postLink functions reversed
|
||
|
||
The order of postLink fn is now mirror opposite of the order in which corresponding preLinking and compile functions execute.
|
||
|
||
Previously the compile/link fns executed in order, sorted by priority:
|
||
|
||
<table class="table table-bordered table-striped code-table">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
<th>Step</th>
|
||
<th align="center">Old Sort Order</th>
|
||
<th align="center">New Sort Order</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>Compile Fns</td>
|
||
<td align="center" colspan="2">High → Low</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td colspan="3">Compile child nodes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>PreLink Fns</td>
|
||
<td align="center" colspan="2">High → Low</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td colspan="3">Link child nodes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>PostLink Fns</td>
|
||
<td align="center">High → Low</td>
|
||
<td align="center">**Low → High**</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<small>"High → Low" here refers to the `priority` option of a directive.</small>
|
||
|
||
Very few directives in practice rely on the order of postLinking functions (unlike on the order
|
||
of compile functions), so in the rare case of this change affecting an existing directive, it might
|
||
be necessary to convert it to a preLinking function or give it negative priority.
|
||
|
||
You can look at [the diff of this
|
||
commit](https://github.com/angular/angular.js/commit/31f190d4d53921d32253ba80d9ebe57d6c1de82b) to see how an internal
|
||
attribute interpolation directive was adjusted.
|
||
|
||
See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253ba80d9ebe57d6c1de82b).
|
||
|
||
|
||
### Directive priority
|
||
|
||
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
|
||
|
||
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precedence:
|
||
|
||
|
||
Directive | Old Priority | New Priority
|
||
-----------------|--------------|-------------
|
||
ngRepeat | 1000 | 1000
|
||
ngSwitchWhen | 500 | 800
|
||
ngIf | 1000 | 600
|
||
ngInclude | 1000 | 400
|
||
ngView | 1000 | 400
|
||
|
||
See [b7af76b4](https://github.com/angular/angular.js/commit/b7af76b4c5aa77648cc1bfd49935b48583419023).
|
||
|
||
|
||
### ngScenario
|
||
|
||
browserTrigger now uses an eventData object instead of direct parameters for mouse events.
|
||
To migrate, place the `keys`,`x` and `y` parameters inside of an object and place that as the
|
||
third parameter for the browserTrigger function.
|
||
|
||
See [28f56a38](https://github.com/angular/angular.js/commit/28f56a383e9d1ff378e3568a3039e941c7ffb1d8).
|
||
|
||
|
||
### ngInclude and ngView replace its entire element on update
|
||
|
||
Previously `ngInclude` and `ngView` only updated its element's content. Now these directives will
|
||
recreate the element every time a new content is included.
|
||
|
||
This ensures that a single rootElement for all the included contents always exists, which makes
|
||
definition of css styles for animations much easier.
|
||
|
||
See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d6fe57a6c45561a05b182),
|
||
[aa2133ad](https://github.com/angular/angular.js/commit/aa2133ad818d2e5c27cbd3933061797096356c8a).
|
||
|
||
|
||
### URLs are now sanitized against a trusted URI matcher
|
||
|
||
A trusted URI matcher configured via `$compileProvider` can be used to configure what URLs are considered safe.
|
||
By default all common protocol prefixes are trusted including `data:` URIs with mime types `image/*`.
|
||
This change shouldn't impact apps that don't contain malicious image links.
|
||
|
||
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
|
||
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
|
||
|
||
|
||
### Isolate scope only exposed to directives with `scope` property
|
||
|
||
If you declare a scope option on a directive, that directive will have an
|
||
[isolate scope](https://github.com/angular/angular.js/wiki/Understanding-Scopes). In AngularJS 1.0, if a
|
||
directive with an isolate scope is used on an element, all directives on that same element have access
|
||
to the same isolate scope. For example, say we have the following directives:
|
||
|
||
```
|
||
// This directive declares an isolate scope.
|
||
.directive('isolateScope', function() {
|
||
return {
|
||
scope: {},
|
||
link: function($scope) {
|
||
console.log('one = ' + $scope.$id);
|
||
}
|
||
};
|
||
})
|
||
|
||
// This directive does not.
|
||
.directive('nonIsolateScope', function() {
|
||
return {
|
||
link: function($scope) {
|
||
console.log('two = ' + $scope.$id);
|
||
}
|
||
};
|
||
});
|
||
```
|
||
|
||
Now what happens if we use both directives on the same element?
|
||
|
||
```
|
||
<div isolate-scope non-isolate-scope></div>
|
||
```
|
||
|
||
In AngularJS 1.0, the nonIsolateScope directive will have access to the isolateScope directive’s scope. The
|
||
log statements will print the same id, because the scope is the same. But in AngularJS 1.2, the nonIsolateScope
|
||
will not use the same scope as isolateScope. Instead, it will inherit the parent scope. The log statements
|
||
will print different id’s.
|
||
|
||
If your code depends on the AngularJS 1.0 behavior (non-isolate directive needs to access state
|
||
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly:
|
||
|
||
**Before**
|
||
|
||
```
|
||
<input ng-model="$parent.value" ng-isolate>
|
||
|
||
.directive('ngIsolate', function() {
|
||
return {
|
||
scope: {},
|
||
template: '{{value}}'
|
||
};
|
||
});
|
||
```
|
||
|
||
**After**
|
||
|
||
```
|
||
<input ng-model="value" ng-isolate>
|
||
|
||
.directive('ngIsolate', function() {
|
||
return {
|
||
scope: {value: '=ngModel'},
|
||
template: '{{value}}
|
||
};
|
||
});
|
||
```
|
||
|
||
See [909cabd3](https://github.com/angular/angular.js/commit/909cabd36d779598763cc358979ecd85bb40d4d7),
|
||
[#1924](https://github.com/angular/angular.js/issues/1924) and
|
||
[#2500](https://github.com/angular/angular.js/issues/2500).
|
||
|
||
|
||
### Change to interpolation priority
|
||
|
||
Previously, the interpolation priority was `-100` in 1.2.0-rc.2, and `100` before 1.2.0-rc.2.
|
||
Before this change the binding was setup in the post-linking phase.
|
||
|
||
Now the attribute interpolation (binding) executes as a directive with priority 100 and the
|
||
binding is set up in the pre-linking phase.
|
||
|
||
See [79223eae](https://github.com/angular/angular.js/commit/79223eae5022838893342c42dacad5eca83fabe8),
|
||
[#4525](https://github.com/angular/angular.js/issues/4525),
|
||
[#4528](https://github.com/angular/angular.js/issues/4528), and
|
||
[#4649](https://github.com/angular/angular.js/issues/4649)
|
||
|
||
### Underscore-prefixed/suffixed properties are non-bindable
|
||
|
||
<div class="alert alert-info">
|
||
<p>**Reverted**: This breaking change has been reverted in 1.2.1, and so can be ignored if you're using **version 1.2.1 or higher**</p>
|
||
</div>
|
||
|
||
This change introduces the notion of "private" properties (properties
|
||
whose names begin and/or end with an underscore) on the scope chain.
|
||
These properties will not be available to AngularJS expressions (i.e. {{
|
||
}} interpolation in templates and strings passed to `$parse`) They are
|
||
freely available to JavaScript code (as before).
|
||
|
||
**Motivation**
|
||
|
||
AngularJS expressions execute in a limited context. They do not have
|
||
direct access to the global scope, `window`, `document` or the Function
|
||
constructor. However, they have direct access to names/properties on
|
||
the scope chain. It has been a long standing best practice to keep
|
||
sensitive APIs outside of the scope chain (in a closure or your
|
||
controller.) That's easier said than done for two reasons:
|
||
|
||
1. JavaScript does not have a notion of private properties so if you need
|
||
someone on the scope chain for JavaScript use, you also expose it to
|
||
AngularJS expressions
|
||
2. The new `controller as` syntax that's now in increased usage exposes the
|
||
entire controller on the scope chain greatly increasing the exposed surface.
|
||
|
||
Though AngularJS expressions are written and controlled by the developer, they:
|
||
|
||
1. Typically deal with user input
|
||
2. Don't get the kind of test coverage that JavaScript code would
|
||
|
||
This commit provides a way, via a naming convention, to allow restricting properties from
|
||
controllers/scopes. This means AngularJS expressions can access only those properties that
|
||
are actually needed by the expressions.
|
||
|
||
See [3d6a89e8](https://github.com/angular/angular.js/commit/3d6a89e8888b14ae5cb5640464e12b7811853c7e).
|
||
|
||
|
||
### You cannot bind to select[multiple]
|
||
|
||
Switching between `select[single]` and `select[multiple]` has always been odd due to browser quirks.
|
||
This feature never worked with two-way data-binding so it's not expected that anyone is using it.
|
||
|
||
If you are interested in properly adding this feature, please submit a pull request on Github.
|
||
|
||
See [d87fa004](https://github.com/angular/angular.js/commit/d87fa0042375b025b98c40bff05e5f42c00af114).
|
||
|
||
|
||
### Uncommon region-specific local files were removed from i18n
|
||
|
||
AngularJS uses the Google Closure library's locale files. The following locales were removed from
|
||
Closure, so AngularJS is not able to continue to support them:
|
||
|
||
`chr`, `cy`, `el-polyton`, `en-zz`, `fr-rw`, `fr-sn`, `fr-td`, `fr-tg`, `haw`, `it-ch`, `ln-cg`,
|
||
`mo`, `ms-bn`, `nl-aw`, `nl-be`, `pt-ao`, `pt-gw`, `pt-mz`, `pt-st`, `ro-md`, `ru-md`, `ru-ua`,
|
||
`sr-cyrl-ba`, `sr-cyrl-me`, `sr-cyrl`, `sr-latn-ba`, `sr-latn-me`, `sr-latn`, `sr-rs`, `sv-fi`,
|
||
`sw-ke`, `ta-lk`, `tl-ph`, `ur-in`, `zh-hans-hk`, `zh-hans-mo`, `zh-hans-sg`, `zh-hans`,
|
||
`zh-hant-hk`, `zh-hant-mo`, `zh-hant-tw`, `zh-hant`
|
||
|
||
Although these locales were removed from the official AngularJS repository, you can continue to
|
||
load and use your copy of the locale file provided that you maintain it yourself.
|
||
|
||
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
|
||
|
||
### Services can now return functions
|
||
|
||
Previously, the service constructor only returned objects regardless of whether a function was returned.
|
||
|
||
Now, `$injector.instantiate` (and thus `$provide.service`) behaves the same as the native
|
||
`new` operator and allows functions to be returned as a service.
|
||
|
||
If using a JavaScript preprocessor it's quite possible when upgrading that services could start behaving incorrectly.
|
||
Make sure your services return the correct type wanted.
|
||
|
||
**Coffeescript example**
|
||
|
||
```
|
||
myApp.service 'applicationSrvc', ->
|
||
@something = "value"
|
||
@someFunct = ->
|
||
"something else"
|
||
```
|
||
|
||
pre 1.2 this service would return the whole object as the service.
|
||
|
||
post 1.2 this service returns `someFunct` as the value of the service
|
||
|
||
you would need to change this services to
|
||
|
||
```
|
||
myApp.service 'applicationSrvc', ->
|
||
@something = "value"
|
||
@someFunct = ->
|
||
"something else"
|
||
return
|
||
```
|
||
|
||
to continue to return the complete instance.
|
||
|
||
See [c22adbf1](https://github.com/angular/angular.js/commit/c22adbf160f32c1839fbb35382b7a8c6bcec2927).
|