Search code examples
angularangular7mat-tableangular-material-7

How to select one cell in mat-table (Angular 7)


I'm using angular materials mat-table for displaying data. Well, when you click on one cell, an input field is displaying and the span-tag hides.

But in my case, every cell in this row is displaying an input field as you can see on the screenshot:

enter image description here

My ngIf-Statement looks as follows:

Shows span-tag: !editable || (selectedRowIdx !== idx)

Shows input-tag: editable && (selectedRowIdx == idx)

<ng-container matColumnDef="TYPE">
  <mat-header-cell *matHeaderCellDef> TYPE </mat-header-cell>
  <mat-cell *matCellDef="let elem; let idx = index" (click)="testFocusIn(elem.TYPE)">
    <span *ngIf="!editable || (selectedRowIdx !== idx)">{{elem.TYPE}}</span>
    <mat-form-field *ngIf="editable && (selectedRowIdx == idx)"> 
      <input matInput [(ngModel)]="elem.TYPE" [appAutoFocus]="(focus === elem.TYPE)">
    </mat-form-field>
  </mat-cell>
</ng-container>

<ng-container matColumnDef="NAME">
  <mat-header-cell *matHeaderCellDef> NAME </mat-header-cell>
  <mat-cell *matCellDef="let elem; let idx = index" (click)="testFocusIn(elem.NAME)">
    <span *ngIf="!editable || (selectedRowIdx !== idx)">{{elem.NAME}}</span>
    <mat-form-field *ngIf="editable && (selectedRowIdx == idx)"> 
      <input matInput [(ngModel)]="elem.NAME" [appAutoFocus]="(focus === elem.NAME)">
    </mat-form-field>
  </mat-cell>
</ng-container>

What else could I check? Maybe to define an ID-tag?


Solution

  • Currently you are only checking one "coordinate", meaning you are only checking for the row that you want to edit, but not which column in that row. Therefore you are not able to uniquely identify the cell to edit.

    I did not find a clean solution, but this stackblitz shows a working solution to your problem.

    When we click on a cell, we set the currently editable index and the currently editable column. This helps us to uniquely identify the cell that we want to edit.

    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef> Name </th>
      <td mat-cell *matCellDef="let element; let i = index;" (click)="edit(i, 'name')">
        <span *ngIf="showValue(i, 'name')">{{element.name}}</span>
        <mat-form-field *ngIf="showInput(i, 'name')"> 
          <input matInput placeholder="Placeholder">
        </mat-form-field>
      </td>    
    </ng-container>
    

    In the component

    edit(index: number, column: string) {
      this.editableColumn = column;
      this.editableIndex = index;
    }
    
    showInput(index: number, column: string) {
      return this.editableColumn === column && this.editableIndex === index;
    }
    
    showValue(index: number, column: string) {
      return this.editableColumn !== column || this.editableIndex !== index;
    }
    

    It is a bit ugly in the sense that we have to pass the column name in the template 3 times to the function and all the function calls pollute the template quite a bit. But I am sure that with some refactoring you could come up with a clean solution (one simplification would be to use a template variable and ng-template which would get rid of one function call).