Binding tables
Tables are a special case because the HTML structure itself carries meaning: headings define columns, rows define records, and sometimes cells already contain their own markup.
DomTemplate’s table binder supports a few different shapes so we can choose the level of control we need.
1. Binding to an empty table
HTML:
<table data-bind:table="weather"></table>
PHP:
$binder->bindTable([
["Day", "Weather"],
["Mon", "Rain"],
["Tue", "Cloud"],
["Wed", "Sunny"],
], bindKey: "weather");
If we do this, DomTemplate creates the <thead>, <tbody>, rows, and cells for us.
Output HTML:
<table>
<thead>
<tr>
<th>Day</th>
<th>Weather</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mon</td>
<td>Rain</td>
</tr>
<tr>
<td>Tue</td>
<td>Cloud</td>
</tr>
<tr>
<td>Mon</td>
<td>Sunny</td>
</tr>
</tbody>
</table>
2. Using column-oriented data
The same table can also be expressed with headings as keys:
$binder->bindTable([
"Day" => ["Mon", "Tue", "Wed"],
"Weather" => ["Rain", "Cloud", "Sunny"],
], bindKey: "weather");
This is often a natural fit when data already arrives from another layer grouped by column.
3. Binding to a table with existing headings
HTML:
<table data-bind:table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
</table>
If a table already has a <thead>, those heading cells define which columns are shown and in what order.
That means we can safely bind a wider data set and only expose the columns we actually want in the view.
4. data-table-key
Similarly to above, the table’s existing headers can be used to define which columns are shown, but the heading’s text is not the same as the data’s key.
HTML:
<th data-table-key="firstName">First name</th>
<th data-table-key="email">Email address</th>
This lets us keep friendly UI copy while still mapping the correct data fields.
5. Binding to existing row markup
For more complex tables, we can define the row markup ourselves and let DomTemplate clone it.
HTML:
<table>
<thead>
<tr>
<th>Delete</th>
<th data-table-key="id">ID</th>
<th data-table-key="name">Name</th>
<th>Flag</th>
</tr>
</thead>
<tbody>
<tr data-list>
<td>
<form method="post">
<input type="hidden" name="id" data-bind:value="@" />
<button name="do" value="delete">Delete</button>
</form>
</td>
<td></td>
<td></td>
<td>
<form method="post">
<input type="hidden" name="id" data-bind:value="@name" />
<button name="do" value="flag">Flag</button>
</form>
</td>
</tr>
</tbody>
</table>
Note that the tr has the data-list attribute, indicating that it is the tr within the tbody that we want to repeat for each record. Here we can see table binding and ordinary element binding working together. The data fills the ID and name cells, but the surrounding forms remain in place.
6. Double-header tables
DomTemplate also supports what the tests call a “double header” structure: one heading row across the top, and another heading value at the start of each body row.
PHP:
$binder->bindTable([
["Item", "Price", "Stock"],
[
"Washing machine" => ["698.00", "24"],
"Television" => ["998.00", "7"],
"Laptop" => ["799.99", "60"],
],
]);
In this shape:
- the first inner array is the top heading row
- each subsequent associative row uses its key as a body
<th> - the remaining values become body
<td>cells
That is useful when the left-most column is itself a row heading rather than ordinary cell data.
Supported input shapes
The public bindTable() method accepts any iterable that can be normalised into one of these shapes:
- row-oriented arrays where the first row contains headings
- associative row lists
- heading => value-list maps
- double-header data
- traversable equivalents
If the structure is not compatible, DomTemplate throws IncorrectTableDataFormat.
Bind key matching
If multiple tables exist in the same document, give them explicit bind keys:
<table data-bind:table="sales"></table>
<table data-bind:table="returns"></table>
$binder->bindTable($salesTable, bindKey: "sales");
$binder->bindTable($returnsTable, bindKey: "returns");
The bind key can also live on an ancestor wrapper instead of the table itself.
We have now covered the three major data shapes. Next, move on to conditional and optional elements to tidy up the output and deal with parts of the page that may disappear completely.