Skip to main content

Sortable table partials

These partials follow the shared rules in Server templates. The tabs remember your framework, and a link can choose it with ?server-framework=laravel.

The server sorts the rows, so each sortable header is a link to the same page with the other sort order. The partial renders one header cell: its data-rc-sort-key, aria-sort when the table is sorted by that column, and a link that asks for the next direction. That is the reverse direction on the sorted column, and on any other column its first direction, ascending unless first is descending. The page wraps the table in <rc-data-table> with the current sort-key and sort-direction.

app/views/rc/_sort_header.html.erb
<%# locals: (key:, label:, sort:, direction:, href:, first: "ascending") -%>
<th scope="col" <%= tag.attributes(data: { rc_sort_key: key, rc_sort_first: (first if first == "descending") }, aria: { sort: (direction if key == sort) }) %>>
<a href="<%= href.call(key, rc_next_sort_direction(key, sort, direction, first: first)) %>"><%= label %></a>
</th>
<% sort_href = ->(key, direction) { members_path(sort: key, dir: direction) } %>
<rc-data-table sort-key="<%= @sort %>" sort-direction="<%= @direction %>">
<table>
<caption>Members. Column headers with links sort the table.</caption>
<thead>
<tr>
<%= render "rc/sort_header", key: "name", label: "Name",
sort: @sort, direction: @direction, href: sort_href %>
<%= render "rc/sort_header", key: "joined", label: "Joined", first: "descending",
sort: @sort, direction: @direction, href: sort_href %>
<th scope="col">Role</th>
</tr>
</thead>
<tbody>
<%= render @members %>
</tbody>
</table>
</rc-data-table>

Read the sort key and direction from the query string through an allowlist before they reach the database query, and fall back to the default order for any other value:

SORTS = { "name" => :name, "joined" => :created_at }.freeze

@sort = SORTS.key?(params[:sort]) ? params[:sort] : "name"
@direction = params[:dir] == "descending" ? "descending" : "ascending"
@members = Member.order(SORTS[@sort] => @direction == "ascending" ? :asc : :desc)