DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Frameworks
  4. Improve Front-End Performance Using Angular Change Detection Strategy

Improve Front-End Performance Using Angular Change Detection Strategy

Learn how to improve your performance using the change detection functionality that comes with Angular out of the box.

han huynh user avatar by
han huynh
·
Feb. 12, 19 · Tutorial
Like (5)
Save
Tweet
Share
14.87K Views

Join the DZone community and get the full member experience.

Join For Free

Angular change detection helps us reflect our component data to the user. If you don't use it properly, you can easily hurt your front-end performance.

In this post, I'll use Chrome Dev Tools for measuring the performance.

What We Have Now

We have an app component with a drop-down list to change the number of items per page. Inside the app component, we have a membership component to display a list of our users and their level of membership.

App component

The membership component will receive input data (a variable named "members") from the app component and display it. This is the high-level code for our demonstration application:

Component-level code

The function heavyCalculation inside of the calcMemberShipLevel function will simulate each membership-level calculation and take 10ms to finish. In a real use case, it can be a heavy calculation or make you end up waiting for the result to be returned from other sources.

Address the Problem

Let's open the console and open the drop-down list:

Image title

You see the function gets called many times, even though we still have not selected an item in this dropdown list.

Even worse, if I choose to display five items per page, the performance becomes really bad. As shown below, it gets only 4FPS and that function takes more than 200ms to finish.

Image title

Explaining Our Problem

Angular change detection will be triggered when there's an event that happens, like a mouse click, and it will check all individual components, even though there's no change in the data. As in our case, we click on the drop-down list and the function gets called because Angular cannot detect whether the result of thecalcMemberShipLevel function is changed until it runs the function.

Solution

First Attempt

Now we'll try to make the change detection on the membership component only get triggered when it gets new input data.

With OnPush, change detection will be triggered when the component gets new input. 'New' here means a new reference to a data object.

With that in mind, let's open the membership component and add a setting for the OnPush strategy as shown in line 4 below:

 @Component({
  selector: "app-membership",
  templateUrl: "./membership.component.html",
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ["./membership.component.scss"]
})

Next, I'll use the Immutable library to accomplish this step. The Immutable library helps us simplify the process of copying new objects and add memory optimization by reusing the data without completely copying all the data to the new list. For more about this library, check it out at its home page: Immutable.

Firstly, we'll declare our variable with the List type from Immutable:

import { List } from 'immutable';
...
members :List ;
membersInCurrentPage: List;  

Then we're able to use methods from the List type as shown in line 3:

changeNumberOfItem(itemPerPage: number) {
    this.itemPerPage = itemPerPage;
    this.membersInCurrentPage = this.members.setSize(itemPerPage);
 }

The setSize method returns a new List with size as its input. Now, the change detection only gets triggered when we actually change the data of the list. It's time to check the performance:

Image title

You'll see that calcMemberShipLevel only gets called after we click and it takes more than 100ms to finish; the FPS rate increases to 7 (almost 50% improved), but it's still really bad.

Still, there's one more technique we can apply to improve it.

Second Attempt

If our function is pure, which means it has one input, we'll always get the same returned result. We can apply Angular Pipe to cache the result next time we use it; this will help us save a lot of effort. For more about Pipe, check it out in my another post here.

Now we’ll create a pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'calcMemberShipLevel',
  pure: true
})
export class CalcMemberShipLevelPipe implements PipeTransform {
  transform(value: number, args?: any): any {
    return this.getMemberShipLevel(value);
  }
  getMemberShipLevel(point: number): String{
    console.info("---calcMemberShipLevel runs---");
    this.heavyCalculation(10);    
    if(point > 900){
      return 'Platinum';
    }else if(point > 700){
      return 'Gold';
    }else if(point > 500){
      return 'Silver';
    }
    return 'Basic';
  }
}

And use it as in line 7:

<tbody>
      <tr *ngFor="let member of members; index as i">
        <th scope="row">{{ i + 1 }}</th>
        <td>{{ member.firstName }}</td>
        <td>{{ member.lastName }}</td>
        <td>{{ member.point }}</td>
        <td>{{ member.point | calcMemberShipLevel }}</td>
      </tr>
</tbody>

Let's check our performance again:

Angular Performance

Now with the result in cache, our performance is really improved as in picture 45 FPS.

That's really cool!

Conclusion

We used OnPush change detection to make Angular change detection only get triggered when needed.

We used Pipe to cache the result of our function in the template if it’s a pure function.

Last, but not least, always remember to measure to make sure your performance doesn't hurt the user experience.

Don't forget to check out the source code here.

AngularJS

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Internal Components of Apache ZooKeeper and Their Importance
  • Application Assessment Questions for Migration Projects
  • A ChatGPT Job Interview for a Scrum Master Position
  • Build CRUD RESTful API Using Spring Boot 3, Spring Data JPA, Hibernate, and MySQL Database

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: