1

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.

nem035
  • 34,790
  • 6
  • 87
  • 99
  • 1
    By design, Handlebars does not permit this degree of "logic" in templates. You'll have to do some prepping of your data in the controller or view, to get it into a shape where Handlebars can spit it out easily. –  Jan 09 '15 at 03:49
  • Thanks, I wasn't sure if there was some way of accessing these properties that isn't documented by Handlebars or at least not in the obvious places. Even though I would prefer doing things the way I mentioned here and it would be much cleaner that way, I coded a workaround that does what I need. – nem035 Jan 09 '15 at 16:03

2 Answers2

0

You can always extend the textfield component and at least get the value you are looking for.

App.DynamicInputComponent = Ember.TextField.extend({
  row: null,
  col: null,
  value: function(){
    var row = this.get('row');
    var col = this.get('col');

    return row[col];
  }.property('row', 'col')
});

Then, in your template you can do:

<table>
  {{#each item in model}}
  <tr>
    {{#each col in columns}}
      <td> {{ dynamic-input type='text' row=item col=col }} </td>
    {{/each}}
  </tr>
  {{/each}}
</table>

(Partially) working solution here

Kalman
  • 8,001
  • 1
  • 27
  • 45
  • Not exactly what I need. I am not interested in the value of `row[col]` but i want to bind the value of the input field to the property `col` in the `row` object so it is automatically computed by Ember each time the user types something in. – nem035 Jan 09 '15 at 15:59
0

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

nem035
  • 34,790
  • 6
  • 87
  • 99