January 1, 2024

How to optimize performance of large lists in angular

When dealing with extensive lists or tables in Angular, optimizing performance becomes crucial to ensure a smooth user experience. In this post we will show you different ways to optimize the performance of large lists in Angular. We will be covering low hanging fruits, that are fairly easy to implement, two lazy loading strategies and two advanced rendering techniques.

The following steps should clean up your code and increase the performance of your grid measurably.

Low hanging fruits for optimize performance of large lists

1. Change detection strategy: OnPush

Most of the time, the change detection of Angular feels like a nice magic, that makes your life much easier. If you have a large app with a big amount of values though, you will realize that change detection is a complex thing, that can make your app a bit slow sometimes.

If a value in your template is changed, Angular checks each value in the view for updates. Doing this Angular compares the old value with a supposed new value. Depending on the amount of data this can take a lot of time. To fix this problem, it is possible to change the change detection strategy of Angular on the component level.

After this change, Angular only goes for a deep check if the input (@Input) reference of a value has changed. Otherwise Angular will only do a references check. If the references of the variable change, Angular will compare both values like mentioned before.

Now you have to care to use immutable objects. Make sure to always create a new object instead of manipulating the old one, so the object gets a new reference and the change detection will work as expected. Using the OnPush strategy should significantly reduce the change detection calls.

Angular also provides a way to trigger the change detection programmatically. It might be useful to trigger the change detection every few seconds programmatically, instead of triggering the change detection automatically every time a value has changed.

Therefore take a look at this snippet:

For more information read here.

2. The updateOn property

Each FormGroup has a property called updateOn. This property determines when the form validation for the current form group is called. Possible values are change (default), submit and blur.

  • default: Form validation is always called when the value has changed
  • blur: Form validation is called when you leave the input field
  • submit: Form validation is called when you submit the form

We change the updateOn property to blur and as result after leaving the input field the change detection is called the first time. Otherwise Angular calls the form validation every time we make changes inside the input field. Depending on the use case of the application the submit or blur property could be the better choice and will offer always a better performance.

3. Where to attach form validators

The construction of form validation for a table is based on one parent form group containing one form group for each row in the table.

Pay attention that you attach the validation function to the hierarchically lowest point possible. For example, you have a field with the name age which cannot be zero amongst other input fields inside a form group. You should attach your custom form validator only to the age field and not to the whole form group it belongs to.

The required function only runs if the value of the age field changes. If we instead attach the validator to the whole group, the form validation function is called when any value of the form group changes.

4. Pure pipes instead of function calls

Sometimes you might want to manipulate data in the template directly. We want to avoid unnecessary calls, so we should choose to implement a pipe instead of calling a function inside the template.

Angular just calls pure pipes if the reference (Objects or Arrays) or the value (string, number or boolean) of the current field changes.

If you use a function or an impure pipe, they will calculate a value in every change detection lifecycle.

Check this out for a detailed explanation and a clean example.

5. The trackBy function and Angular 17’s track expression

For example you have a dropDown list with a lot of items (using *ngFor). Now you are constantly adding and removing items to the list. Angular always renders the full list, because Angular doesn’t know which item has changed. To avoid unnecessary rendering you can use the trackBy function.

If you want to read more about trackBy click here.

Since loops iterating over immutable data often lead to performance issues, using a track expression has become a mandatory practice for @for loops as of Angular 17.

6. Always unsubscribe

Always make sure to unsubscribe to avoid memory leaks. For example if you use a subscription for the valueChanges of your form group, pay attention to unsubscribe at the latest in the ngOnDestroy function. If you don’t, Angular will not throw away the subscription until all subscribers are unsubscribed. You can also use a library, that takes care about it.

Lazy loading strategies to optimize large list performance

Due to their lazy nature these two methods are some of the most effective ways to optimize the performance of large lists in Angular.

1. Virtual scrolling

Angular provides the opportunity to render only the part of the table, that is currently in view. This way the application needs less resources than for rendering the full table and more resources are available to calculate other stuff.

See our full implementation here.

2. Infinite scrolling

Virtual scrolling only loads the elements, that are currently visible in the viewport. This comes at the cost of previously loaded elements having to be reloaded when you scroll up again. For this use case we have infinite scrolling. Infinite scrolling loads the list elements laziliy. So if you want to keep the already loaded elements in the DOM, infinite scrolling is your method. One famous example are the rabbit holes of Facebook, Instagram or Youtube that seem to deliver infinite content. The application loads the content when you reach a certain threshold on the bottom of the loaded list.

Check out the package ngx-infinite-scroll for an Angular implementation for infinite scrolling.

Advanced list performance optimization approaches

If you are not satisfied yet, let’s dive into some really advanced approaches to optimize performance of large lists in angular. Whereas the upper solutions mostly rely on tools delivered by angular or angular-material libraries, the following approaches require you to take a look under the hood of the framework. They may not be practicable in most scenarios, but might save your day when there is a very tricky performance problem.

1. Using RxFor

RxFor is a part of the rx-angular/template package and offers a concurrent strategy for rendering child templates. This approach has a lot of benefits compared to the synchronous nature of *ngFor which can block the user experience of your application especially with big data sets or complex templates.

2. Building a custom NgForOf directive

In some cases, *ngForOf can cause unneccessary DOM updates by recreating whole components and not just updating the template internals only. You can resolve this by creating a tailored NgForOf directive with an _applyChanges method where DOM updates are handled in a different way. Read here for details on the implementation.

Conclusion

So as you can see, there are several ways to optimize the performance of large lists in Angular. Which one you want to use depends on your specific requirements and context.

Related Posts

Lino Fischer
Developer at thecodecampus </>


Leave a Reply

Add code to your comment in Markdown syntax.
Like this:
`inline example`

```
code block
example
```

Your email address will not be published.