ember-table
ember-table copied to clipboard
Best practice for formatting cells
Hi,
I need to perform some minor formatting on my data before outputting the cell contents. For example I need to write timestamps in a more compact form, output numbers in engineering format, etc... without the need to instantiate a fully fledged component...
To me the proper place would be in the template, however I find it difficult to case/switch within the cell component (also because of handlebars lack of such constructs).
How would approach this kind of situations? Would you still use a custom component to render the cell?
Hey @vihai, thanks for trying out Ember Table!
There are really two options here. You can use custom components for each type of cell, as you mentioned, or you can use handlebars helpers with if statements to change the template. To do this, you would need to introduce some extra helpers such as ember-truth-helpers (which I highly recommend):
<EmberTable as |t|>
<t.head @columns={{columns}} />
<t.body @rows={{rows}} as |b| />
<b.row as |r| />
<r.cell as |cellValue columnValue|>
{{#if (eq columnValue.type 'string')}}
{{format-string cellValue}}
{{else if (eq columnValue.type 'number')}}
{{format-number cellValue}}
{{/if}}
</r.cell>
</b.row>
</t.body>
</EmberTable>
This is fairly verbose, but perhaps not as heavy as using custom components. I could also imagine a component that acts as a switch/case (or even better, a pattern matcher):
{{#pattern-match (typeof value) as |when|}}
{{#when 'number'}}
{{/when}}
{{#when 'string'}}
{{/when}}
{{/pattern-match}}
But would have to think about that a bit more, and it would likely be its own library.
The way I'm doing this is setting attributes on the column definition. I was using the truth helpers but wound up removing it.
{{#if columnValue.isStatus}}
<!-- my Status column code or component -->
{{else if columnValue.isMedication}}
<!-- my Medication column code or component -->
{{else if columnValue.isDirections}}
<!-- my Directions column code or component -->
{{/if}}
with column definition:
let columns = [
{
name: '',
valuePath: 'reviewed',
isStatus: true,
cssClass: 'reviewed'
}, {
name: 'Medication',
valuePath: 'nameInfo',
isMedication: true,
cssClass: 'medName'
}, {
name: 'Directions for Use',
valuePath: 'directions',
isDirections: true,
isSortable: false,
cssClass: 'directions'
}, {
name: 'Related Condition',
valuePath: 'condition',
isCondition: true,
cssClass: 'condition'
}, {
name: 'Prescriber',
valuePath: 'prescriber',
isPrescriber: true,
cssClass: 'prescriber'
}, {
name: '',
isDelete: true,
isSortable: false,
cssClass: 'delete'
}];
I created a template only glimmer component which removes some of the perf problems of a regular component.
Here is what that component looks like:
{{#if (eq @column.formatter 'date')}}
{{format-date @value @column.format}}
{{else if (eq @column.formatter 'number')}}
{{format-number @value [email protected]}}
{{else if (eq @column.formatter 'percent')}}
{{format-percent @value [email protected] [email protected]}}
{{else if (eq @column.formatter 'money')}}
{{format-money @value @column.format [email protected]}}
{{else if (eq @column.formatter 'titleize')}}
{{titleize @value}}
{{else}}
{{@value}}
{{/if}}
My columns definition look like this:
...
{
name: 'Duration',
valuePath: 'duration',
formatter: 'number',
decimals: 0
},
...
And the Ember Table call looks like this:
<t.body
@rows={{this.dataRows}}
@key="id" as |b|>
<b.row as |r|>
<r.cell as |value column|>
<TableCellFormatter @value={{value}} @column={{column}} />
</r.cell>
</b.row>
</t.body>
Note: I have seen some performance regression in sorting, for example, it re-renders all the cells component, so it will go over all of my formatters again. I wounder if the @key param might be helping or not with performance re-renders. (I need to pass key because my data is not Ember Data models, I'm using GraphQL for my data.)
Thank you for the various solutions! I now feel more confident I am not missing something obvious :)