Angular – Speed up filtering and display of *ngFor

Tags: , , , ,



I have a list of users (at the moment about 450) which I filter using a onValueChanges subscription and javascript filter method. It seems pretty slow. I believe it should be quicker but am unsure if it is the actual filtering of the object or the rerendering the html that is slow.

When I recreated it in stackBlitz it is super fast so am not sure why it is slow to show the filtered items in my project.

The actual object is little different but not by much

address: null
emailAddress: "fred.bloggs@example.com"
employeeId: 1
extraPhoneNumber: null
fullName: "Administrator Account"
jobTitle: "Learning Administrator"
personId: 52
phoneNumber: "01234567890"
photograph: null
searchTerms: "Administrator Account, 1, Learning Administrator"
totalAlerts: 0
totalCompleted: 0
totalMandatory: 0
totalOverdue: 0
totalToDo: 0

My Html

<form class="" [formGroup]="myForm">
   <input type="text" class="input learningItemsSearch" placeholder="Search My Team"
     name="searchString" formControlName="searchString" />
</form>
 
<cdk-virtual-scroll-viewport itemSize="70" class="viewport">
 <div *ngFor="let user of filteredUsers">
            <table>
                <tbody>
                    <tr>
                        <td colspan="3">
                            {{ user.fullName }}
                        </td>
                        <td colspan="3">
                            {{ user.jobTitle }}
                        </td>
                        <td colspan="3">
                                {{ user.emailAddress }}
                        </td>
                        <td colspan="3">
                               {{ user.phoneNumber }}
                       </td>      
                  </tr>
             </tbody>
         </table>
    </div>
 </cdk-virtual-scroll-viewport>

My Component

this.myForm.valueChanges.pipe(
  debounceTime(400),
  distinctUntilChanged(),
  tap((value: any) => {
      let searchTerm = value.searchString;
      this.filteredUsers = this.users.filter((userName) =>
        userName.searchTerms
          .toLowerCase()
          .indexOf(searchTerm.toLowerCase()) !== -1)
  })
).subscribe( );

Is this the most efficient way to filter an array of objects? Is there another reason why it appears to take some time to display on the screen? There is nothing too complicated in the html. I am using a cdk-virtual-scroll to see if it would load quicker but it is slow without it too.

StackBlitz here

EDIT: I forgot to use *cdkVirtualFor instead of *ngFor. It is a lot quicker now. I think adding the trackby mentioned below has also helped.

Answer

You may see a rendering speedup by using the trackBy feature https://angular.io/api/core/TrackByFunction. Add the following function to your component:

public trackById = (_: number, item: any) => item.id; // or userId, whatever is the unique identifier

Then modify your template:

<div *cdkVirtualFor="let user of filteredUsers; trackBy:trackById">


Source: stackoverflow