Create data grid in angular with primeng
Define Column Types and Cell Types
1
2
3
4
5
6
7
8
9
enum TableCellType {
Input = 'input',
InputNumber = 'inputNumber',
Checkbox = 'checkbox',
Dropdown = 'dropdown',
Icon = 'icon',
Date = 'date',
Link = 'link',
}
1
2
3
4
5
6
7
8
9
10
type TableColumn = {
header: string;header.
field: string;
cellType?: TableCellType;
width?: string;
editable?: boolean;
align?: 'left' | 'right' | 'center';
iconClass?: string;
iconOnClick?: AnyFunc;
};
- header:
string: Represents the column’s display name in the table header. - field:
string: Specifies the key in the data source that the column will use.
Define Columns
1
2
3
4
5
6
7
8
9
10
cols: TableColumn[] = [
{
header: 'Code',
field: 'code',
editable: true,
cellType: TableCellType.Input,
width: '100px',
},
// More column definitions...
];
Define Form Group
To handle the data updates, I use a FormGroup that keeps track of each row’s data:
1
2
3
4
5
6
7
8
9
formGroup = new FormGroup({
id: new FormControl(),
code: new FormControl(),
name: new FormControl(),
active: new FormControl(),
price: new FormControl(),
category: new FormControl(),
quantity: new FormControl(),
});
Since only one row can be edited at a time, the row’s data is updated into the form group during editing. To listen for editing events, I use onEditInit and onEditComplete
1
2
3
4
5
6
7
onEditInit(event: TableEditInitEvent) {
const entity = this.products[event.index];
this.formGroup.setValue(
pick(entity, ['id', 'code', 'name', 'active', 'price', 'category', 'quantity']),
{ emitEvent: false }
);
}
1
2
3
4
5
6
7
8
9
10
onEditComplete(event: TableEditCompleteEvent) {
if (this.formGroup.dirty && event.index) {
this.products = [
...this.products.slice(0, event.index),
this.formGroup.value,
...this.products.slice(event.index + 1),
];
this.formGroup.markAsPristine();
}
}
Initialize PrimeNG Table
The table is initialized in the HTML as follows:
1
2
3
4
5
6
7
8
9
10
<p-table
[value]="products"
dataKey="id"
[tableStyle]="{ 'min-width': '50rem' }"
selectionMode="single"
[columns]="cols"
(onEditInit)="onEditInit($event)"
(onEditComplete)="onEditComplete($event)"
>
</p-table>
Initialize the Table Colgroup
To control the column widths:
1
2
3
4
5
6
7
8
9
<ng-template pTemplate="colgroup" let-columns>
<colgroup>
<col
*ngFor="let col of columns"
span="1"
[style.width]="col.width ? col.width : 'auto'"
/>
</colgroup>
</ng-template>
Initialize the Table Header
The header is rendered as follows:
1
2
3
4
5
6
7
8
9
10
11
<ng-template pTemplate="header" let-columns>
<tr>
<th
*ngFor="let col of columns"
[class.text-center]="col.align === 'center'"
[class.text-right]="col.align === 'right'"
>
{{ col.header }}
</th>
</tr>
</ng-template>
Initialize the Table Body
For rendering the table body dynamically and handling inline editing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<ng-template
pTemplate="body"
let-rowData
let-columns="columns"
let-i="rowIndex"
>
<tr>
<td
*ngFor="let col of columns"
[class.text-center]="col.align === 'center'"
[class.text-right]="col.align === 'right'"
[pEditableColumn]="rowData[col.field]"
[pEditableColumnField]="col.field"
[pEditableColumnRowIndex]="i"
[pEditableColumnDisabled]="!col.editable"
>
<p-cellEditor>
<ng-template pTemplate="input">
@switch (col.cellType) {
@case (cellType.InputNumber) {
<form [formGroup]="formGroup">
<p-inputNumber [formControlName]="col.field" inputStyleClass="w-full text-right" />
</form>
}
@default {
<form [formGroup]="formGroup">
<input [formControlName]="col.field" pInputText class="w-full" />
</form>
}
}
</ng-template>
<ng-template pTemplate="output">
@switch (col.cellType) {
@case (cellType.Dropdown) {
<p-dropdown
[options]="categories"
optionLabel="label"
optionValue="label"
appendTo="body"
[ngModel]="rowData[col.field]"
placeholder="Select a category" />
}
@case (cellType.Checkbox) {
<p-checkbox [binary]="true" [ngModel]="rowData[col.field]"/>
}
@case (cellType.Icon) {
<i
class="{{ col.iconClass }}"
[ngStyle]="{ cursor: 'pointer' }"
(click)="
col.iconOnClick &&
col.iconOnClick(rowData[col.field], i, $event)
"
></i>
}
@default {
{{ rowData[col.field] }}
}
}
</ng-template>
</p-cellEditor>
</td>
</tr>
</ng-template>
The complete source code can be found on Stackblitz.
Summary
You may have noticed that the left and right arrow keys trigger a focus event on the table, which should ideally be disabled while editing a cell. To achieve this, you can create a custom directive and apply it to each cell to override this behavior.
Additionally, you can enhance the grid by adding a feature that selects the cell’s data automatically when it receives focus, making editing more convenient.
Just drop me a message if you have any question.
Thanks for reading!
Leave a comment