thumbnail
Technology | 8 MIN READ

Angular Performance Tips and Tricks

Angular performance:With the advent of Angular 2 and React, there is a lot of backlash against Angular on performance. While some of them are justified, a lot can be done to make Angular UI peppy.Having built many complex front-ends with Angular, I wanted to share a few things we learnt in the trenches. Also, some of the advice out there on tuning Angular is out-dated after the 1.3/4 release. So here is the latest on how to get the best performance out of Angular!Tools:As with any performance exercise, don’t start anything without profiling. Premature optimization is the root of all evil – Donald Knuth.


The tools we found most useful are:

Chrome profiler:
Timeline Tab:

See exactly what your application does in a given timeframe. Tracks resource loading, javascript parsing, style calculation, and repainting.

2.Timeline

Pro tip:
Chrome profiler output is very noisy. For Angular related timing, use the “Flame chart”. Here you can see digest cycle timings, ng-repeat bottlenecks etc. This is super useful.

Flame-chart

Batarang (AngularJS browser plugin):This is from the Angular team that helps in profiling and debugging. Pay attention to the Models and Performance tab. But beware its a bit slow and doesn’t handle large applications well.

1.Angular-batarang

ng-stat:
This is a simple yet useful tool.Once you measure and understand your current performance, plan your goals. When setting goals, instead of focussing just on raw times, do consider perceived performance. In UI, perceived performance is more important than actual performance.The perceived page performance is how long the user thinks the major elements of the page took to load. By definition it is highly subjective – some users may think that the page is loaded just because the initial furniture appears. But for most users this will be the parts of the page they consider most important.Is it Angular, really?Before we blame Angular, let’s make sure the problem is not else where. Take care of non-Angular performance items before tackling Angular. For instance, fix your APIs. Implement all the YSlow principles.Now that you have taken care of all that, let’s look at Angular-specific items.Before we beginA good understanding of how Angular implements 2-way binding via its dirty checking mechanism in the digest loop is imperative to understanding many of the performance strategies. If they are not already familiar with this, a quick primer here.Let us recall a couple of facts. Anytime a value changes, all watchers tied to the scope are evaluated. This is a multi-pass evaluation to account for values changed in the loop itself.
So, the time taken by the digest loop has a direct impact on the performance. Number of watchers, time taken by the watch expression and the scope in which the loop runs are important.

Tip 1:
Optimize watchers on the digest loop.Angular performance is greatly impacted by the number of watcher expressions and the complexity of expressions. The rule of thumb is, at around 2000 expressions a page begins to lag in performance.
New comers to Angular are surprised by the number of watchers as they don’t realize the implicit watcher Angular adds. When a $watch is called on a scope value, or a value is bound from the DOM with interpolation, a ng-repeat, a ng-switch, and ng-if, or any other DOM attribute/element, a function gets added to the $$watchers array of the innermost scope. All these watchers quickly add up.So here are a few things you can do to improve performance:o Decrease the number of watchers by avoiding long lists with pagination or infinite scroll.
o Remove or disable watchers as soon as possible using the

unbind function.var unregisterFn = $scope.$watch(‘name’, function(){/*expr*/}); unregisterFn();

o Keep the watcher functions simple. Remember that these functions are evaluated in every run.
o Don’t observe function results directly.
o Call back functions attached to watchers should not change its own or other watch values. This is to avoid digest cycle running again. The cycle runs till all values are returned not dirty in a single run.

Tip 2:
Bind once when possible.From Angular 1.3, this feature is available. For cases where the values are static, once bound to the UI, we can use this. Angular will bind once and then remove the watcher.Instead of

<p>Hello {{name}}!</p>do<p>Hello {{::name}}!</p>

We can use this in any type of Angular expression like ng-repeat.

Tip 3:
Beware of $apply.Usually, when a value changes, only the watch list associated with that scope will be evaluated. But if we use $apply, the digest cycle starts with the root scope.You do need $apply while changing values outside of Angular context and want to bring it back. Even here, if your use case needs changes to be propagated only in the current scope and downward use local events facility provided by https://github.com/ansukla/ng-perfUsing this you can trigger $digest instead of $apply. This is very useful when you are building UI components like auto-complete where the event needs to be applied only to the current directive.

Tip 4:
Debounce ng-model.This is available since Angular 1.3.
With this we can tell Angular to wait for a certain amount of time and when the current operation is in progress. For example, in type ahead fields instead of triggering digest cycle for every key stroke, we can delay it by 500 ms.

