AngularJs: Notes for a better performance

Performance notes for AngularJs 1.x:

angularjs performanceThis is a compilation of few tips that you can use to improve the performance of your Angular applications. I got some of this techniques from internet and others from my personal experience when working with large enterprise AngularJs applications.

Avoid $watch

-Try to avoid as much as possible the use of $watch because Angular uses dirty checking to keep track of all the changes in app. This means it will have to go through the collection of watcher to check if they need to be updated (call the digest cycle). If one of the watcher is relied upon by another watcher, Angular would have to re-run the digest cycle again, to make sure that all of the changes has propagated. It will continue to do so, until all of the watchers have been updated and app has stabilized.

-One-time binding syntax:

In newer versions of Angular (v1.3.0-beta.10+), use the one-time binding syntax {{ ::value }} where it makes sense

//try to avoid
<div>{{ vm.title }}</div>

// recommended
<hdiv>{{ ::vm.title }}</div>

Binding once removes the watcher from the scope’s $$watchers array after the undefined variable becomes resolved, thus improving performance in each dirty-check. Make sure you use this only when you don’t need to update the value through the application life cycle.

-Debounce the ng-model

If you know there is going to be a lot of changes coming from an ng-model, you can de-bounce the input.
For example if you have a search input like Google, you can de-bounce it by setting the following ng-model option: ng-model-options=”{ debounce: 250 }.
This will ensure that the digest cycle due to the changes in this input model will get triggered no more than once per 250ms. Also you can make the model updates when an event happen, e.g. on blur: ng-model-options=”{ updateOn: ‘blur’ }”
https://docs.angularjs.org/api/ng/directive/ngModelOptions

-Limit DOM filters

Filters are really simple to use, we insert a pipe, the filter name and we’re done. However, Angular runs every single filter twice per $digest cycle once something has changed. This is some pretty heavy lifting. The first run is from the $$watchers detecting any changes, the second run is to see if there are further changes that need updated values.
Here’s an example of a DOM filter, these are the slowest type of filter, preprocessing our data would be much faster. If you can, avoid the inline filter syntax.

{{ filter_expression | filter : expression : comparator }}

Angular includes a $filter provider, which you can use to run filters in your JavaScript before parsing into the DOM. This will preprocess our data before sending it to the View, which avoids the step of parsing the DOM.

$filter('filter')(array, expression, comparator);

 

-Avoid JavaScript expensive operations

The most expensive operation in JavaScript is add or remove elements from the DOM, maybe you wont notice a performance issue when you add or remove a few element, but it could be a problem if you have to remove or insert thousands in a large application.

-How to detect bad performance in AngularJs:

  • Benchmark functions using console.time. You can use the console.time to have a rough idea of how long a function takes to execute the code, you can read more about console.time here.
  • Use Batarang extension to detect performance issues, also you can us it to debug your Angular app in general.

 


 

Tips and good practices in general:

  • Design your directives with Angular in mind, try to avoid as much as possible the use of JQuery.
  • Everything related to DOM manipulation should be in a directive.
  • Avoid use the controller to do business logic, the purpose of controllers is binding data to your view. They should not contain any logic and merely communicate with your services.
  • Use services and /or factories to do business logic and call the Apis.
  • If possible, you should consider fetching your data before your view gets initialized. By doing so, you can ensure that necessary data is available as soon as the user get’s the page, you could accomplish that by using resolve in your routingProvider. See this article.
  • Extend directives by using Directive Controllers, you can place methods and properties into a directive-controller, and access that same controller from other directives. You can even override methods and properties through this relationship.
  • Don’t wrap element inside of $(). All AngularJS elements are already jq-objects.
  • Don’t do if (!$scope.$$phase) $scope.$apply(), it means your $scope.$apply() isn’t high enough in the call stack. Best Practice to run the cycle digest
  • Don’t create a new plugin without trying to discover, fork and pull request existing plugins first.