demo D85
The fill handle in Angular
Dragging a corner to extend a series it recognises, with a pinned row-number column so a sort never loses count
fillHandle · rowNumbers
This grid extends a selection by dragging the small grip at its corner, filling the cells it crosses with a series it recognises. It brings the spreadsheet fill gesture to the grid, so a date or a pattern carries down a column with a drag. A pinned row-number column on the left counts the rows as they sit on screen, renumbering the moment you sort.
This is the Angular version. A standalone component takes the whole configuration through one config input, surfaces grid events as outputs, and exposes the live grid on a getter for anything the inputs do not cover.
The configuration
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { LatticeGridComponent, provideLattice } from '@toclocoinc/lattice-grid/angular';
const columns = [
{ field: 'id', title: 'Order', layout: { width: 120, pin: 'start' } },
{ field: 'reference', title: 'Reference', layout: { width: 130 } },
{ field: 'due', title: 'Due', type: 'date', layout: { width: 140 }, edit: true },
{ field: 'circuits', title: 'Circuits', type: 'number', layout: { width: 110 }, edit: true },
{ field: 'impact', title: 'Impact', type: 'number', layout: { width: 100 }, edit: true },
{ field: 'budget', title: 'Budget', type: 'number', layout: { width: 140 },
format: { style: 'currency', currency: 'GBP' }, total: 'sum', edit: true },
];
const rows = [/* work orders */];
@Component({
selector: 'app-root',
standalone: true,
imports: [LatticeGridComponent],
template: '<lattice-grid [config]="grid" style="display:block;height:540px"></lattice-grid>',
})
export class AppComponent {
// rowNumbers pins a read-only column ahead of the also-pinned Order column,
// counting the rows as shown; clicking a heading to sort leaves it 1..n.
grid = {
rowKey: 'id',
selection: { mode: 'multiple', ranges: true, fillHandle: true },
edit: true,
rowNumbers: true,
columns: columns,
rows: rows,
};
}
bootstrapApplication(AppComponent, {
providers: [provideLattice({ createGrid })],
});
Extending a series by dragging the fill handle
The fill handle is the small grip at the corner of a selected cell or range that, when dragged, extends the selection and populates the cells it crosses. A developer reaches for it wherever a spreadsheet-trained user expects to drag a date down a column and get the next few days, drag a “1” and a “2” and get “3, 4, 5”, or drag a single value across a row to repeat it. Lattice Grid exposes this through the fillHandle option: the grid inspects the source cells itself and works out whether the drag should repeat a constant, continue an arithmetic step, or advance a recognised date sequence. Because a JavaScript data grid handling this at scale cannot afford to recompute the whole visible range on every pointer move, the fill preview only touches the cells between the anchor and the current pointer position, and the write itself is a single batched update applied once the drag ends rather than one mutation per cell crossed. The result integrates with the grid’s existing range selection, so a filled range is immediately addressable for copy, further fill, or a subsequent formula reference, with no separate “fill mode” state to fall in or out of.
How do I let users drag-fill a series in a data grid, like Excel?
Enable fillHandle on the grid. It decides how a drag should extend the source cells on its own: as a repeated constant, an arithmetic sequence, or a date series. Lattice Grid renders the drag handle on the active selection’s corner, previews the affected cells as the pointer moves, and commits the filled values as one update when the drag is released.
A row-number column that always matches what’s on screen
rowNumbers: true adds a read-only column, pinned to the left, counting the rows in the order they are actually shown: after any sort, filter or grouping. Click a heading in this grid to sort it and the numbers in that left column stay a plain 1, 2, 3 run, because they count position rather than carrying a value stamped onto a row. Grouping is handled the same honest way: a group heading is not itself numbered, and the count continues across it rather than skipping or restarting, so the leaf rows underneath always add up. The column is for reading, not reporting; a CSV, Excel or clipboard export leaves it out by default; a call site that wants the numbers in the file passes { rowNumbers: true } to that export call, the same flag name whichever surface it is exporting to.