How to get the best performance out of your Angular apps

How to get the best performance out of your Angular apps

<strong>Angular is a great framework and can be used for developing large scale applications, but can be tricky to fine tune and achieve good load time and run-time performance. In this post, I’ll detail some best practices I have learned along the way, so you will not make the same mistakes I made.</strong>

Angular is a great framework and can be used for developing large scale applications, but can be tricky to fine tune and achieve good load time and run-time performance. In this post, I’ll detail some best practices I have learned along the way, so you will not make the same mistakes I made.

Change detection

Change detection is Angular’s mechanism to see if there are any values that have been changed and require the view to be updated. By default, Angular runs change detection with nearly every user interaction. In order to know if the view should be rendered again, Angular accesses the new updated value, compares it with the old one and makes the decision. As your application grows, it will include a lot of expressions, and having a change detection cycle on each one of them will cause a performance problem.

We can optimize things if we create a ‘dumb’ component with a certain attribute to handle the change detection cycles. This component relies only on non specific input data and in that way, we can tell Angular only to run change detection when an input changes or when we manually trigger it. When a reference type is immutable, every time the object is being updated, the reference on the stack memory will have to change. Now we can have a simple reference check on the object between the memory address and the stack. If the memory address has changed, then we check all the values. This will skip change detection in that component.

We need to keep in mind that primitive types such as numbers, booleans, strings, etc are passed by value. Objects, arrays and functions are also passed by value, but the value is a copy of a reference.

You can look for more details on that here.

And now we will see two examples of how this is implemented.

Example: ChangeDetectionStrategy.Default

You don’t have to specify changeDetection type, it will be ‘ChangeDetectionStrategy.Default’ by default.

@Component({
  selector: 'app',
  template: `
  <person-details [person]="person"></person-details>
  <button type="button" (click)="changeProperties()">
      Change Properties
  </button>
  <button type="button" (click)="changeObject()">
      Change Object
  </button>
  `
})

export class AppComponent { person: Person = new Person('John', 'Lennon');

changeProperties(): void { this.person.firstName = 'George'; this.person.lastName = 'Harrison'; }

changeObject(): void { this.person = new Person('Paul', 'McCartney'); } } export class Person { constructor(public firstName: string, public lastName: string) { } } @Component({ selector: 'person-details', template: &lt;p&gt; &lt;label&gt;Person: &lt;/label&gt; &lt;span&gt;{{person.firstName}} {{person.lastName}}&lt;/span&gt; &lt;/p&gt;, })

export class PersonDetailsComponent { @Input() person: Person; }

ChangeDetectionStrategy.OnPush

In order to use the OnPush change detection, we need to modify the child component from the first example.

