Building dynamic table with handlebars

13,956

Solution 1

Based on what you are describing, I think you would have no option other than to build an array of table headers (object keys) by iterating over every object in your array and filtering out all of the duplicates. In other words, you must build an array of all of the unique keys from all of your data objects and these will serve as the column headings in your table. You could use an existing library to help yo do this, like Underscore's .uniq, but I will provide an example implementation:

var uniqueKeys = data.reduce(function (acc, obj) {
    return acc.concat(Object.keys(obj).filter(key => acc.indexOf(key) === -1));
}, []);

Next, we must pass our array of unique keys to our template. The unique keys will be used to create the table headings as well as in lookup operations for each object in data.

template({
    uniqueKeys: uniqueKeys,
    data: data
});

Our template must be updated to the following:

<thead>
    <tr>
        {{#each uniqueKeys}}
            <th>{{this}}</th>
        {{/each}}
    </tr>
</thead>
<tbody>
    {{#each data}}
        <tr>
            {{#each ../uniqueKeys}}
                <td>{{lookup .. this}}</td>
            {{/each}}
        </tr>
     {{/each}}
</tbody>

Note that the line {{lookup .. this}} is saying, "render the value on the current object in data at the property with the name of this key in uniqueKeys.

Also note that I added <tr> tags within your <thead>. According to MDN, these are the only permissible tags within a <thead>.

I have created a fiddle for reference.

Solution 2

Would something like this work for what you're trying to accomplish?

{
  "titles": [
    "The Room",
    "Test"
  ],
  "tableObjs": [{
      "director": "Tommy Wiseau",
      "genre": "Drama?",
      "rating": "1"
    },
    {
      "director": "Tommy Wiseau",
      "genre": "Drama?",
      "rating": "1"
    }
  ]
}
<thead>
  {{#each titles}}
  <th>{{@key}}</th>
  <!--property name here-->
  {{/each}}
</thead>
<tbody>
  {{#each tableObjs}}
  <tr>
    <td>{{this.director}}</td>
    <td>{{this.genre}}</td>
    <td>{{this.rating}}</td>
    <!--property value here-->
  </tr>
  {{/each}}
</tbody>
Share:
13,956
Kazura
Author by

Kazura

Updated on June 04, 2022

Comments

  • Kazura
    Kazura almost 2 years

    I am trying to create a dynamic table with handlebars.js. An example of an object I want to put in the table is:

    Object { title: "The Room", director: "Tommy Wiseau", genre: "Drama?", rating: "1"}
    

    The HTML I would like to look like:

        <thead>
        <th>Title</th>
        <th>Director</th>
        <th>Genre</th>
        <th>Rating</th>
        <th>Edited</th>
      </thead>
      <tbody>
        <tr>
          <td>The Room</td>
          <td>Tommy Wiseau</td>
          <td>Drama?</td>
          <td>1</td>
          <td>2017-05-16</td>
        </tr>
      </tbody>
    

    My problem is that the user can also add their own table headers, so i can't for example just do:

    <td>{{title}}</td>
    

    It has to be done dynamically. I have looked at: {{#each}}, but just can't seem to wrap my head around around it. Also, how can I print the property name instead of just the value in an object (like I want in the table headers).

      <thead>
        {{#each data}}
        <th>{{@key}}</th><!--property name here-->
        {{/each}}
      </thead>
      <tbody>
        {{#each data}}
        <tr>
          <td>{{this}}</td><!--property value here-->
        </tr>
        {{/each}}
      </tbody>
    

    This is my template. I feel like I am closing in on the right solution, but I am not sure where I am doing something wrong.

    $.getJSON("php/loadItems.php?category=" + selected, function(data) {
        let source = $("#itemTemplate").html();
        let template = Handlebars.compile(source);
        delete data[0].id
        $("#tableContainer").append(template(data));
      });
    

    This is how I handle the data in my JS. Any help is greatly appreciated!

  • Kazura
    Kazura almost 7 years
    I'm afraid not. Since the user can add stuff themselves I would not be able to know what the property names are. In this instance they might add another category; say "Notes" where they could write what they want. I would have no idea what the property name would be then
  • JohnK
    JohnK about 4 years
    Tremendous help! Thank you. Btw you can also get an array of object keys using Object.assign(summaryObj, obj) to accumulate keys from each obj in summaryObj, and then Object. keys(summaryObj) gives the array of keys.