Angular mat-select 通过 ngFor 的索引 属性 绑定到数组的元素会导致不良行为

Angular mat-select binded to array's elements by ngFor's index property causes undesirable behaviour

有问题的代码ts部分:

    import { Component } from '@angular/core';
    import { FormControl } from '@angular/forms';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html'
    })
    export class AppComponent {
      constructor() {}
      fields = [null, null];
      countries: any = [
        {
          full: 'Great Britain',
          short: 'GB'
        },
        {
          full: 'United States',
          short: 'US'
        },
        {
          full: 'Canada',
          short: 'CA'
        }
      ];
    }

有问题的代码html部分:

    <div *ngFor="let f of fields; let i = index">
      <mat-form-field>
        <mat-select name="countryString" [(ngModel)]="fields[i]" placeholder="Country">
          <mat-option *ngFor="let item of countries" [value]="item.short">{{item.full}}</mat-option>
        </mat-select>
      </mat-form-field>
    </div>

Here 是我在 stackblitz 上的例子。

基本思路如下:

当我们想要 select 第一个元素时,问题就开始了,因为当您选择它时,第二个组件也会采用 属性。有趣的是,数组的第二个元素没有得到值,但是绑定的 mat-select 组件切换到那个元素。

如您所见,我写了一个没有 ngFor 部分的工作版本,它的工作方式就像一个魅力,所以我的问题是这里可能有什么问题?是material设计缺陷还是我犯了一些错误?

发生这种情况是因为 ngFor 认为数组中的项目是相同的,并且正在比较它们以找到它应该替换哪个模板。因此,当值相同时,指令会为所选值标记出新模板并替换两次出现的值,因为该值是唯一需要比较的东西。 您可以尝试使用数组中的对象,这样模板就不会像这样重新呈现:

  fields = [{
    value: null
  }, {
    value: null
  }];
<div *ngFor="let f of fields; let i = index">
  <mat-form-field>
    <mat-select name="countryString" [(ngModel)]="fields[i].value" placeholder="Country">
      <mat-option *ngFor="let item of countries" [value]="item.short">{{item.full}}</mat-option>
    </mat-select>
  </mat-form-field>
</div>

这样 ngFor 中的对象总是相同的,避免了模板的重新渲染,但允许您更改该索引处的特定值。

或者使用trackBy并使用索引来跟踪模板:

  trackBy(index, val) {
    return index
  }
<div *ngFor="let f of fields; let i = index; trackBy: trackBy">

示例here