On the API design of components for rendering HTML tables or tabular data (like ember-light-table, etc.).
Most of these APIs (if not all) require you to pass a configuration object containing the columns you want to display.
columns: [columnSpec({...}), ..., columnSpec({...})]
Furthermore, there is often a table configuration object needed, too.
tableOptions: tableSpec(...)
Then you render the component in your template by passing those objects as options to the table component. (edited)
{{some-table-component tableOptions=myTableOptions columns=myColumns data=myData ...}}
Problems IMO with this approach:
- It is not clear in the template what the presentation of the data (
myData
) is without looking in the component’s JS file. - Cannot easily customize the presentation of the table without knowledge of JavaScript
- it is not declarative.
The component design I would propose would like something like this:
{{#display-table data=theData as |table|}}
{{table.column property='name'}}
{{#table.column sortable=true sortKey='foo' as |item|}}
{{some-aweome-component model=item.foo}}
{{/table.column}}
{{/display-table}}
(most basic example)
Key properties are:
- the column specification is declarative. You can see it directly in the template. no javascript is needed.
- if a column’s content is more than simply rendering the property as a string, then you use a block form with the
table.column
contextual component. - i think this API is rather elegant, terse, easy to understand.
Problems with the simple version I presented above: Where is {{table.footer
, {{table.pagination
? For those, would i need to wrap the columns in an outer {{table.columns
?
Perhaps a more extensible but slightly more verbose for the common case (no pagination, no footer - just rows of data).
{{#display-table data=theData as |table|}}
{{#table.empty}}This table has no data{{/table.empty}}
{{#table.columns header-classes='some-header-classes' even-class='some-even-class' odd-class='some-odd-class'}}
{{table.column property='name'}}
{{#table.column title='Foo column' sortable=true sortKey='foo' as |item|}}
{{some-aweome-component model=item.foo}}
{{/table.column}}
{{/table.columns}}
{{table.pagination page=myPageNumber total=myTotalCount change=(action 'handleChangingPage')}}
{{/display-table}}
I guess even the above is missing header column customization (other than the title). But that could be added straightforwardly to the scheme above.
Questions:
- Is this a good idea?
- What challenges do folks anticipate to writing a table component with the api presented above? (The challenge i see is how to parse the column definitions out first for rendering the header and then executing the same body for rendering the actual data.)
Here’s one possible work around for the challenge I describe in question 2 above.
That would just build up the table configuration object used by all the other frameworks. Then you would need a separate component to actually render the table:
{{render-table table=tableConfig}}
If the {{render-table
kludge could be eliminated, great. But even that two-step process is better than defining the column definitions in the component’s JavaScript instead of in the template.
I know that’s a lot, but I’m done writing. Thoughts?
I intend to hack on something like this when I get a chance.