Data Sources
Connect the Angular grid to client-side arrays or server-side endpoints, and plug streaming, paginated, or fully remote data flows into gp-grid with ease.
gp-grid provides the same data source abstractions across bindings. Angular consumers have two ergonomic entry points on top of the core factories: an injectable GridDataService for component-scoped DI, and a plain createGridData() helper for when DI is not a fit.
Basic Usage with rowData
The simplest approach is to pass an array through the rowData input:
@Component({
selector: "app-my-grid",
standalone: true,
imports: [GpGridComponent],
template: `
<gp-grid [columns]="columns" [rowData]="data" [rowHeight]="36" /> // [!code highlight]
`,
})
export class MyGridComponent {
data = [
{ id: 1, name: "Giovanni" },
{ id: 2, name: "Luca" },
];
}This internally creates a client data source.
Client Data Source
For more control — custom field accessors, disabling the Web Worker, etc. — use createClientDataSource:
import { GpGridComponent, createClientDataSource } from "@gp-grid/angular";
@Component({
standalone: true,
imports: [GpGridComponent],
template: `<gp-grid [columns]="columns" [dataSource]="dataSource" [rowHeight]="36" />`,
})
export class MyGridComponent {
dataSource = createClientDataSource(largeDataset, {
useWorker: true,
});
}Options
| Option | Default | Description |
|---|---|---|
getFieldValue | – | Custom accessor for nested properties |
useWorker | true | Use a Web Worker for sorting large datasets |
Mutable Data Source
For CRUD operations and real-time updates inside an Angular component, prefer the injectable GridDataService via provideGridData() / injectGridData(). It wraps createMutableClientDataSource with automatic cleanup on component destroy and gives every @Component its own isolated instance.
import { Component } from "@angular/core";
import {
GpGridComponent,
provideGridData,
injectGridData,
type ColumnDefinition,
} from "@gp-grid/angular";
interface Person {
id: number;
name: string;
age: number;
}
@Component({
selector: "app-my-grid",
standalone: true,
imports: [GpGridComponent],
providers: [
provideGridData<Person>({
getRowId: (row) => row.id,
initialData: seedRows,
debounceMs: 50,
}),
],
template: `
<gp-grid [columns]="columns" [dataSource]="grid.dataSource" [rowHeight]="36" />
<button (click)="addOne()">Add</button>
<button (click)="renameFirst()">Rename first</button>
`,
})
export class MyGridComponent {
protected readonly grid = injectGridData<Person>();
columns: ColumnDefinition[] = [
{ field: "id", cellDataType: "number", width: 80 },
{ field: "name", cellDataType: "text", width: 200 },
{ field: "age", cellDataType: "number", width: 100 },
];
addOne(): void {
this.grid.addRows([{ id: Date.now(), name: "Mario", age: 30 }]);
}
renameFirst(): void {
this.grid.updateCell(1, "name", "Maria");
}
}provideGridData() must be registered in the component's providers array, not in a module or a parent component. Registering it higher up would silently share one data source between every child — which is usually a bug. One grid component, one provideGridData() call.
Function-based alternative: createGridData
When you want a mutable data source without going through DI — for example in a service that manages several grids, or in a standalone utility — reach for createGridData(). It returns the same API object shape, minus the automatic cleanup:
import { createGridData } from "@gp-grid/angular";
@Component({ /* ... */ })
export class MyGridComponent implements OnDestroy {
protected readonly grid = createGridData<Person>(seedRows, {
getRowId: (row) => row.id,
});
ngOnDestroy(): void {
this.grid.clear();
}
}Unlike GridDataService, createGridData() does not dispose of the underlying data source when the component is destroyed. Call clear() (or tear it down yourself) in ngOnDestroy to avoid leaking workers and subscriptions.
Escape hatch: raw factory
Reach for the raw createMutableClientDataSource factory only when neither path above fits — module-level singletons, shared data sources used by multiple unrelated components, or non-Angular code. In a component, always prefer provideGridData / createGridData.
// Outside a component (e.g. a shared module)
import { createMutableClientDataSource } from "@gp-grid/angular";
export const sharedDataSource = createMutableClientDataSource(seed, {
getRowId: (row) => row.id,
});Transaction System
The mutable data source batches mutations through an internal transaction system:
- Automatic batching — operations within the debounce window collapse into a single transaction, minimising re-renders.
- Optimistic processing — mutations are queued asynchronously so the UI stays responsive under heavy churn.
- Flush control — call
flushTransactions()to force immediate processing (useful before navigating away or running assertions).
await this.grid.flushTransactions();Options
interface GridDataOptions<TData> {
initialData: TData[]; // Required: initial rows
getRowId: (row: TData) => RowId; // Required: unique ID accessor
debounceMs?: number; // Batch window (default: 50)
useWorker?: boolean; // Worker sort (default: true)
parallelSort?: ParallelSortOptions | false;
}GridDataService Interface
The injected service exposes the same surface as the React hook and Vue composable, plus the dataSource itself:
class GridDataService<TData> {
readonly dataSource: MutableDataSource<TData>;
addRows(rows: TData[]): void;
removeRows(ids: RowId[]): void;
updateRow(id: RowId, data: Partial<TData>): void;
updateCell(id: RowId, field: string, value: CellValue): void;
clear(): void;
getRowById(id: RowId): TData | undefined;
getTotalRowCount(): number;
flushTransactions(): Promise<void>;
}Use Cases
The mutable data source is ideal for:
- Real-time dashboards — live feeds with frequent updates
- Streaming data — WebSocket or SSE pipelines
- User editing — forms and inline editing with immediate feedback
- Bulk operations — imports, mass updates, batch delete
DataSource Interface
All data sources implement the DataSource<TData> interface. Both createClientDataSource and createServerDataSource return objects that conform to it; MutableDataSource<TData> extends it with mutation methods.
interface DataSource<TData> {
readonly loadMode?: "all" | "paginated";
query(request: DataSourceRequest): Promise<DataSourceResponse<TData>>;
destroy?(): void;
moveRow?(fromIndex: number, toIndex: number): void;
}| Member | Required | Description |
|---|---|---|
loadMode | No | "all" (default for client data sources) or "paginated" (default for server). Controls whether the grid requests every row at once or only the visible window via range.startRow / range.endRow. |
query | Yes | Async function returning { rows, totalRows } for the requested range, sort, and filter. Called by the grid whenever the visible window, sort, or filter changes. |
destroy | No | Cleanup hook for releasing workers, sockets, or other resources. Called when the data source is detached from the grid. |
moveRow | No | Called on row drag-end. Implement it to update the underlying data; the grid does not mutate data on its own. |
Server Data Source
For server-side sorting, filtering, and windowed row loading:
import { GpGridComponent, createServerDataSource } from "@gp-grid/angular";
@Component({
standalone: true,
imports: [GpGridComponent],
template: `<gp-grid [columns]="columns" [dataSource]="dataSource" [rowHeight]="36" />`,
})
export class MyGridComponent {
dataSource = createServerDataSource(async (request) => {
const { range, sort, filter } = request;
const response = await fetch("/api/data", {
method: "POST",
body: JSON.stringify({
startRow: range.startRow,
endRow: range.endRow,
sortBy: sort,
filters: filter,
}),
});
const result = await response.json();
return { rows: result.data, totalRows: result.total };
});
}Pass { loadMode: "all" } as a second argument if the server returns the entire result set in one call. The default is "paginated" — the grid only requests the row window currently in view via range.startRow / range.endRow.
const dataSource = createServerDataSource(queryFn, { loadMode: "paginated" });Request / Response
interface DataSourceRequest {
range: {
startRow: number; // First row index to fetch (0-indexed, inclusive)
endRow: number; // First row index after the range (exclusive)
};
sort?: SortModel[];
filter?: FilterModel;
}
interface DataSourceResponse<TData> {
rows: TData[];
totalRows: number;
}Choosing a Data Source
| Scenario | Recommended |
|---|---|
| Small dataset (under 10k rows) | rowData input |
| Large dataset, client-side | createClientDataSource |
| Editable data with CRUD, inside a component | provideGridData + injectGridData |
| Editable data, outside DI (service, shared utility) | createGridData |
| Editable data, shared singleton across unrelated components | createMutableClientDataSource |
| Server-side operations | createServerDataSource |
Column Definition
Configure each Angular grid column individually with type, width, sorting, filtering, rendering, and editing options through the column definition object.
Guides Overview
Explore in-depth guides covering performance, styling, and integration techniques for using gp-grid efficiently inside an Angular application across teams.