Search code examples
htmlangulartypescriptmat-select

Load data in a td of the same row where an option is marked


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.

table

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
      )
    );
  }

Solution

  • 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>
    

    Stackblitz example