javascriptember.jseachhtmlbars

Ember double each loop. Using value of inner each loop variable to bind to property named the same in the variable of the outer each loop


To elaborate on the title, what I am trying to achieve is the following.

I am building an interactive table component in Ember. Here is the stripped template:

<table>
    <thead>
        <tr>
            {{#each header in headers}}
                <th>{{header}}</th>
            {{/each}}
        </tr>
    </thead>
    <tbody>
        {{#each row in rows}}
        <tr>
            {{#each header in headers}}
                <td>{{input value=row.[header]}}</td> <!-- can I bind to row.header here somehow? -->
            {{/each}}
        </tr>
        {{/each}}
    </tbody>
</table>

I want each input field for a specific row and specific column, to be bound to that row's row object, specifically to a property named the way the header column is named.

Essentially, I want to use the value of the header variable to bind a property in the row object called by that value (If current header has value 'title' then I want to bind to row.title)

Here is an example of how I initialize these objects:

var headers = ['title','description'];

var rows = [],
    row = {};

for(var i = 0; i < headers.length; i++) {
    row[headers[i]] = '';  // this line does similar thing to what I am trying to achieve
}

rows.push(row);

/* This gives

    rows = [
        {
            title: '',
            description: ''
        }
    ]
*/

After researching, I found this in the Handlebars documentation that says I can access properties like this:

{{#each articles.[10].[#comments]}}
    ...
{{/each}}

Which is, according to the docs, pretty much the same as:

articles[10]['#comments']

However, using:

rows.[header] 

doesn't work for me because it tries to literally access the 'header' property of the rows object (i.e. rows.header) and not the value contained in the header variable.


Solution

  • This functionality can now easily be achived in Ember (since 1.13) using the new and awesome inline helpers mut and get:

    The way to achieve this is in two basic steps:

    1. Use get to dynamically lookup a property from r named as whatever the value of h is at that each iteration. For example, if h = 'title', then this would return r['title'].
    2. Use mut to specify that this extracted value is mutable by our input component (specifically its value property).

    This is how the whole each looks:

    {{#each rows as |r|}}
    <tr>
      {{#each headers as |h|}}
      <td>
        <input onkeyup={{action (mut (get r h)) value="target.value" }}>
      </td>
      {{/each}}
    </tr>
    {{/each}}
    

    Detailed example on Ember Twiddle