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.
- 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
}
- 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