Lattice Grid Buy a licence

angular data grid · kpi tiles

Angular Grid: KPI Tiles

A row of live figures beside the grid, reading the same rows it holds: a running total, an average measured against a baseline, a count that changes colour past a threshold, and a sparkline, moving together as the data changes.

Also in: React

Install and import

npm install @toclocoinc/lattice-grid
# free on localhost; a production domain needs a licence key (see /pricing/)

The file

pipeline.component.ts

// pipeline.component.ts
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createKPI } from '@toclocoinc/lattice-grid/modules/kpi';
import { LatticeGridComponent, LatticeKpiComponent, provideLattice } from '@toclocoinc/lattice-grid/angular';

const MONEY = { style: 'currency', currency: 'USD', decimals: 0 };
const OPEN = { warn: 14, critical: 22, direction: 'lowerIsBetter' };

@Component({
  selector: 'app-pipeline',
  standalone: true,
  imports: [LatticeGridComponent, LatticeKpiComponent],
  template:
    '<lattice-kpi [config]="kpiConfig" style="display:block;margin-bottom:12px"></lattice-kpi>' +
    '<lattice-grid [config]="gridConfig" style="display:block;height:320px"></lattice-grid>',
})
export class PipelineComponent {
  // Both the grid and the panel read and publish the page's one
  // default-named grid, so neither side has to name it explicitly.
  gridConfig = {
    rowKey: 'id',
    columns: [
      { field: 'id', title: 'Ref', layout: { width: 96, pin: 'start' } },
      { field: 'region', title: 'Region', filter: { type: 'set' } },
      { field: 'stage', title: 'Stage', filter: { type: 'set' } },
      { field: 'value', title: 'Value', type: 'number', format: MONEY, total: 'sum' },
    ],
    rows: [] as unknown[], // deal records: id, region, stage, value
  };

  // A running total, an average against a baseline, a count with a
  // good/warn/critical band, and a sparkline over the biggest deal.
  kpiConfig = {
    columns: 4,
    tiles: [
      { id: 'revenue', label: 'Pipeline value', aggregation: 'sum', field: 'value', format: 'currency' },
      { id: 'avg', label: 'Average deal vs $12k', aggregation: 'avg', field: 'value', format: { type: 'currency', decimals: 0 }, baseline: 12000 },
      { id: 'open', label: 'Still open', aggregation: 'count', filter: (r: any) => r.stage !== 'won', thresholds: OPEN },
      { id: 'biggest', label: 'Biggest deal', aggregation: 'max', field: 'value', format: 'currency', sparkline: 'value' },
    ],
  };
}

bootstrapApplication(PipelineComponent, {
  providers: [provideLattice({ createGrid, createKPI })],
});

See it running

Building…
Loading a live grid…

Open this demo's Angular tab →

Working in Angular

No explicit link between the two components. Both <lattice-grid> and <lattice-kpi> publish to and read from the same default-named grid registry, so as long as neither names a different name or gridName, the panel finds the grid on its own.

The panel waits for its grid. Nothing is built until the grid it needs exists, so a <lattice-kpi> written above its <lattice-grid> in the template still renders correctly once the grid arrives.

Tiles update on a grid event, not a change-detection cycle. A tile recomputes when the grid's own rows or filter changes, so filtering the grid moves every tile with no field of your own to keep in sync.

Related