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 组件
当我们想要 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
有问题的代码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 组件
当我们想要 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