in Development

Angular 1.4 Breaking Changes to Be Aware Of

The Angular 1.4 release introduced a lot of breaking changes. You might have had trouble upgrading your 1.3 apps (if you even decided to try).

There’s a good run-down on how to migrate your code in the Angular docs. We’ll cover some of the same ground here but also dive into some extra approaches for adjusting your code, as well as some cases I’ve run into where I’ve had to support both 1.3 and 1.4.

Jump to a section to read the changes.

 

$animate

JavaScript and CSS animations can no longer be run in
parallel.

In older versions, if both JS and CSS animations were detected they would be run at the same time. This won’t happen automatically any more, but you can use $animateCss to create CSS-based animations in your code.

 

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.

I ran into this guy when trying to upgrade UI-Grid to Angular 1.4. The fix is to just swap the order of your parameters, unless you have to support multiple versions. In that case you’ll have to do some fun (not) version checking:

 

In addition to disabling the children of the element,
$animate.enabled(element, false) will now also disable animations on
the element itself.

Just something to keep in mind. It’s unlikely that you were implicitly relying on this, but it could bite you.

 

Animation-related callbacks are now fired on
$animate.on instead of directly being on the element.

If you were listening for events on your element with something like  element.on('$animate:before', ...) you’ll need to switch to $animate.on(element, 'enter', ...).

 

There is no need to call $scope.$apply or $scope.$digest inside of an animation promise callback anymore since the promise is resolved within a digest automatically (but a digest is not run unless the promise is chained).

Yay! This makes things a bit cleaner. It wouldn’t really hurt to keep any remaining $timeouts in your animation callbacks, but you don’t need them to trigger a $digest anymore.

 

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.

This will probably help you out by not having multiple animations running when you don’t intend them.

 

Prior to this fix there were two ways to apply CSS animation code to an anchor animation. With this fix, the suffixed CSS -anchor classes are now not used any more for CSS anchor animations.

Animation anchoring lets you pair up elements that are in different structural parts of the application, like views, so that it appears that a single element is transitioning. You can read about it and see a demo in the Angular docs.

With this change you can just specify an .ng-anchor CSS class for your element (like .my-element.ng-anchor rather than having to make -anchor-suffixed extra rules.

Also note that you can use .ng-anchor-in and .ng-anchor-out if you want to target those phases of the animation.

 

If your CSS code made use of the ng-animate-anchor
CSS class for referencing the anchored animation element then your
code must now use ng-anchor instead.

They just renamed the class. If you want to support both 1.3.x and 1.4.x you could add an extra rule for.ng-anchor anywhere you have .ng-animate-anchor.

 

Partially or fully using a regex value containing
ng-animate as a token is not allowed anymore. Doing so will trigger a
minErr exception to be thrown.

This refers to using the classNameFilter() method in $animateProvider; ng-animate is essentially a reserved word. You can’t do this:

but you could do this if you wanted to:

 

 

$animateCss

The $animateCss service will now always return an object even if the animation is not set to run.

You won’t need to check and see if $animateCss(element, { ... }); has returned anything any more.

 

 

$cookies

$cookies no longer exposes properties that represent the current browser cookie values. Now you must explicitly the methods described above to access the cookie values. This also means that you can no longer watch the $cookies properties for changes to the browser’s cookies.

The $cookies API originally polled for changes, synchronizing the local set with the browser. This was expensive and had out-of-sync issues.

Now you need to use the API methods to access the cookies:

 

 

filter

Previously, the filter was not applied if used with a non array. Now, it throws an error. This can be worked around by converting an object to an array, using a filter such as https://github.com/petebacondarwin/angular-toArrayFilter

The “filter” filter would fail silently if you used it on an object. As explained in the initial bug report  this would fail silently:

You’d just see stuff repeated twice in your DOM with no explanation.

With 1.4.0 you’ll now get an error that looks like this: Expected array but received: {“display”:false,”foo”:”bar”}. Using PBD’s filter is a good way to make sure you’re using the proper object type with filter.

 

 

$http

transformRequest functions can no longer modify request headers … This behavior was unintended and undocumented, so the change should affect very few applications

The $http property  transformRequest can be used to alter a given request’s http request body. If you want to modify headers, the changelog says you can do it in your $http request with a headers function:

Or if you want more global alteration you can modify $httpProvider‘s header defaults:

 

 

limitTo

limitTo changed behavior when limit value is invalid.
Instead of returning empty object/array it returns unchanged input.

The limitTo filter creates a subset of an array, much like slice in vanilla JS. If you supply a bad value to limitTo, like a string or NaN, you’ll now get the unaltered array back rather than an empty array.

 

 

ngMessages

The ngMessagesInclude attribute is now its own directive and that must
be placed as a child element within the element with the ngMessages
directive.

Angular 1.3 introduced a nicer way to display form validation messages called ngMessages. You can read a nice intro in Pascal Precht’s blog post.

When displaying validation messages you would often need to use the same message over and over. This could lead too a less than DRY situation in your app. ngMessageInclude was a way to write a template for a validation message once and then include it in multiple places. It still exists; it’s just a directive instead of an attribute now:

You can read more about the change in Pasca’s follow-up on ngMessages.

 

 

ngOptions

When using ngOptions: the directive applies a surrogate key as the value of the <option> element. This commit changes the actual string used as the surrogate key. We now store a string that is computed by calling hashKey on the item in the options collection; previously it was the index or key of the
item in the collection.

This shouldn’t have too much effect on you, as you shouldn’t be relying on ngOptions‘ surrogate key. If you do then you’ll want to either add a track by expression to control how the surrogate key is defined, or just access the property that the select’s ng-model  is bound to.

Be Aware: If you were relying on the actual value of a select box in your unit tests, you’ll need to adjust for this. We ran into this with UI-Grid. Instead of $('.my-select-box').val() returning 5, you’ll get "number:5".

 

When iterating over an object’s properties using the (key, value) in obj syntax
the order of the elements used to be sorted alphabetically. … 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).

If you were relying on this implicit ordering in ngOptions you’ll need to add a sorting filter.

 

Although it is unlikely that anyone is using it in this way, this change does change the behaviour of ngOptions in the following case:

  • You are iterating over an array-like object, using the array form of the ngOptions syntax (item.label for item in items) and that object contains non-numeric property keys.

In this case these properties with non-numeric keys will be ignored.

Hopefully you would not treat yourself (or your coworkers) so villainously, but there’s probably at least one person out there who has, at least unintentionally. If you find code with arrays that have extra properties and the repeater is iterating over them in this way, change it to the object iterator syntax: value.label for (key, value) in items.

 

 

ngRepeat

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.

This is the same as the breaking change to ngOptions above. If you were relying on implicit sorting in ngRpeat when using an object as the source, then you’ll need to add your own sorting filter. Otherwise the elements in your repeater will come out in whatever order the user’s browser decides on.

Be Aware: The changelog says “…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.”