Home > Blockchain >  Is there a way to create a dynamic form group from an array of objects to edit an table row for dyna
Is there a way to create a dynamic form group from an array of objects to edit an table row for dyna

Time:06-21

I want to create a dynamic data table that will receive data from a backend server. The data will come in as an array of objects. Is there a way to dynamically create/bind a form group from the data that would allow inline editing for each row of the table and to update the changes when in edit mode. I don't want to hardcode data for the table each time unless I would have to. I am not able to use Angular Material for this project either. This is the component for the table row. The table functions as a data table but I can't seem to figure out how to make it editable.

import {
  Component,
  Input,
  Output,
  EventEmitter,
  ViewChild,
  ChangeDetectionStrategy,
} from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { EditButtonGroupComponent } from '../../edit-button-group/edit-button-group.component';

@Component({
  selector: '[dataTableRow]',
  templateUrl: './data-table-row-form.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DataTableRowFormComponent {
  @ViewChild('editButtonGroup', { static: true })
  editButtonGroup: EditButtonGroupComponent;
  @Input() data: any[];
  @Input() formModel: any;
  @Input() useEditButton: boolean;
  @Input() updating: boolean;
  @Input() deleting: boolean;
  @Input() index: number;
  
  checkMark(value: any) {
    if (value === true) {
      return 'bi bi-check-lg';
    } else {
      return '';
    }
  }

  booleanCheck(val: any) {
    if (typeof val === 'boolean') {
      return true;
    } else {
      return false;
    }
  }

  returnZero() {
    return 0;
  }

  enableEditMode() {
    this.editModeEnabled = true;
    this.copyModel();
  }

  disableEditMode() {
    this.editModeEnabled = false;
    this.copyModel();
  }
  
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.0/angular.min.js"></script>

<td *ngFor="let y of data[index] | keyvalue : returnZero">
  <i [ngClass]="checkMark(y.value)" *ngIf="booleanCheck(y.value) else default"></i>
  <ng-template #default>
    <ng-container *ngIf="!editModeEnabled else form">{{y.value}}</ng-container>
    <ng-template #form>
      <input type="text"  [(ngModel)]="formModel" />
    </ng-template>
  </ng-template>
</td>
<td *ngIf="useEditButton">
  <edit-button-group [editing]="isEditing && updating" [deleting]="isDeleting && deleting" (onDelete)="delete()"
    (onEdit)="update()" (onEditModeEnabled)="enableEditMode()" (onEditModeDisabled)="disableEditMode()"
    (onDelete)="onEmit()">
  </edit-button-group>
</td>

1.

List item

CodePudding user response:

Yes, this is definitely possible. You'll need to create a form array.

Ultimately, you need to push your data into the form.

  1. Create a data structure for the data you'll push into the form group. For example:
export interface TableRowData {
  ...whatever properties the data has here 
}
  1. Push the data into the form array. It will look something like: :
addRow(row: TableRowData): void {
  const row = new FormGroup({
    exampleProperty1: new FormControl(row.exampleProp1),
    exampleProperty2: new FormControl(row.exampleProp2),
  });
  this.table.push(row);
}
table: FormArray = new FormArray([]);

constructor(dataService: DataService) {
    // Fetch data from server
    this.dataService.getDataFromServer().subscribe((data: TableRowData[]) => {
        data.forEach(d => this.addRow(data));
    });

}

CodePudding user response:

To create a table from a FormArray it's just some like

<table >
    <thead>
        <tr >
            <th >Country</th>
            <th >Population</th>
            <th >GDP</th>
        </tr>
    </thead>
    <tbody *ngIf="formArray" >
        <tr  *ngFor="let item of formArray.controls;let i=index"
           [formGroup]="formGroup(i)">
            <td ><input formControlName="country"></td>
            <td ><input formControlName="population"></td>
            <td ><input formControlName="GDP"></td>
        </tr>
    </tbody>
</table>

Where we has

  formGroup(i) {
    return this.formArray.at(i) as FormGroup;
  }

See that you use a getter to get the formGroup. In this way you neen'd enclosed the formArray inside a FormGroup

But we want create the columns dinamically, so the first is know the "columns" of your table

formArray = new FormArray([]);
columns: string[] = [];
headers: string[] = [];
editIndex: number = -1; //used to change the state to edit/show
oldValue:any;  //used to recover the changes

  @Input('data') set _data(value: any[]) {
    this.columns = Object.keys(value[0]);
    this.headers = this.columns.map(
      (x) => x[0].toUpperCase()   x.substr(1).toLowerCase()
    );
    this.formArray = new FormArray(value.map((data) => this.createGroup(data)));
  }

  createGroup(data: any=null) {

    if (!data)
    {
       data={};
       this.columns.forEach(x=>{
         data[x]=null;
       })
    }

    const formGroup = new FormGroup({});
    Object.keys(data).forEach((key: string) => {
      formGroup.addControl(key, new FormControl(data[key]));
    });
    return formGroup;
  }

The .html becomes like

<table >
    <thead>
        <tr>
            <th *ngFor="let head of headers">{{head}}</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let item of formArray.controls;let i=index"
            [formGroup]="formGroup(i)">
            <ng-container *ngIf="editIndex==i">
               <td *ngFor="let column of columns">
                 <input [formControlName]="column">
               </td>
            </ng-container>
            <ng-container *ngIf="editIndex!=i">
               <td *ngFor="let column of columns">
                   {{formArray.at(i).value[column]}}
               </td>
            </ng-container>
            <td><button (click)="edit(i)">
                   {{editIndex==i?'ok':'edit'}}
                </button>
                <button *ngIf="editIndex==i" (click)="cancel(i)">
                   cancel
                </button>
            </td>
        </tr>
    </tbody>
</table>

And we have two functions, edit can cancel

  edit(index)
  {
    this.editIndex=this.editIndex==index?-1:index;
    this.oldValue={...this.formArray.at(index).value};

  }
  cancel(index)
  {
    this.formArray.at(index).setValue(this.oldValue);
    this.editIndex=-1;
  }

the stackblitz

  • Related