Home/Data & State/Columns from Data
Data & State

Columns from Data

Bind rows and no columns, and the grid derives them from the data's shape — typed, humanized, and still ordinary column definitions you can override one at a time.

No column definitions at all

This grid binds a data source and a config, and nothing else. Sort Weight Kg and the numbers order numerically; sort Dispatched and the rows order by date.

ID
Customer Ref
Destination
Weight Kg
Dispatched
Delivered
1
CR-1000
Alexandria
8
2024-01-01
Yes
2
CR-1007
Rotterdam
1200
2025-06-08
Yes
3
CR-1014
Singapore
96
2026-11-15
No
4
CR-1021
Valencia
4
2024-04-22
Yes
5
CR-1028
Busan
730
2025-09-02
Yes
6
CR-1035
Santos
55
2026-02-09
No
7
CR-1042
Durban
310
2024-07-16
Yes
8
CR-1049
Hamburg
2
2025-12-23
Yes
9
CR-1056
Jebel Ali
980
2026-05-03
No
10
CR-1063
Manzanillo
41
2024-10-10
Yes
11
CR-1070
Colombo
6600
2025-03-17
Yes
12
CR-1077
Tanger Med
17
2026-08-24
No
All records loaded
12 / 12 records

Six columns, none of them written

The grid above is <gridcraft [dataSource]="dataSource" [config]="config" /> — there is no [columns] binding on it. Every header, every alignment and every filter came from the rows.

They are typed, not just present

Sort Weight Kg: the order is 2, 4, 8, 17… not 1200, 17, 2. Sort Dispatched: the rows order by date, not by the text of a weekday name. Open the filter under Delivered: it is a Yes / No dropdown. A generated column goes through the same type engine a hand-written one does — see Cell Data Types.

Headers are read from the key

customerRef becomes Customer Ref and id becomes ID. It is a default label, not a translation — write headerFrom for your own wording, or a column's name for one header.

The courier column is missing, and that is deliberate

courier holds an object. Generation skips it rather than printing [object Object]. The next example brings it back in one line.

Two columns written, the rest generated

Courier reaches a nested value generation skips, and Weight Kg states only a width — it keeps the generated header and numeric type.

Courier
Weight Kg
ID
Customer Ref
Destination
Dispatched
Delivered
Ramsey
8
1
CR-1000
Alexandria
2024-01-01
Yes
Vale
1200
2
CR-1007
Rotterdam
2025-06-08
Yes
Kirkby
96
3
CR-1014
Singapore
2026-11-15
No
Ramsey
4
4
CR-1021
Valencia
2024-04-22
Yes
Vale
730
5
CR-1028
Busan
2025-09-02
Yes
Kirkby
55
6
CR-1035
Santos
2026-02-09
No
Ramsey
310
7
CR-1042
Durban
2024-07-16
Yes
Vale
2
8
CR-1049
Hamburg
2025-12-23
Yes
Kirkby
980
9
CR-1056
Jebel Ali
2026-05-03
No
Ramsey
41
10
CR-1063
Manzanillo
2024-10-10
Yes
Vale
6600
11
CR-1070
Colombo
2025-03-17
Yes
Kirkby
17
12
CR-1077
Tanger Med
2026-08-24
No
All records loaded
12 / 12 records

Two written columns, four generated

This grid binds [columns] with exactly two entries and sets autoGenerateColumns: true. The written ones come first, in the order you wrote them; the rest of the keys follow.

A written column keeps what it did not say

Weight Kg is written as { key: 'weightKg', prop: 'weightKg', width: 120 } — no name, no type. It renders with the generated header Weight Kg and still sorts numerically, because a supplied definition merges over the generated one field by field instead of replacing it.

{ key: 'weightKg', prop: 'weightKg', width: 120 }   // name and dataType come from generation

Reaching a nested value

Courier is the key generation skipped. One hand-written column with a dotted prop renders it, and the other five columns are still generated:

{ key: 'courier.name', name: 'Courier', prop: 'courier.name', filter: {} }

When to use it

Bind a data source, bind no columns, and the grid derives them from the data's shape — one column per scalar key, in the order the keys appear. It is the fastest way to see your data in a grid, and it survives contact with real work: a generated column is an ordinary column definition, so you can keep generation and write the two columns you care about.

How to turn it on

  • Bind no [columns] — generation happens on its own.
  • autoGenerateColumns: true — generate even though [columns] is bound, filling in the keys those columns do not cover.
  • autoGenerateColumns: false — never generate, even with no columns bound. An empty grid on purpose.

What a generated column looks like

  • key and prop — the object key.
  • name — a humanized label: firstName → First Name, api_url → API URL.
  • dataType — read from the data, so sorting, filtering and cell text all match what the column holds.
  • sortable and resizable.
  • Never editable. Editing is something you ask for, not something a grid decides for your data.
  • No width — the sizing modes decide. See Sizing Modes.

What it skips

  • Objects, arrays and functions — a nested value has no obvious cell text, so the key is left out rather than printed as [object Object]. Write that column by hand with a dotted prop.
  • Keys starting with __ — internal fields.
  • Column groups — nesting cannot be read from a flat object, so generation never invents one. Groups you write yourself pass through untouched. See Column Groups.

It reads the data once

Generation runs when the grid first has rows and then holds still. A row arriving later with a field nobody has seen does not grow the table under the reader — the layout they resized stays the layout they resized. When you do want the new field, ask for it:

grid.regenerateColumns();   // on a ViewChild of the grid component

Discovery reads the first 20 rows by default, so the cost is the same on twelve rows as on a hundred thousand. A key that appears only after those rows needs a wider sampleSize — or one hand-written column.

Options

readonly config: GridConfig<Shipment> = {
  autoGenerateColumns: {
    // Read 50 rows instead of 20 when discovering the keys.
    sampleSize: 50,
    // Never build a column for these.
    exclude: ['id'],
    // Your own header text. Return the key itself to leave it raw.
    headerFrom: key => key.replace(/([a-z])([A-Z])/g, '$1 $2'),
  },
};

Generating the definitions yourself

The same function the grid runs is exported, so you can generate once, adjust the result and pass it in as ordinary column definitions — useful when you want to store the layout, or show the user what was derived:

import { generateColumnDefs } from '@gridcraft/angular';

readonly columns = generateColumnDefs(this.rows).map(c =>
  c.key === 'weightKg' ? { ...c, width: 120 } : c);

Column widths, order, pinning and hidden state persist by column key, and a generated key is the object key — so a saved layout keeps matching across reloads. See Grid State.

Generation options

PropertyTypeDefaultDescription
config.autoGenerateColumnsboolean | AutoGenerateColumns—Derive column definitions from the data. Omitted, generation happens only when no columns are bound; true generates alongside supplied columns; false never generates.
autoGenerateColumns.sampleSizenumber20Rows read to discover the keys. A key that first appears after them does not become a column.
autoGenerateColumns.excludestring[][]Keys that never become columns.
autoGenerateColumns.headerFrom(key: string) => stringhumanizedHeader text for a key. A column's own name still wins over it.
columnsGridColumn[] | ColumnGroup[]—Optional. Bind nothing and the columns are generated; bind some and each merges field-by-field over the generated definition for the same key.
regenerateColumns()() => void—Re-derive the generated columns from the rows the grid holds now. No-op when generation is off.
generateColumnDefs(rows, options?)(rows, options?) => GridColumn[]—The exported function the grid itself runs. Generate the definitions in your own code, adjust them, and pass them in as ordinary columns.
humanizeKey(key)(key: string) => string—The default header label, exported so a custom headerFrom can fall back to it.