AngularJS Performance Tuning in Enterprise Applications
Introduction
Before developing any application, it is always important to use the technology stack in a beneficial way. Using a technology doesn’t count until and unless we use it effectively.
Let’s see some of the common performance issues caused by AngularJS and their possible suggestions to fix or ways to avoid them.
Problem Faced or What Went Wrong During Implementation of Good Practices
From my past learnings and while working on AngularJS projects, I came to know few lags which I was making. This blog cover certain performance issues and possible suggestions to fix them.
Problem Area 1: parseHTML
The style and layout changes are applied by the browser in small batches. When batching is not possible, lagging is caused.
Too many DOM operations are costly.
Possible Solution Area 1: parseHTML
- We must write inline templates in directive’s template property.
- Angular’s built-in $templateCache must be prefilled with our own templates. Whenever we need an external template, the cache is checked by angular. Many grunt tasks automatically combines and minifies the HTML templates and prefills the AngularJS $templateCache
Problem Area 2: Slow dirty checking
There are two things in Dirty Checking:
- Recognizing changes
- Reacting to changes
We register too many watchers and the complexity of values being watched is high, hence recognizing changes can be prolonged. Also, we do too much in watch operation that reacting to changes can be very slow.
Possible Solution Area 2: Slow dirty checking
- Use unregister function to unregister all the watches after using the watcher:
var unregisterFunction = $scope.$watch(‘namehere’, function(){expression});
unregisterFunction (); - $watch function with 2 parameters is fast. A 3rd parameter to $watch is also supported by Angular: $watch(‘val’, function(){}, true). This 3rd parameter lets Angular perform deep checking, i.e checking each and every property of the object. This could be much more expensive hence causing performance issue.
- To solve this issue, $watchCollection(‘val’, function(){}) is added by Angular. This method is almost similar to $watch with 3 parameters, except that it checks only the first layer of object’s properties. This improves the performance to a great extent.
- Use isolated scopes in directives and pass only necessary values/models.
Recalculate and cache complex model changes and apply them to watch expression. - Pre-filter large collections and use the result as a source for ng-repeat. Only deep-watch if required. Deep-watching uses angular. Equals on each property.
Problem Area 3: Many DOM operations
Accessing the DOM is expensive-
angular.element('div.elementClass')
Therefore, one should work using as few DOM nodes as possible.
For further reading, please see-
Mastering Web Application Development with AngularJS Chapter 11
Possible Solution Area 3: Many DOM operations
- Wherever possible, keep the DOM tree small.
- If possible, avoid DOM modification and do not set inline styles.
- Use ng-if instead of ng-show/ng-hide.
The ng-show directive toggles CSS display property on an element, whereas the ng-if directive removes the element from DOM and reconstructs it if required.
ng-show/ng-hide-
<span ng-show='Age==18`></span> <span ng-hide='Age<18`></span>
ng-if-
<span ng-if=’Age==18`> </span>
Problem Area 4: Code Duplication
For similar functionalities, we are in the habit of copy & paste the code.
Possible Solution Area 4: Code Duplication
- Use custom directives
With Directives, we can create custom HTML tags, classes or attributes to execute required functionality on an HTML section. Then it becomes a reusable and independent component which can be embedded in HTML pages.
myApp.directive(‘myDirective’, function() { return { restrict: ‘E’, template: ‘<h1>I made a directive!</h1>’ }; }); <body ng-app=“myApp”> <my-directive></my-directive> </body>
For further reading, please see-
https://dzone.com/articles/angularjs-10-best-practices
- There should be unique responsibilities for each task in a controller.
Standard code should be written up to a level that no if -else is required in case of any modification required in a module
Problem Area 5: Memory Leaks in Angular
Whenever we have a reference in a child scope to a higher node, a reference is maintained and all the subtrees are kept alive. This creates a memory leak. If we register a listener on a service or on a DOM node that is not being deleted, it creates a risk of memory leaks.
Memory leaks lead to performance problems as it slows down the applications and can cause process termination. In large applications, this is not at all good.
The most common reason for the memory leak in Angular is jQuery usage in the directives. When we attach an event-listener in the directive using a jQuery plugin, it keeps a reference to DOM even if Angular deletes its own reference to the DOM. This means that it would never be garbage-collected by the browser, i.e., “Detached DOM tree” in the memory.
Possible Solution Area 5: Memory Leaks in Angular
- Remove bindings to avoid memory leaks.
Always unbind the jQuery event in the directive. Use $destroy method to clean up DOM bindings before removing an element from the DOM.
$scope.$on("$destroy",function() { $(window).off("resize.Viewport"); });
- Always cancel $timeout timers in $destroy Events.
$scope.$on("$destroy",function(event) { $timeout.cancel(timer); });
Other Workable Solutions
- Avoid filter on ng-repeat-
Filters are very easy to use- insert a pipe, the filter name and it’s done. However, Angular runs every filter 2 times per $digest cycle. Once whenever anything changes and another time to collect other changes. They do not really remove the collection from memory, instead only mask filtered items with CSS. This leads to heavy lifting.
Instead of using a filter in ng-repeat –
<ul> <li ng-repeat=”data in bookData | filter:’A’”>{{ data }}</li> </ul>
Use the $filter service in controller-
$filter provider should be used to run filters before parsing into the DOM. This pre processes the data before sending it to View, which saves the parsing of the DOM and knowing the inline filter syntax.
// controller
$scope.filteredWords = $filter('filter')($scope,bookData, 'A');
// HTML
<ul> <li ng-repeat=”data in filteredWords”>{{ data }}</li> </ul>
- Use pagination or infinite scrolling for lists (with ng-repeat).
Infinite scroll is a separate module of angular that you can use to implement infinite scrolling in AngularJS applications.
<div ng-app='myApp' ng-controller='MyController'> <div infinite-scroll='loadMore()' infinite-scroll-distance='2'> <img ng-repeat='img in images' ng- src='http://placehold.it/225x250&text={{img}}'> </div> </div> var myApp = angular.module('myApp', ['infinite-scroll']); myApp.controller('MyController', function($scope) { $scope.images = [1, 2, 3, 4, 5, 6, 7, 8]; $scope.loadMore = function() { var last = $scope.images[$scope.images.length - 1]; for(var i = 1; i <= 8; i++) { $scope.images.push(last + i); } }; });
Benefits
Quantitative Benefits
Performance optimizations always bear the risk of increasing code complexity and therefore decreasing maintainability, by optimization we will be making AngularJS as a primary choice for development.
It encourages an individual to deal with models not only on the server side but also on the client side. Angular JS enables you to create software more quickly and with less effort. It has a 2-way data-binding, so saving it to the server now takes a smaller number of lines, unlike jQuery that requires creating your own object, and several different click and event handlers.
The Dependency injection and the ability to create directives in AngularJS has really excited the developer community. These features let developers create and share components very easily.
Qualitative Benefits
Angular is next generation framework where every tool was designed to work with all other tools in an interconnected manner. It is not merely bundling of existing tools, but the plugins are designed sophisticatedly to meet the purpose of each tool used.
1.) Data models in Angular are plain old JavaScript objects (POJO) which do not need irrelevant setter and getter functions. One can add and change properties directly on it and loop over objects and arrays. Due to this, the code looks more intuitive and much cleaner.
2.) Improve the testability of the software – AngularJS has dependency injection at its core, which makes it easy to test. Even the documentation on the site of the portal has testing as a part of every tutorial step, which almost makes it hard NOT to test.
3.) This allows us to keep in mind the same object-oriented design principles that in general makes the software more maintainable compared to procedural which results in a software that is more maintainable.
Learning/ Improvements
Main application module should be in the root client directory. A module should not be altered apart from the one where it is defined because a module should be reliable if anyone wants to include it as a reusable component.
When writing web applications, we have objects at server-side that at times aren’t represented as objects at the client-side. This might be OK for simple sites, but if it gets complicated, it can be an immense help to mirror these objects on both sides.
It gives elasticity with filters. Filters filter the data before reaching the view and can include something as simple as reversing the order of an array, formatting decimal places on a number, implementing pagination or filtering an array based on a parameter.
In applications like jQuery etc., the view presents data by modifying the DOM and adds behavior by manipulating it. With Angular, code for DOM manipulation must be inside directives and not inside view. Angular looks at the view just like any other HTML page with placeholders for data. This pairs perfectly with UI designers.
Applicability to Other Projects
Projects that are using AngularJS for the development of UI will meet these scenarios, by keeping our model clean and minimum number of watchers we will be able to optimize the performance with great ease. All the mentioned problems/solutions are applicable for any kind of AngularJS project and are handy for getting things done in one go.