angularinline-editing

Property Binding in Angular 2


I'm attempting to create inline editing functionality on tabular data in an Angular 2 application. Each row displays an edit icon which toggles the inline editor. When editing, there should be two buttons, one to cancel and another to save the row.

Currently I'm having trouble when trying to revert to the previous state when canceling my edit. Please see the sample code below for more information.

Typescript:

interface IRow {
  id: number;
  foo: string;
  bar: string;
  baz: string;
  isEditing: boolean;
}

Template:

<div class="table">
  <div class="table-head">
    <div class="table-row">
      <div class="table-cell">Foo</div>
      <div class="table-cell">Bar</div>
      <div class="table-cell">Baz</div>
      <div class="table-cell"><!-- Edit --></div>
    </div>
  </div>
  <div class="table-body">
    <div *ngFor="let row of rows" class="table-row">

      <-- Show this if not editing -->
      <ng-container *ngIf="!row.isEditing; else editing">
        <div class="table-cell">{{ row.foo }}</div>
        <div class="table-cell">{{ row.bar }}</div>
        <div class="table-cell">{{ row.baz }}</div>
        <div class="table-cell">
          <button (click)="edit(row)>
            <i class="icon-pencil"></i>
          </button>
        </div>
      </ng-container>

      <!-- Show this if editing -->
      <ng-template #editing>
        <div class="table-cell"><input type="text" [(value)]="row.foo"></div>
        <div class="table-cell"><input type="text" [(value)]="row.bar"></div>
        <div class="table-cell"><input type="text" [(value)]="row.baz"></div>
        <div class="table-cell">
          <button (click)="cancel(row)>
            <i class="icon-back"></i>
          </button>
          <button (click)="save(row)>
            <i class="icon-save"></i>
          </button>
        </div>
      <ng-template>

    </div>
  </div>
<div>

Component:

// Class variable
public originalRow;

edit(row) {
  // Save a copy of the original
  this.originalRow = { ...row };
  this.row.isEditing = true;
}

cancel(row) {
  // This doesn't work
  // row = this.originalRow; 

  // This works
  row.foo = this.originalRow.foo;
  row.bar = this.originalRow.bar;
  row.baz = this.originalRow.baz;
  this.row.isEditing = false;
}

save(row) {
  // Store new value in state
  this.originalRow = row;

  // Post update
  this.rowSvc.updateRow(row);
}

What's the best strategy for reverting data to its previous state on cancel of the edit?


Solution

  • This is the expected behavior. When you set the row, you just overwrite the parameter and losing the object reference.

    First set the index:

    <div *ngFor="let row of rows; let i = index" class="table-row">
    

    Then pas the index to edit function:

    <-- Show this if not editing -->
    <ng-container *ngIf="!row.isEditing; else editing">
      <div class="table-cell">{{ row.foo }}</div>   <div class="table-cell">{{ row.bar }}</div>
      <div class="table-cell">{{ row.baz }}</div>
      <div class="table-cell">
        <button (click)="edit(row, i)> <-- Pass the index -->
          <i class="icon-pencil"></i>
        </button>
      </div>
    </ng-container>
    

    Use index in the cancel function to set the row to original state:

    cancel(row: IRow, index: number) {
      this.rows[index] = { ...this.originalRow };
      this.row.isEditing = false;
    }
    

    Another option would be simply iterating over the attributes of the row:

    cancel(row: IRow) {
      Object.keys(row).forEach((key: string) => row[key] = this.originalRow[key]);
      this.row.isEditing = false;
    }
    

    Note that, in either solution, you'd have problems with editing multiple rows at the same time.