<div ng-controller=”UserController”>
<form name=”userForm”> Name: <input type=”text” name=”userName” ng-model=”user.name” ng-model-options=”{ debounce: 500 }” />
<button ng-click=”userForm.userName.$rollbackViewValue(); user.name=””> Clear </button> <br />
</form> <pre>user.name =</pre>
</div>

https://docs.angularjs.org/api/ng/directive/ngModelOptions

Tip 5:
Disable debug info in production.Since 1.3, we can use the following snippet to significantly boost the performance of load time by disabling debug information.config

([‘$routeProvider’, ‘$compileProvider’, function($routeProvider, $compileProvider) { //configure routeProvider as usual $compileProvider.debugInfoEnabled(false); }]

Tip 6:
Avoid DOM filtersAvoid DOM filters which are slow, instead, pre-process data. Angular provides the $filter with which we can run filters in Javascript before passing it to the DOM. This will pre-process the data before binding it to the view which avoids a step of passing the DOM.Instead of{{ filter_expression | filter : expression : comparator }}do$filter(‘filter’)(array, expression, comparator);

Tip 7:
ng-show instead of ng-ifBoth Usually and ng-if hide an element from view if a given condition is true. But they do this is very different fashion.Ng-show/ng-hide do this by using display: name CSS style.Ng-if actually removes the element if the condition is false and adds it back when the condition is true. The implication of this is all the watchers associated with this element are also removed from the watch list.
So use ng-show if
o if you hide/show the element frequently
o if your initialization logic is heavyUse ng-if
o hide/show rarely
o a lot of watchers associated with this element

Tip 8:
Avoid frequent reflows / repaints / parseHTMLUsually the browser applies style and layout changes in batches since DOM operations are expensive. Lagging is caused if by constant repainting, which appears when batching is not possible.

Possible solutions:Write inline templates in your directive’s template propertyPrefill angular’s build-in $templateCache with your templates. When you require an external template, AngularJS first checks the cache. There’s also a handy grunt task which automatically minifies and combines your HTML templates and prefills the AngularJS $templateCache. (https://github.com/ericclemmons/grunt-angular-templates)

Tip 9:
$watchCollection instead of $watch where applicableAvoid deep checking with$watch(‘value’, function(){}, true).Use$watchCollection(‘value’, function(){})if you need only the first layer of the object’s properties to be checked.Reference: https://code.angularjs.org/1.3.15/docs/api/ng/type

Tip 10:
Keep the size of the object returned from server small.It is appealing from the design perspective to return JSON representation of an entire model object to a view. But for better performance consider creating custom serializers to return a partial representation of a model.

     @JsonIgnoreProperties({ "username", "email", "phonePrefix", "phoneNumber", "language", "address", "enabled", "verified", "emailVerified", "phoneVerified", "timeZone", "gender", "agreedPrivacyTC", "hasReadPrivacyTC", "phoneNos", "emailIds", "fullTimeZone", "fullCountryName", "loginCount", "devicePhoneNumber",                       "phoneVerifiedBy", "emailVerifiedBy", "haveVerifyCode", "enableTravelPlan", "noOfContacts", "noOfRegContacts", "noOfChats", "noOfEvents",               "noOfEventsInvite", "languageName", "dateFormat", "aliasPhoneNumber", "conflict", "conflictMessage", "accountVerifiedOn", "isMobileUser", "phoneConflict", "createdOn", "modifiedOn", "dateOfBirth", "deviceType", "lastLoginTime", "version", "emailVerifiedOn", "phoneVerifiedOn" }) public User getUser() { return user; }

For instance, in an event management system that we developed, we need to display a bunch of events on a page. Each event displayed needs a few attributes from the user model. We retrieve only these attributes instead of the whole user object with an annotation like this.Tip 11: Go outside AngularNot everything needs to be Angular. For things that don’t work well in the Angular context like heavy DOM manipulation, consider using Javascript using a directives link function.Hope this helps.
Do share your bag of tips and tricks in the comments section.About us:Ideas2IT is a high-end Product Development firm with a strong front-end development practice. Check out our work here. Angular, React, Ionic, Knockout, whatever is your choice, we can help you develop, test and performance tune your application front-end. Let us know if we can  help.





Leave a Reply

Your email address will not be published. Required fields are marked *

SHARE linkedin twitter