Angular Material Autocomplete方法调用次数异常问题咨询
isAccountingTypeSelected called 1428 times instead of 51 times in Angular Material Autocomplete? Hey there! Let's break down why your method is getting called way more often than you expected, and how to fix it.
The Root Cause
This happens because of two key factors: Angular's change detection system and the internal behavior of Angular Material's Autocomplete component:
Angular's Change Detection Cycle
Angular re-evaluates every expression in your template during every change detection cycle. Since you're callingisAccountingTypeSelected(code)directly in the[class.selected]binding, this method runs once per list item every time change detection triggers.Autocomplete's Frequent State Changes
The MatAutocomplete component triggers change detection multiple times during its lifecycle:- When the async pipe emits new values from
filteredAccountingTypes - When the autocomplete panel opens/closes
- When the input field is updated (even if filtering doesn't change the list)
- When
selectedAccountingTypesis modified - Even small UI interactions like hovering or scrolling can trigger cycles
- When the async pipe emits new values from
With 51 list items and 28 change detection cycles, you end up with 51×28=1428 method calls.
Fixes to Reduce Unnecessary Calls
1. Precompute Selected States (Best Practice)
Instead of calling a method in the template, pre-calculate the selected status for each item when your data loads. This way, you're just referencing a property in the template, not running a function every cycle.
Update your component code:
import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; // Add a new observable that includes selected status filteredAccountingTypesWithSelection$: Observable<(AccountingType & { isSelected: boolean })[]>; ngOnInit() { this.filteredAccountingTypesWithSelection$ = this.filteredAccountingTypes.pipe( map(accountingTypes => accountingTypes.map(type => ({ ...type, isSelected: this.selectedAccountingTypes.some(at => at.code === type.code) })) ) ); }
Then update your template:
<mat-option *ngFor="let accountingType of filteredAccountingTypesWithSelection$ | async" [value]="accountingType.code"> <span class="accounting-type-options" [class.selected]="accountingType.isSelected"> {{ accountingType.name + ' (' + accountingType.code + ')' }} </span> </mat-option>
2. Use OnPush Change Detection
Limit when change detection runs for your component by setting ChangeDetectionStrategy.OnPush. This tells Angular only to run change detection when input properties change or internal events are triggered.
Add this to your component decorator:
import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'your-component-selector', templateUrl: './your-component.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class YourComponent { // ... rest of your code }
3. Cache Results (Quick Fix)
If you need to keep the method call in the template, cache the selected codes to avoid re-running the find operation every time:
private selectedCodes = new Set<string>(); ngOnInit() { this.updateSelectedCodes(); // If selectedAccountingTypes changes dynamically, call updateSelectedCodes() whenever it updates } private updateSelectedCodes() { this.selectedCodes.clear(); this.selectedAccountingTypes.forEach(at => this.selectedCodes.add(at.code)); } isAccountingTypeSelected(code: string): boolean { return this.selectedCodes.has(code); }
This turns the expensive find operation into a fast Set lookup, even if the method is called multiple times.
内容的提问来源于stack exchange,提问作者Pritam Bohra

