Lattice Grid Buy a licence

demo D23

Durations

Elapsed time that sorts numerically and reads as 3h 12m

type: 'duration'

Building…
Loading a live grid…

The configuration

import * as ng from '@angular/core';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import createLatticeGrid from '@toclocoinc/lattice-grid/modules/angular';

const { LatticeGridComponent } = createLatticeGrid({ ng, createGrid });

const columns = [
  { field: 'pipeline', title: 'Pipeline', filter: { type: 'set' }, layout: { width: 130, pin: 'start' } },
  { field: 'stage', title: 'Stage', filter: { type: 'set' }, layout: { width: 130 } },
  {
    field: 'outcome', title: 'Outcome', filter: { type: 'set' }, layout: { width: 120 },
    cell: { decoration: 'pill', variant: { map: { ok: 'success', retried: 'warning', failed: 'danger' } } },
  },
  // Milliseconds in, "3h 12m" out. The value stays a number, so the sort is
  // numeric, the range filter takes numbers and the total is a sum.
  { field: 'elapsed', title: 'Elapsed', type: 'duration', layout: { width: 140 }, total: 'sum' },
  { field: 'queued', title: 'Queued', type: 'duration', layout: { width: 140 }, total: 'sum' },
  { field: 'cpu', title: 'CPU time', type: 'duration', layout: { width: 140 }, total: 'sum' },
  {
    field: 'elapsedText', title: 'Elapsed, as text', type: 'text', layout: { width: 170 },
    // The same elapsed time as a person would have typed it. Sort this column
    // and then the one three to its left: this is the case the type exists for.
    filter: { type: 'text' },
  },
  {
    id: 'share', title: 'CPU / elapsed', type: 'number', layout: { width: 150 },
    format: { style: 'percent', decimals: 0 },
    value: { deps: ['elapsed', 'cpu'], pure: true, compute: (d) => (d.elapsed ? d.cpu / d.elapsed : null) },
  },
  { field: 'records', title: 'Records', type: 'number', layout: { width: 130 }, format: { notation: 'compact' }, total: 'sum' },
];

const rows = [/* pipeline runs, durations stored in milliseconds */];



@Component({
  selector: 'app-root',
  standalone: true,
  imports: [LatticeGridComponent],
  template: '<lattice-grid [config]="grid" style="display:block;height:540px"></lattice-grid>',
})
export class AppComponent {
  grid = {
    rowKey: 'id',
    toolPanel: { side: 'left', panels: ['columns', 'filters', 'views', 'quick'],
          actions: ['undo', 'redo', 'export', 'restore', 'maximise'], exportName: 'lattice-demo' },
    columns: columns,
    rows: rows,
  };
}

bootstrapApplication(AppComponent);

Formatting and sorting elapsed time correctly

Elapsed time is a poor fit for the number or date types: a raw second count is unreadable in a cell, and a formatted string like “3h 12m” sorts alphabetically rather than by duration, so “1h 5m” ends up after “10h 0m”. Lattice Grid’s type: 'duration' column stores the underlying value as seconds (or milliseconds, if you prefer) and keeps the comparator numeric while the renderer shows the short form a person actually reads, so a column of build times, session lengths or SLA countdowns sorts by true elapsed time even though every cell displays as hours and minutes rather than a number. A developer reaches for this type wherever a JavaScript data grid needs to show a span rather than a point: task duration, video length, uptime, time-to-resolution. Because the comparator works on the stored numeric value rather than the rendered string, sorting a column of durations costs the same as sorting any other numeric column, with no string-parsing step inserted per row, and it stays correct at any row count the grid virtualises.

The renderer also accepts a threshold so unusually long or short durations can carry a variant, letting a duration column double as an at-a-glance SLA indicator without a separate formatting rule.

How do you sort a duration column correctly in a data grid?

Store the duration as a plain number of seconds or milliseconds and let the grid’s type: 'duration' comparator sort on that value, rather than sorting the formatted string. Sorting the display text (“3h 12m”, “45m”) puts short-looking strings ahead of longer numeric durations; sorting the underlying seconds keeps the order numerically correct regardless of how compact the rendered label is.