Lattice Grid Buy a licence

demo D280

Bring rows in from a CSV in Angular

Paste a block or drop a file, each column typed and mapped onto your fields, shown as a preview so nothing lands until you confirm; a spreadsheet block copied from Excel pastes the same way

grid.import.preview() · apply()

This grid brings rows in from a pasted block or a dropped CSV file, typing each column and mapping it onto your fields, shown as a preview so nothing lands until you confirm. It turns a spreadsheet export into grid rows without a separate import tool, and a block copied from Excel pastes the same way.

Building…
Loading a live grid…

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, ViewChild, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
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 CSV = `sku,name,category,price,stock
B-201,Floor lamp,Lighting,72,24
C-300,Bookshelf,Furniture,180,9`;

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [LatticeGridComponent, FormsModule],
  template:
    '<textarea rows="5" style="width:100%;font:12px ui-monospace, monospace" [(ngModel)]="csv"></textarea>' +
    '<div style="margin:8px 0">' +
    '  <button (click)="preview()" [disabled]="!grid">Preview</button> ' +
    '  <button (click)="confirm()" [disabled]="!pending() || pending().rowCount === 0">Confirm and add</button> ' +
    '  <span>{{ note() }}</span>' +
    '</div>' +
    '<lattice-grid [config]="gridConfig" (gridReady)="onGridReady($event)" style="display:block;height:460px"></lattice-grid>',
})
export class AppComponent {
  @ViewChild(LatticeGridComponent) gridRef!: LatticeGridComponent;

  csv = CSV;
  pending = signal<any>(null);
  note = signal('');
  grid: any = null;

  gridConfig = {
    rowKey: 'id',
    // import: true turns on the DOM affordances too: a drop target on the
    // grid, paste, and a cell-menu item, all running the same
    // preview-then-confirm pipeline as the buttons above. The grid.import
    // API is present either way.
    import: true,
    columns: [
      { field: 'sku', title: 'SKU', layout: { width: 110, pin: 'start' } },
      { field: 'name', title: 'Product', layout: { flex: 1, min: 160 } },
      { field: 'category', title: 'Category', filter: { type: 'set' } },
      { field: 'price', title: 'Price', type: 'number', format: { style: 'currency', currency: 'GBP' }, total: 'sum' },
      { field: 'stock', title: 'In stock', type: 'number', total: 'sum' },
    ],
    rows: [/* the rows already in the grid: sku, name, category, price, stock */],
  };

  onGridReady(grid: any) {
    this.grid = grid;
  }

  // preview() parses the text, infers each column's type and maps it onto
  // the grid's own fields, and reports the row count and any warnings, all
  // without changing the grid. A spreadsheet block copied from Excel reads
  // the same way.
  preview() {
    const result = this.grid.import.preview(this.csv);
    this.pending.set(result);
    this.note.set('Ready to add ' + result.rowCount + ' rows, ' + (result.warnings?.length ?? 0) + ' warnings.');
  }

  // Nothing lands until apply(), so a ragged or mismatched file is caught
  // first. mode: 'append' adds the rows; mode: 'replace' would swap the set.
  confirm() {
    this.grid.import.apply(this.pending(), { mode: 'append' });
    this.pending.set(null);
  }
}

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