Chapter 20 of 34

Sorting: double-click and own comparators

Locale-aware names, a workflow order for status – and sorting from your own button.

  • ⏱ 6 min
  • ▶ Video episode 020
  • git tag ep-020
  • needs @guiexpert/table@1.1.90

After this chapter you can

  • make columns sortable
  • write a sortComparator
  • sort from code with sortByColumn() and sort()

Double-click on the header

A column with sortable sorts on a double-click on its header: ascending → descending → ascending … Without a comparator, values are compared with < / > – fine for numbers and ISO dates, not for names or workflow states.

Own comparators

src/model/order-columns.ts
import { ColumnDef, TrueFn, px110, px120, px180 } from "@guiexpert/table";
import type { Order } from "./data/orders";
const STATUS_ORDER: Order["status"][] = ["open", "paid", "shipped", "cancelled"];
const rank = (s: Order["status"]) => STATUS_ORDER.indexOf(s);
const customer = ColumnDef.create({
property: "customer", headerLabel: "Customer", width: px180, sortable: TrueFn,
sortComparator: (a: string, b: string) => a.localeCompare(b, "de") // "Müller" before "Nordlicht"
});
const status = ColumnDef.create({
property: "status", headerLabel: "Status", width: px110, sortable: TrueFn,
sortComparator: (a: Order["status"], b: Order["status"]) => rank(a) - rank(b) // workflow order
});
const total = ColumnDef.create({
property: "total", headerLabel: "Total", width: px120, sortable: TrueFn,
sortState: "desc" // only the icon – see doSort() below
});
  • sortComparator gets two cell values (and the two rows, if you need them) and returns a negative number, 0 or a positive number – like Array.prototype.sort.
  • localeCompare(b, "de") sorts “Müller” before “Nordlicht” – the default comparison puts umlauts at the end.
  • A rank array sorts status values in workflow order: open → paid → shipped → cancelled.

Sorted from the start

sortState only sets the arrow in the header. Sort the rows once to match it, with TableModelIf.doSort() and a SortItem:

const tableModel = TableFactory.createTableModel({ rows: orders, columnDefs });
const index = columnDefs.indexOf(total);
tableModel.doSort([new SortItem(index, "desc")]); // sort once to match the icon

Sorting from code

// like a double-click on the header: icon included, without a direction it toggles
statusButton.onclick = () => tableApi.sortByColumn("status");
totalButton.onclick = () => tableApi.sortByColumn("total", "desc");
// any comparator on the row objects (clears the header icons – they would not match)
newestButton.onclick = () => {
tableApi.sort((a: Order, b: Order) => b.date.localeCompare(a.date));
});
  • TableApi.sortByColumn() – exactly like a double-click: by property name or column index, with the header icon. Without a direction it toggles; sortByColumn("total", "desc") sets it.
  • TableApi.sort() – any comparator on the row objects, e.g. by two fields. The header icons are cleared, because they would not match.
  • TableApi.reSort() – after data changes (chapter 31 (Inline editing)) apply the last sort again.
Live Sorting 100,000 orders
Screenshot: Sorting 100,000 orders Screenshot: Sorting 100,000 orders
Double-click “Customer”, then “Status”. Try the buttons – the toolbar shows how long sorting 100,000 rows takes.

Next

chapter 21 (Filtering) – a live search over 100,000 rows.

API used in this chapter