Angular 2 *ngFor performance issue with select/option

5.8k views Asked by At

I'm using *ngFor to populate options of <select></select>. My issue is that I can have 1000 array values for 1 of my 6 <select>. Performance is suffering. I know it's because of changeDetection or One-way Binding. Is there a way to better implement *ngFor for <option>. I don't actually need change detection or one-way binding.

My Code

<select [formControl]="requestForm.controls['SellCommodityId']">
   <option [value]="" disabled selected>COMMODITY/GRADE</option>
   <option [value]="item.Id" *ngFor="let item of fieldOptions?.Product;">{{item.Value}}</option>                
</select>

UPDATE 12-20-2016

I placed the select into it's one Component like this:

import { Component, ChangeDetectionStrategy,Input } from '@angular/core';
/**
 * <ihda-select [list]="list" [control]="control" [titleOption]="title"></ihda-select>
 */
@Component({
  selector:'ihda-select',
  changeDetection:ChangeDetectionStrategy.OnPush,
  template:`
    <select [formControl]="control" class="form-control">
       <option [value]="" disabled selected>{{titleOption}}</option>
       <option [value]="item.Id" *ngFor="let item of list">{{item.Value}}</option>                
     </select>
  `,
  styleUrls: ['../app.component.css']
})
export class IHDASelect{
    @Input() list: any[];
    @Input() control: any;
    @Input() titleOption: string;
}

Performance issue persisted.

It seems like it wasn't the changeDetection, because i tried removing the [formControl] attribute from the <select> and then there was no longer a performance issue. It seems that using the [formControl] attribute here to track it for the form group causes the performance issue. Is there information about this? or how I may fix it?

UPDATE 12-21-2016

Performance Issue shown in the plunker here:

https://plnkr.co/edit/2jNKFotBRIIytcmLto7y?p=preview

If you click the Options Route it will take a long time to load the options. If you remove the form code the route doesn't take a long time to load.

2

There are 2 answers

13
Günter Zöchbauer On BEST ANSWER

When the Options route is selected all <options> for all <select> are rendered in one go before the application responds again.

To avoid that the rendering of the <options> could be delayed so that they are only rendered on demand

<select [formControl]="control" class="form-control" (focus)="hasFocus = true">
   <option [value]="" disabled selected></option>
   <ng-container *ngIf="hasFocus">
     <option [value]="item.id" *ngFor="let item of list">{{item.value}}</option>                
   </ng-container>
</select>

https://plnkr.co/edit/KRgYHktFXHyPugS3nPQk?p=preview

The strategy can be further optimized so that hasFocus is set to true delayed by a timer after the component is already shown for one <select> at a time, so that while the browser is idle it already renders the <option>s.

5
Milad On

If your current component's change detection is not OnPUsh , you could wrap this select inside a component and make that ones to be OnPush and the pass the Product list to it to be used .

@Component({

  selector:'my-select',
  changeDetection:ChangeDetectionStrategy.OnPush,
  inputs:['list','control'],
  template:`
    <select [formControl]="control">
       <option [value]="" disabled selected>COMMODITY/GRADE</option>
       <option [value]="item.Id" *ngFor="let item of list">{{item.Value}}</option>                
     </select>

  `
})

export class MySelectCompoent{

}

Then to use it :

   <my-select [control]='requestForm.controls['SellCommodityId']' [list]='fieldOptions?.Product'></my-select>

Then who ever is supplying the list , if want's to update it , should replace it.