You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中*ngFor遍历函数返回对象数组导致MatSelect页面冻结问题

Angular Material MatSelect 使用函数返回对象数组导致应用冻结问题

问题描述

在使用Angular Material的MatSelect组件时,尝试用函数返回的对象数组作为*ngFor的迭代数据源,结果不仅功能失效,还导致Web应用冻结,控制台无任何报错信息。

Angular Material官网正常示例

HTML文件

<h4>基础下拉选择框</h4>
<mat-form-field appearance="fill">
  <mat-label>喜爱的食物</mat-label>
  <mat-select>
    <mat-option *ngFor="let food of foods" [value]="food.value">
      {{food.viewValue}}
    </mat-option>
  </mat-select>
</mat-form-field>

TS文件

import {Component} from '@angular/core';

interface Food {
  value: string;
  viewValue: string;
}

/**
 * @title 基础选择框
 */
@Component({
  selector: 'select-overview-example',
  templateUrl: 'select-overview-example.html',
})
export class SelectOverviewExample {
  foods: Food[] = [
    {value: 'steak-0', viewValue: '牛排'},
    {value: 'pizza-1', viewValue: '披萨'},
    {value: 'tacos-2', viewValue: '塔可'},
  ];
}

失效尝试场景(应用冻结)

HTML文件

<h4>基础下拉选择框</h4>
<mat-form-field appearance="fill">
  <mat-label>喜爱的食物</mat-label>
  <mat-select>
    <mat-option *ngFor="let food of data()" [value]="food.value">
      {{food.viewValue}}
    </mat-option>
  </mat-select>
</mat-form-field>

TS文件

import {Component} from '@angular/core';

interface Food {
  value: string;
  viewValue: string;
}

/**
 * @title 基础选择框
 */
@Component({
  selector: 'select-overview-example',
  templateUrl: 'select-overview-example.html',
})
export class SelectOverviewExample {
  foods: Food[] = [
    {value: 'steak-0', viewValue: '牛排'},
    {value: 'pizza-1', viewValue: '披萨'},
    {value: 'tacos-2', viewValue: '塔可'}
  ];

  data(): Food[] {
    return [
      {value: 'steak-0', viewValue: '牛排'},
      {value: 'pizza-1', viewValue: '披萨'},
      {value: 'tacos-2', viewValue: '塔可'}
    ];
  }
}

补充信息

若函数返回字符串数组则可正常运行,示例如下:

HTML文件

<h4>基础下拉选择框</h4>
<mat-form-field appearance="fill">
  <mat-label>喜爱的食物</mat-label>
  <mat-select>
    <mat-option *ngFor="let food of data()" [value]="food">
      {{food}}
    </mat-option>
  </mat-select>
</mat-form-field>

TS文件

import {Component} from '@angular/core';

interface Food {
  value: string;
  viewValue: string;
}

/**
 * @title 基础选择框
 */
@Component({
  selector: 'select-overview-example',
  templateUrl: 'select-overview-example.html',
})
export class SelectOverviewExample {
  foods: Food[] = [
    {value: 'steak-0', viewValue: '牛排'},
    {value: 'pizza-1', viewValue: '披萨'},
    {value: 'tacos-2', viewValue: '塔可'},
  ];

  foods2 = ['牛排', '披萨', '塔可'];

  data(): string[] {
    return ['牛排', '披萨', '塔可'];
  }
}

问题原因

Angular的变更检测机制会在每次检测周期调用模板中的data()函数。当函数返回对象数组时,每次调用都会创建全新的对象引用,Angular会判定数据持续变化,进而触发无限循环的变更检测,最终导致应用资源耗尽而冻结。

而返回字符串数组时,虽然每次调用也会创建新数组,但字符串是值类型,Angular能识别到数组内的实际值未发生变化,不会触发无限检测循环,因此可以正常运行。

解决方案

方案1:缓存数组到组件属性

将函数返回的数组缓存到组件的类属性中,模板直接引用该属性,避免每次变更检测都调用函数创建新对象:

// TS文件修改
export class SelectOverviewExample {
  // 缓存函数返回的数组
  dataArray: Food[] = this.data();

  foods: Food[] = [
    {value: 'steak-0', viewValue: '牛排'},
    {value: 'pizza-1', viewValue: '披萨'},
    {value: 'tacos-2', viewValue: '塔可'}
  ];

  data(): Food[] {
    return [
      {value: 'steak-0', viewValue: '牛排'},
      {value: 'pizza-1', viewValue: '披萨'},
      {value: 'tacos-2', viewValue: '塔可'}
    ];
  }
}
<!-- HTML模板修改 -->
<mat-option *ngFor="let food of dataArray" [value]="food.value">
  {{food.viewValue}}
</mat-option>

方案2:使用OnPush变更检测策略

给组件设置changeDetection: ChangeDetectionStrategy.OnPush,仅当输入属性变化或组件内部触发变更时才执行检测,减少不必要的检测循环:

import {Component, ChangeDetectionStrategy} from '@angular/core';

@Component({
  selector: 'select-overview-example',
  templateUrl: 'select-overview-example.html',
  changeDetection: ChangeDetectionStrategy.OnPush // 添加此行
})
export class SelectOverviewExample {
  // ... 其余代码不变
}

方案3:使用纯管道封装函数逻辑

将数据生成逻辑封装成纯管道,纯管道仅在输入值变化时才重新计算,避免重复执行:

// 创建纯管道
import { Pipe, PipeTransform } from '@angular/core';
import { Food } from './select-overview-example';

@Pipe({
  name: 'foodData',
  pure: true // 默认就是true,可显式声明
})
export class FoodDataPipe implements PipeTransform {
  transform(): Food[] {
    return [
      {value: 'steak-0', viewValue: '牛排'},
      {value: 'pizza-1', viewValue: '披萨'},
      {value: 'tacos-2', viewValue: '塔可'}
    ];
  }
}
<!-- 模板中使用管道 -->
<mat-option *ngFor="let food of [] | foodData" [value]="food.value">
  {{food.viewValue}}
</mat-option>

内容的提问来源于stack exchange,提问作者LPZadkiel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:45:29