How to pass in a template as a property

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 :+1: