Angular mat-select, привязанный к элементам массива с помощью свойства index ngFor, вызывает нежелательное поведение
Проблемная часть кода 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>
Вот мой пример на stackblitz.
Основная идея заключается в следующем:
- есть массив строк
- изначально массив состоит из фиксированного количества элементов, но с нулевым значением
- мы хотим придать значение элементам, поэтому мы перебираем их и привязываем к компоненту выбора мата
Проблема начинается, когда мы хотим выбрать первый элемент, потому что, когда вы выбираете его, второй компонент также принимает свойство. Интересно то, что второй элемент массива не получает значение, но связанный компонент выбора матов переключается на этот элемент.
Как вы можете видеть, я написал рабочую версию без части ngFor, и она работает как шарм, поэтому мой вопрос в том, в чем может быть проблема? Это недостаток материального дизайна или я ошибаюсь?
1 ответ
Это происходит потому, что 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">
Пример здесь