Hi, I have a table component that accepts columns and I would like to pass as a part of a column definition how the content of that column should be rendered.
<CustomTable
@columns={{array
(hash
title='Name'
template=(user => <strong>{{user.name}}</strong>) // <- here
)
}}
@items={{this.users}}
/>
Is there a way to achieve this?
That looks like it might need to be a Helper and when called internally, it executes the Helper (function) and returns out the html safe elements you want to display. Effectively, when @columns[0].template
in invoked a function (Helper) is called and spits out what should be displayed.
Creating a helper or a component every time I need to display something in table cell is a lot of boilerplate plus those helpers/components will be used only on one specific place, they are not reusable. I would like to have something that is within the context of the CustomTable declaration.
Hi @ondrejsevcik!
It looks like you’re used to React? (I know React! (2016-2019))
In particular, idk if you’ve seen this: https://emberatlas.gitbook.io/emberatlas/learning/coming-from-another-ecosystem/ember-for-react-developers
but it may be helpful? It may also be missing things, if it is, lemme know!
But to answer your question, there are a couple of techniques:
<CustomTable
@items={{this.users}}
@columns={{array
(hash
title='Name'
template=(component 'custom-table/bold-header-cell' name=user.name}})
)
}}
/>
{{!
where:
- app/components/custom-table defines CustomTable
- app/components/custom-table/bold-header-cell defines 'custom-table/bold-header-cell`
}}
This uses the component
helper which allows you to pass a component as an argument.
It may seem “heavier” than having a free-floating template, but in the long run, the separation really pays off, and helps keep you organized.
Alternatively, you could use yielded components to compose your table at the invocation site:
<CustomTable as |table|>
<table.Header @columnNames={{this.columnNames}} as |column|>
<th>{{column}}/th>
</table.Header>
<table.Body @items={{this.users}} as |rowData|>
<td>{{rowData.name}}</td>
{{!-- <td>{{rowData.etc}}</td> --}}
</table.Body>
/>
{{! where custom-table: }}
<table>
{{yield (hash
header=(component 'custom-table/header')
body=(component 'custom-table/body')
)}}
</table>
{{! where custom-table/header: }}
<thead>
<tr>
{{#each @columnNames as |column|
{{yield column}}
{{/each}}
</tr>
</thead>
{{! where custom-table/body: }}
<tbody>
{{#each @items as |item|}}
<tr>
{{yield item}}
</tr>
{{/each}}
</tbody>
1 Like
Thanks for a exhaustive reply.
Actually, the first solution is what we currently use. It doesn’t scale well. We have a lot of tables and each table has some very specific cells that show content based on certain conditions or some other state or it’s an inline edit etc. This is almost impossible to generalize as there will be always a lot of special cases (like it often is in UI). That’s why I’m looking for a way to define column template right where the table is declared so it lives together and is not disconnected.
The second approach is better, but that would require a massive refactoring on my side. I was hoping that there is another solution.
The second approach is better, but that would require a massive refactoring on my side. I was hoping that there is another solution.
no need to do everything at once – you could totally migrate in phases.
first solution is what we currently use. It doesn’t scale well.
sounds like the second solution, giving template control to the calling context, is exactly what you need!
That’s why I’m looking for a way to define column template right where the table is declared so it lives together and is not disconnected.
the second example covers this