To make a demo of the problem I have a table where I show a list of details and several mat-select
that when selecting an option the "size" of this marked option is loaded in the td.
The problem is that it does not load the "size" in the same row where an option is selected. How I can get this? The size is shown in the td but in order from top to bottom.
For example if there is no option marked and I select an option from row 3. The "size" of this option would be loaded in the first row and if I mark an option in any other row the data is loaded following the order of the rows.
I have the demo in stackblitz: DemoStackblitz
HTML
<table class="table">
<tr>
<th>Titles</th>
<th>Size</th>
<th>Detail</th>
<th>Duration</th>
</tr>
<tr *ngFor="let row of dataDetails let i = index">
<td>
<mat-form-field appearance="outline">
<mat-select [formControl]="dataCtrl" placeholder="Select" [compareWith]="compareItems" #singleSelect>
<mat-option>
<ngx-mat-select-search [formControl]="dataFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let op of filteredData | async" [value]="op.id" (click)="onSelect(op)"
[disabled]="op.condition===1">
{{op.title}}
</mat-option>
</mat-select>
</mat-form-field>
</td>
<td>{{OptionSelected[i]?.size}}</td>
<td>{{row.detail}}</td>
<td>{{row.duration}}</td>
</tr>
</table>
TS
dataTitles: any = DataTitles;
dataDetails: any = DataDetails;
dataCtrl: FormControl = new FormControl();
dataFilterCtrl: FormControl = new FormControl();
filteredData: ReplaySubject<any> = new ReplaySubject<any>(1);
@ViewChild('singleSelect', { static: true }) singleSelect: MatSelect;
_onDestroy = new Subject<void>();
ngOnInit() {
this.dataCtrl.setValue(this.dataDetails[0]);
this.filteredData.next(this.dataTitles.slice());
this.dataFilterCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterData();
});
}
compareItems(i1: any, i2: any) {
return i1 && i2 && i1.key === i2.key;
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
OptionSelected: any = [];
onSelect(option: any) {
var objIndex = this.dataTitles.findIndex(x => x.id == option.id);
if (this.dataTitles[objIndex].title !== 'None') {
this.dataTitles[objIndex].condition = 1;
}
if (this.OptionSelected.length === 0) {
this.OptionSelected.push({ id: option.id, size: option.size });
} else {
let check = this.OptionSelected.map((item: any) => item.id).includes(
option.id
);
if (check === false) {
this.OptionSelected.push({ id: option.id, size: option.size });
}
}
console.log(this.OptionSelected);
}
filterData() {
if (!this.dataTitles) {
return;
}
let search = this.dataFilterCtrl.value;
if (!search) {
this.filteredData.next(this.dataTitles.slice());
return;
} else {
search = search.toLowerCase();
}
this.filteredData.next(
this.dataTitles.filter(
(x: any) => x.title.toLowerCase().indexOf(search) > -1
)
);
}
Issue
You´r pushing the current selected value into the OptionSelected
during the call of
onSelect
. This causes the mismatch of selected row data and which row actualy shows the data.
Pseudo code example:
Select TitleA in Row 4
Push new Data into OptionSelected
OptionSelected[0]?.size displays data in the first row
Solution
You are already tracking the value of the current selected value with the template variable #singleSelect
over here:
<mat-select ... #singleSelect>
The <mat-select>
component has a value input.
@Input()
value: any Value of the select control.
So you can display the current value by simply using the template ref:
<td>{{singleSelect.value}}</td>