import { Component, Input, ChangeDetectionStrategy} from '@angular/core';
import {Person} from '../models/person';
@Component({
  selector: 'person-details',
  templateUrl: './personDetails.component.html',
  styleUrls: [ './personDetails.component.css' ],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonDetailsComponent  {
  @Input() person: Person;
}

Minimize DOM manipulations

If you have some list of data that was retrieved from some server and you need to show it, you are probably using the Angular directive, ngFor. Angular will create a new template for you for each item in that list.

If at some point some of the data has been changed, Angular can’t really know that and will replace the whole list, instead of just the items that were changed. In order to improve that, Angular provide us with the trackByfunction.trackBy takes a function which has two arguments: index and [item](https://angular.io/api/core/IterableChangeRecord#item). If trackBy is given, Angular tracks changes by the return value of the function.

Syntax:

<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>

Most common use is just to return the index itself or item.id as a unique identifier for the item: trackByFn(index, item){ return item.id; }.

With that, Angular can track which items have been added or removed according to the unique identifier and create or destroy only the things that were changed.

Avoid using methods in your template

While it is very convenient to use methods in Angular templates, Angular will recalculate them on each change detection. For larger lists it will affect rendering time and the application may even get stuck due to huge memory consumption. In the following example, Angular will run getNumberOfCars on each change detection cycle (ie upon adding a new row).

How can we handle this situation? We can pre-compute the results and then just access the data we have computed. In our example, we can add a new attribute to the person object, vehiclesNumber, which holds in advance the amount of vehicles each person has. The other way to do this is by implementing the method getNumberOfCars as a pure pipe.

According to Angular pipe documentation:

Angular executes a pure pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (StringNumberBooleanSymbol) or a changed object reference (DateArrayFunctionObject).
Angular ignores changes within (composite) objects.
This may seem restrictive but it’s also fast. An object reference check is fast — much faster than a deep check for differences — so Angular can quickly determine if it can skip both the pipe execution and a view update.

The pipe will still be executed on each change detection cycle. However, if a pipe is executed more than once with the same parameters, the results of the first execution are returned. Meaning, Angular will cache the results for better performance.

Let’s see an example.

Without a pipe:

import { Component } from '@angular/core';
import { Person } from '../models/person';
@Component({
  selector: 'my-app',
  template:   &lt;button (click)="addPerson()"&gt;add person&lt;/button&gt;     &lt;ul&gt;       &lt;li *ngFor="let person of personsList"&gt;           &lt;span&gt; Name: {{person.firstName}} {{person.lastName}},&lt;/span&gt;           &lt;span&gt; Number of cars: {{getNumberOfCars(person)}}&lt;/span&gt;       &lt;/li&gt;     &lt;/ul&gt;    
})
export class AppComponent {
  personsList: Person[] = [
    {
      firstName: 'John',
      lastName: 'Lennon',
      vehicles: ['Austin maxi', '1979 Mercedes-Benz 300TD Wagon', '1965 Rolls-Royce Phantom V', '1956 Bentley S1']
    },
    {
      firstName: 'George',
      lastName: 'Harisson',
      vehicles: ['McLaren F1', 'Mini Cooper S', 'Ford Anglia']
    }
  ];

addPerson(): void { const person: Person = { firstName: 'Paul', lastName: 'McCartney', vehicles: ['Radford Mini Cooper S', 'Lamborghini 400GT', 'Austin Healy'] } this.personsList.push(person) } getNumberOfCars(person: Person): number { console.log("calculating number of cars") return person.vehicles.length; } }

While with a pipe we will get:

@Pipe({ name: 'carsCounter' })
export class CarsCounter implements PipeTransform {
  transform(person: Person) {
    console.log("transforming..")
    return person.vehicles.length;
  }
}
@Component({
  selector: 'my-app',
  template:   &lt;button (click)="addPerson()"&gt;add person&lt;/button&gt;     &lt;ul&gt;       &lt;li *ngFor="let person of personsList"&gt;           &lt;span&gt; Name: {{person.firstName}} {{person.lastName}},&lt;/span&gt;           &lt;span&gt; Number of cars: {{person | carsCounter}}&lt;/span&gt;       &lt;/li&gt;     &lt;/ul&gt;    
})

We can see it will recalculate only on the new data, instead of the whole list.

Use Prod flag in production

It will disable Angular’s development mode, which turns off assertions and other checks within the framework. This will also increase your performance. You can find more details here.

Don’t use console.log in production code

console.log prints can really slow down your application, as it takes some time to compute what you want to print. Also, for long information it will also consume some more time for the printing process.

Don’t forget to unsubscribe from your observables

Your subscription holds a reference to your component instance. If you will not unsubscribe from it, the instance will not be cleared by the garbage collector which will cause a memory leak. You can unsubscribe easily by using ngOnDestory(){this.subscription.unsubscribe();}. You can read more about it here.

Final Words

If you run into any issues, feel free to drop me a line at : markgrichanik[at]gmail[dot]com.

I would also love to hear any feedback/tips you have while working on large scale applications with Angular.

Originally published by Mark Grichanik at https://medium.freecodecamp.org

Learn more

☞ Angular 7 (formerly Angular 2) - The Complete Guide

☞ The Complete Angular Course: Beginner to Advanced

☞ NativeScript + Angular: Build Native iOS, Android & Web Apps

☞ Ionic 4 Crash Course with Heartstone API & Angular

☞ Angular 6 , Angular 7 Step by Step for beginners

☞ NgRx In Depth (Angular 7 and NgRx 7, with FREE E-Book)

☞ Learn Protractor(Angular Automation) from scratch +Framework

angular mobile-apps

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How To Succeed In Mobile App Wireframe Design?

This article covers everything about mobile app wireframe design: what to do and what not, tools used in designing a mobile or web app wireframe, and more.

Mobile App Development North Carolina

Mobile App Development North Carolina In the era of globalization, technology has forced the businesses and industries to jump into the space of competition. Technology has both tangible and intangible benefits that help businesses from different ind...

Top 7 Mobile App Development Companies in Delhi NCR

Looking for a Mobile app development company in Delhi NCR? Here it a list of top mobile app development companies in Delhi for Android & iOS app Development.

ECommerce Mobile App Development | Ecommerce Mobile App Development Services

We are leading ecommerce mobile application development company. Hire our ecommerce mobile app developer for your custom Ecommerce project at competitive rates. **Know about [Top ECommerce Mobile App Development...

Best Mobile App Development Company | Android and iOS Apps

iPrism Tech is a one of the best and offshore mobile app development company in India, Saudi Arabia and USA. We are a major providers of android, iphone and ipad mobile app development services at economical prices.