gp-grid-logo
API Reference

Data Sources

Client-side and server-side data handling in Angular

Data Sources

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

OptionDefaultDescription
getFieldValueCustom accessor for nested properties
useWorkertrueUse 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.

my-grid.component.ts
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

Server Data Source

For server-side sorting, filtering, and pagination:

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 response = await fetch("/api/data", {
      method: "POST",
      body: JSON.stringify(request),
    });
    const result = await response.json();
    return { rows: result.data, totalRows: result.total };
  });
}

Request / Response

interface DataSourceRequest {
  pagination: { pageIndex: number; pageSize: number };
  sort?: SortModel[];
  filter?: FilterModel;
}

interface DataSourceResponse<TData> {
  rows: TData[];
  totalRows: number;
}

Choosing a Data Source

ScenarioRecommended
Small dataset (under 10k rows)rowData input
Large dataset, client-sidecreateClientDataSource
Editable data with CRUD, inside a componentprovideGridData + injectGridData
Editable data, outside DI (service, shared utility)createGridData
Editable data, shared singleton across unrelated componentscreateMutableClientDataSource
Server-side operationscreateServerDataSource

On this page