Skip to content

Commit 59fa403

Browse files
release: 0.0.10 updated the datagrid,added the editService for crud operations, resolve the security issue
1 parent d03da28 commit 59fa403

File tree

6 files changed

+318
-47
lines changed

6 files changed

+318
-47
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ All components are standalone, so you import them directly into your feature com
3939
```ts
4040
import { Component } from '@angular/core';
4141
import { Datagrid } from '@angular-bootstrap/ngbootstrap/datagrid';
42+
import { NgbDatagridDefaultEditService } from '@angular-bootstrap/ngbootstrap/datagrid';
4243

4344
interface User {
4445
id: number;
@@ -54,6 +55,8 @@ interface User {
5455
<ngb-datagrid
5556
[columns]="columns"
5657
[data]="rows"
58+
[trackBy]="trackById"
59+
[editService]="editService"
5760
[enableSorting]="true"
5861
[enableFiltering]="true"
5962
[enablePagination]="true"
@@ -63,6 +66,8 @@ interface User {
6366
`,
6467
})
6568
export class UsersComponent {
69+
trackById = (_: number, row: User) => row.id;
70+
editService = new NgbDatagridDefaultEditService<User>();
6671
columns = [
6772
{ field: 'id', header: 'ID', sortable: true },
6873
{ field: 'name', header: 'Name', sortable: true, filterable: true },
@@ -86,6 +91,8 @@ Key datagrid capabilities:
8691
- Column/global filtering (`enableFiltering`, `enableGlobalFilter`, `filtersChange`).
8792
- Pagination (`enablePagination`, `pageSize`, `pageChange`).
8893
- Inline add/edit/delete (`enableAdd`, `enableEdit`, `enableDelete`, `rowAdd`, `rowSave`, `rowDelete`).
94+
- Stable row identity via `trackBy` (defaults to index).
95+
- Pluggable editing logic via `editService` (implement `NgbDatagridEditService`).
8996
- Export to PDF/Excel via `exportOptions`.
9097

9198
Export requires optional peer dependencies. Install only if you use export:
@@ -330,3 +337,7 @@ Recommended release flow:
330337
- Run `npm version minor` or `npm version patch` to bump `package.json` and create the tag.
331338
- Push the commit and tag: `git push origin main --tags`.
332339
- GitHub Actions will build and publish that tagged version to npm.
340+
341+
## Keywords
342+
343+
Keywords: ngbootstrap, angular bootstrap, bootstrap 5, angular components, UI library, datagrid, data grid, table, pagination, stepper, wizard, splitter, resizable panes, tree view, typeahead, autocomplete, chips, tags input, drag and drop, dnd, accessibility, a11y, ARIA, reactive forms, standalone components.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-bootstrap/ngbootstrap",
3-
"version": "0.0.9",
3+
"version": "0.0.10",
44
"description": "Angular UI library providing datagrid, drag-and-drop, pagination, stepper, splitter, typeahead and chips components with Bootstrap-friendly styling.",
55
"author": {
66
"name": "Harmeet Singh"

src/datagrid/public-api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export * from './src/directives/datagrid-templates.directive';
1212
export * from './src/datagrid.types'
1313

1414
//service
15-
export * from './src/services/export.services'
15+
export * from './src/services/export.services'
16+
export * from './src/services/editing.service'

src/datagrid/src/datagrid/datagrid.component.spec.ts

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,14 @@ describe('Datagrid', () => {
203203
component.enableEdit = true;
204204
component.startEdit(0);
205205
const emitSpy = jest.spyOn(component.rowSave, 'emit');
206+
const originalRow = component.data[0];
206207

207208
component.editForm.patchValue({ name: 'Alice Cooper' });
208209
component.saveEdit(0);
209210

210211
expect(emitSpy).toHaveBeenCalledWith({
211-
original: component.data[0],
212-
updated: { ...component.data[0], name: 'Alice Cooper' },
212+
original: originalRow,
213+
updated: { ...originalRow, name: 'Alice Cooper' },
213214
index: 0
214215
});
215216
expect(component.editingIndex).toBeNull();
@@ -243,10 +244,75 @@ describe('Datagrid', () => {
243244
component.enableDelete = true;
244245
component.pageSize = 3; // ensure paged contains third entry
245246
const emitSpy = jest.spyOn(component.rowDelete, 'emit');
247+
const deleted = component.data[2];
246248

247249
component.deleteRow(2);
248250

249-
expect(emitSpy).toHaveBeenCalledWith({ row: component.data[2], index: 2 });
251+
expect(emitSpy).toHaveBeenCalledWith({ row: deleted, index: 2 });
252+
expect(component.data.find(r => r.id === deleted.id)).toBeDefined();
253+
});
254+
255+
it('uses trackBy callback when provided', () => {
256+
const row = component.data[0];
257+
expect(component.trackRow(0, row)).toBe(0);
258+
259+
component.trackBy = (_i, r) => (r as any).id;
260+
expect(component.trackRow(0, row)).toBe(1);
261+
});
262+
263+
it('uses the custom editService when provided', () => {
264+
const assignValues = jest.fn((row: any, patch: any) => ({ ...row, ...patch }));
265+
const create = jest.fn(() => []);
266+
const update = jest.fn(() => []);
267+
const remove = jest.fn(() => []);
268+
const saveChanges = jest.fn(() => []);
269+
const cancelChanges = jest.fn(() => []);
270+
271+
component.editService = {
272+
create,
273+
update,
274+
remove,
275+
assignValues,
276+
isNew: () => false,
277+
hasChanges: () => false,
278+
saveChanges,
279+
cancelChanges,
280+
} as any;
281+
282+
component.enableAdd = true;
283+
component.startAdd();
284+
expect(create).toHaveBeenCalled();
285+
component.addForm.setValue({
286+
id: 4,
287+
name: 'Dana',
288+
email: 'dana@example.com',
289+
score: 50,
290+
active: true,
291+
created: '2024-04-01'
292+
});
293+
component.saveAdd();
294+
expect(saveChanges).toHaveBeenCalled();
295+
component.startAdd();
296+
component.cancelAdd();
297+
expect(cancelChanges).toHaveBeenCalled();
298+
299+
component.enableEdit = true;
300+
component.startEdit(0);
301+
component.editForm.patchValue({ name: 'Alice Cooper' });
302+
component.saveEdit(0);
303+
304+
expect(assignValues).toHaveBeenCalled();
305+
expect(update).toHaveBeenCalled();
306+
expect(saveChanges).toHaveBeenCalled();
307+
308+
component.startEdit(0);
309+
component.cancelEdit(0);
310+
expect(cancelChanges).toHaveBeenCalled();
311+
312+
component.enableDelete = true;
313+
component.pageSize = 3;
314+
component.deleteRow(2);
315+
expect(remove).toHaveBeenCalled();
250316
});
251317

252318
it('cycles sort direction and emits sortChange', () => {
@@ -317,11 +383,13 @@ describe('Datagrid', () => {
317383
it('starts editing when row clicked outside interactive targets', () => {
318384
component.enableEdit = true;
319385
component.editOnRowClick = true;
386+
const editSpy = jest.spyOn(component, 'startEdit');
320387
const target = document.createElement('div');
321388
const event = createEventForTarget(target);
322389

323390
component.onRowClick(event, 0);
324391

392+
expect(editSpy).toHaveBeenCalledWith(0);
325393
expect(component.editingIndex).toBe(0);
326394
});
327395

@@ -353,6 +421,16 @@ describe('Datagrid', () => {
353421
expect(component.isResponsiveEnabled()).toBe(false);
354422
});
355423

424+
it('validates email input with a safe check (guards extremely long values)', () => {
425+
const emailCol = component.columns.find(c => c.field === 'email')!;
426+
const draft: any = { email: '!@!.' + '!.'.repeat(20000) };
427+
const errors: any = {};
428+
429+
component.validateInto(emailCol, draft, errors);
430+
431+
expect(errors.email).toBe('Invalid email');
432+
});
433+
356434
it('delegates export via triggerExport', () => {
357435
component.exportOptions.enabled = true;
358436
const exportSpy = jest.spyOn(component, 'export').mockResolvedValue();

0 commit comments

Comments
 (0)