@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.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.
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.
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))
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)
### Core: _Directives_ #### **form**: **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`. #### **input[type=number]**: **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; } } }; }) ``` #### **input[type=radio]**: **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 ``` If your code relied on the non-strict comparison, you need to convert the values so that they continue to match with strict comparison. #### **input[type=range]**: **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). #### **ngBind**: **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 [object Object] {'a':1,'b':2} ``` Object with custom `toString()`: ```html [object Object] a+b ``` If you want the output of `toString()`, you can call it manually on the value in `ngBind`: ```html [object Object] ``` #### **ngModel**: **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(); }) ```
**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. #### **ngModelOptions**: **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'); ``` #### **ngTransclude**: **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 ``` #### **select**: **Due to [f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622)**, using `ngValue` on `