Chapter 09 of 34

GuiExpert Table in Angular 14 to 22

One source, nine builds: the package major follows your Angular major.

After this chapter you can

  • pick the right package version
  • use the standalone TableComponent
  • rebuild the table from a signal

Pick the right package version

The major version of @guiexpert/angular-table follows your Angular major. All nine packages are built from one source.

Your AngularInstall
22 (latest)npm i @guiexpert/table @guiexpert/angular-table@22
14 … 21the same major, e.g. Angular 17: npm i @guiexpert/table @guiexpert/angular-table@17 (dist tag a17)

The table CSS ships with the component – you don't import main.css in Angular.

The component

frameworks/angular/src/app/app.ts
// Episode 009 – the same orders model, rendered by the Angular wrapper (Angular 14–22, here 22).
import { ChangeDetectorRef, Component, computed, inject, signal } from "@angular/core";
import { RenderWrapperFactory, TableComponent } from "@guiexpert/angular-table";
import { GeMouseEvent, TableApi, TableOptions } from "@guiexpert/table";
import { createOrderTableModel } from "./model/order-table";
import { StatusChip } from "./status-chip";
@Component({
selector: "app-root",
imports: [TableComponent],
template: `
<header class="app-header">
<b>Angular 22</b>
<button (click)="toggle()">{{ rows() === 100000 ? "1,000 rows" : "100,000 rows" }}</button>
<span id="info">{{ info() }}</span>
</header>
<guiexpert-table class="table-div"
[tableModel]="model()" [tableOptions]="options"
(tableReady)="onReady($event)" (mouseClicked)="onClick($event)" />
`
})
export class App {
private readonly rwf = inject(RenderWrapperFactory);
private readonly cdr = inject(ChangeDetectorRef);
// episode 026: an Angular component per visible status cell
private readonly statusRenderer = this.rwf.create(StatusChip, this.cdr);
readonly rows = signal(100_000);
// a new model object → the table is rebuilt
readonly model = computed(() => createOrderTableModel(this.rows(), { status: this.statusRenderer }));
readonly options = { ...new TableOptions() };
readonly info = signal("");
toggle(): void {
this.rows.update(n => (n === 100_000 ? 1_000 : 100_000));
}
onReady(api: TableApi): void {
this.info.set(`${api.getBodyModel().getRowCount().toLocaleString("en")} orders`);
}
onClick(evt: GeMouseEvent): void {
this.info.set(`row ${evt.rowIndex}, column ${evt.columnIndex}`);
}
}
src/styles.css
html, body { margin: 0; font: 14px/1.4 system-ui, sans-serif; }
.app-header { height: 40px; box-sizing: border-box; padding: 8px 12px; background: #111827; color: #f9fafb; display: flex; gap: 24px; }
#info { color: #f472b6; font-family: ui-monospace, monospace; }
/* the custom element needs display: block and a fixed height (the table CSS ships with the component) */
guiexpert-table.table-div { display: block; height: calc(100vh - 40px); }
/* Episode 025 – status badge */
.status-badge { display: inline-block; margin-top: 6px; padding: 0 10px; line-height: 20px; border-radius: 10px; font-size: 12px; font-weight: 600; }
.status-open { background: #e0e7ff; color: #3730a3; }
.status-paid { background: #dcfce7; color: #166534; }
.status-shipped { background: #fce7f3; color: #9d174d; }
.status-cancelled { background: #f3f4f6; color: #6b7280; text-decoration: line-through; }
Angular 22 with signals: the button changes the signal, computed() creates a new model, the table is rebuilt.
Angular 22 with signals: the button changes the signal, computed() creates a new model, the table is rebuilt.

Rules for Angular

  • TableComponent is standalone – put it into imports. Selector: <guiexpert-table>.
  • It uses OnPush and initialises outside the Angular zone: scrolling 100,000 rows does not trigger change detection.
  • A new [tableModel] object rebuilds the table (ngOnChanges) – here via computed(); ngOnDestroy cleans up.
  • Outputs: (tableReady), (mouseClicked), (selectionChanged), (contextmenu), (mouseDraggingEnded) … $event is the TableApi resp. GeMouseEvent.
  • The element needs display: block and a height.

Angular components inside cells

RenderWrapperFactory.create(MyComponent, cdr) turns a component that implements ComponentRendererIf into a cell renderer – see chapter 26 (Components as cells).

Next

Svelte, Solid, Preact and a side-by-side comparison of all wrappers: chapter 10 (Svelte, Solid, Preact).

API used in this chapter