javascriptember.jsember-1

Iterating through key-values in ember.js handlebars template


I have a javascript object

this.attributes = {
            key: value,
            // etc..
        }

I would like to iterate through it and output key:value

Here is my solution:

import Ember from 'ember';

export default Ember.Component.extend({
init() {
    this._super(...arguments);
    this.attributes = {
        'SKU': '123',
        'UPC': 'ABC',
        'Title': 'Hour Glass'
       }

},

ProductAttributes: Ember.computed('attributes', function() {

    var attribs = this.get('attributes');
    var kvp = Object.keys(attribs).map(key => {
        return {
            'attribute_name': key,
            'attribute_value': attribs[key]
        };
    });
    return kvp;
})});

The template I came up with:

{{#each ProductAttributes as |attribute|}}
    {{attribute.attribute_name}} : {{attribute.attribute_value}}
{{/each}}

I am not happy with this solution since it looks cumbersome: first I convert object into an array of auxiliary objects with non-dynamic keys attribute_name and attribute_value, and then I references non-dynamic names directly within my template.

It works fine but is there a better way of doing this?


Solution

  • My suggestion for this is not that different from the solution you had already described in the question explanation; but my suggestion will provide a you a more reusable and more each-in helper-like approach:

    How about creating a tagless contextual component with a positional param named each-in-component and moving all the computed property definition to that component. I am using kind of Ember 2.x syntax but I guess Ember 1.x will not be very different; so that component will be sth. like:

    import Ember from 'ember';
    
    export default Ember.Component.extend({
      objectProperties: Ember.computed('object', function() {
        let object = this.get('object');
        return Object.keys(object).map(key => {
            return {
                'key': key,
                'value': Ember.get(object, key)
            };
        });
      }),
    
      tagName: ''
    }).reopenClass({
      positionalParams: ['object']
    });
    

    and the corresponding component template will be yielding the computed property array:

    {{#each objectProperties as |objectProperty|}}
        {{yield objectProperty.key objectProperty.value}}
    {{/each}}
    

    So you can now use that component just like regular each-in; which does not exist in Ember 1.x.

    {{#each-in-component attributes as |key value|}}
        {{key}} : {{value}}<br>
    {{/each-in-component}}
    

    With this approach; you can re-use the very same component multiple times and the code you did not want to have in your very own component will be lying within each-in-component. I have wrapped up my solution to illustrate it in action in the following twiddle. I hope it works.