3

I'm getting to know Handlebars.js and I would have a question for the community. I know I have a lot more to learn and I'm on my way, but I'd like to see an example to this problem.

The array created in JS with the objects:

var data = 
[
{
    Field: "id",
    Type: "int(11)",
    Null: "NO",
    Key: "PRI",
    Default: null,
    Extra: "auto_increment"
},
{
    Field: "id2",
    Type: "int(131)",
    Null: "N3O",
    Key: "PR3I",
    Default: null,
    Extra: "auto_increment"
}
];

The format is this because the JSON I receive from the server will look like the exact way, but now for testing I didn't want to make an ajax call.

The template:

<table>
        <thead>
        <tr>

        {{#each this}}
           {{#only_once this}}
            {{#key_value this}}
                <th>{{key}}</th>      
            {{/key_value }}
            {{/only_once}}
        {{/each}}

        </tr>    
        </thead>
...

Because the objects are in an array, I have to loop firstly the array with {{#each}} then there comes a registered helper (I found on github) that help me get the key because I want to write only them to the thead.

Without my if statement it works fine, fill in the thead with the keys, but because there are 2 objects, it prints out the names twice.

My problem is that I want to print them only once and an if would solve my problem that checks if the index of the array is greater than 0 to stop printing out the data, but..

.. Handlebars doesn’t support conditional statements, so code like {{#if x > y}} isn’t possible. What do you guys think would be the best solution for it?

Handlebars.registerHelper("only_once", function(item, fn){
    var buffer;
    var i = 0;

    if (i > 0) {
        buffer = false;
    }

        i++;

    return buffer;
});

Well, I tried to write a helper, but I think I did something wrong. My theory was that I give to my if the 'this' in the template as it (I think) points back to the array and then increase the i to check if the index of the array is > than 0, finally if it's true than send back a false - so I thought it will say to the if that don't run the code inside, but I though wrongly.

ThePianist
  • 611
  • 2
  • 9
  • 14

2 Answers2

5

As stated in this other SO answer and as @SimonBoudrias mentioned in his answer, since Handlebars 1.1.0, {{@first}} is natively supported by {{#each}} helper.

Therefore, you can print all attribute names for the first object in an array by using only handlebars native helpers as follows:

{{#each array}}
    {{#if {{@first}}}}
        <!-- It is the first object on the array, print the key for each attribute -->
        {{#each this}}
            <th>{{@key}}</th>
        {{/each}}
   {{/if}}
{{/each}}

Additional note about adding conditional if statements to Handlebars:

Handlebars is a logic-less templating system so it does not include logical statements.

Still, if you want to do it using templates and Handlebars, you could solve this by writing a helper, as explained in this SO answer. In your case, the helper could be something like:

Handlebars.registerHelper('ifIsZero', function(value, options) {
  if(value === 0) {
    return options.fn(this);
  }
  return options.inverse(this);
});

Then, you can call it in your template as follows to do something only if the index is equal to 0:

{{#each array}}
    {{#ifIsZero {{@index}}}}
        <!-- @index is equal to 0, do something -->
        <!-- eg. print the key for each attribute of the object using {{@key}} -->
        {{#each object}}
            <th>{{@key}}</th>
        {{/each}}

    {{else}}
        <!-- otherwise, do something else -->

    {{/ifIsZero}}
{{/each}}
Community
  • 1
  • 1
veducm
  • 5,933
  • 2
  • 34
  • 40
  • This is a stretch. Use underscore templates if you want to do this. Use Handlebars if you want logic less. – Simon Boudrias Jan 13 '14 at 15:18
  • @SimonBoudrias: you are right it is a stretch adding conditional if statements, that's why I mentioned it in the first line on the answer. Anyway, I have updated my answer to use the `first` helper as you mention in your answer, so the proposed solution uses native helpers only. – veducm Jan 22 '14 at 11:43
3

Handlebars offers helpers when you're looping an array. I don't think you'll need any custom helpers.

  • {{ @index }} return the index (0, 1, 2...)
  • {{ @key }} return the key (Field, Type, etc)
  • {{ @first }} boolean to mark if this is the first row in the array
  • {{ @last }} boolean to mark if this is the last row in the array
Simon Boudrias
  • 42,953
  • 16
  • 99
  • 134
  • I saw it in the documentation of Handlebars, but I always get an error on console: Uncaught Error: Parse error on line 7: ...ch this}} {{@index}} ----------------------^ Expecting 'ID', got 'undefined' – ThePianist Jan 13 '14 at 15:16
  • Should I use something else instead of 'this'? I tried to use 'data' but it doesn't recognized it. In examples I only saw using {{#each}} inside an object to loop through inside an array or something.. – ThePianist Jan 13 '14 at 15:18
  • When you loop the object, use `@key` not `@index` – Simon Boudrias Jan 13 '14 at 15:19
  • Looks like your data object is not what you think it is. Use `{{log this}}` to check what is really is ([doc](http://handlebarsjs.com/#log)). And make sure you use latest Handlebars release. – Simon Boudrias Jan 13 '14 at 15:31
  • Oh yeah, damn what a silly error!! :) I used a cdn from a github example which pointed to the beta version of Handlebar, now I tried again the @index and it works perfectly! Although my other problem still occurs, so I'll check out the possibilities with Underscore.js. – ThePianist Jan 13 '14 at 16:14