Skip to main content

Alpine table configuration

This guide explains how the Alpine-driven data tables in ClubPal are structured. The new approach keeps the visual layout defined in Razor while exposing column metadata through HTML data-* attributes that Alpine reads during component initialisation. Shared UI elements, such as the pagination footer, live in a reusable partial so every table gets the same controls without copy and paste.

Razor markup

Each table that uses the Alpine component must:

  • Wrap the table inside an element with x-data="membersTable()" (or the relevant component) and add data-alpine-table to the <table> element so the component can locate the header metadata.
  • Add data-column-* attributes to each <th> that participates in exports or client-side logic. The key attributes are:
    • data-column-key: The property name returned from the API payload.
    • data-column-label: Human readable label used for exports when the header text is not a simple string.
    • data-sortable and data-sort-key: Whether the column supports sorting and which property should be passed to the server.
    • data-exportable: Set to false to omit columns from CSV/XLSX/print exports (for example, the Actions column).
    • data-renderer: Logical renderer name (member, date, dateTime, membershipStatus, trueFalse, etc.) that is resolved in JavaScript.
    • Optional data-align or additional flags can be added later without touching JavaScript.
  • Provide extra, non-visual metadata columns inside a hidden <tr class="d-none"> so exports can include values that do not appear in the UI (for example emailAddress, groups, consentToContact).
  • Reference the shared pagination footer partial: @await Html.PartialAsync("Shared/_AlpineTableFooter").

Areas/Admin/Pages/Shared/_AlpineTableFooter.cshtml contains the pager UI. The partial expects the Alpine component to expose page, pageSize, totalCount, pageSizeOptions, visiblePages, changePage, and changePageSize. Because the markup lives in one place, any future style or accessibility changes only have to be made once.

JavaScript component

Scripts/pages/admin/membership/membersAlpine.js performs the dynamic work:

  • initColumns() locates table[data-alpine-table], reads every header with data-column-key, and builds the columns collection used by the export utilities.
  • resolveRenderer() maps the data-renderer values to actual render functions from table-renderers.js. Renderers always return a function with the signature (value, row, isExport) so exports and on-screen rendering stay consistent.
  • Dynamic form-field columns are appended after the static metadata so custom fields automatically appear in exports without altering the Razor view.
  • Export buttons call initExportButtons with the computed column list, ensuring CSV/Excel/print output stays in sync with the markup.

When adding a new Alpine table:

  1. Create the table markup in Razor with the necessary data-column-* metadata.
  2. Include the shared footer partial beneath the table.
  3. Ensure the Alpine component reads the same metadata (either by reusing initColumns() or adapting it for the new component).
  4. Provide renderer mappings for any new data-renderer values.

Following this pattern keeps HTML and JavaScript definitions aligned, makes the UI reusable, and avoids duplicating column definitions across multiple tables.