Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handlebars does not fill table

I am using Handlebars (v 1.0.0) to fill a table in HTML. However somehow it does not fill the table like I am used to.

Here is my template:

{{#if users}}
<table>
    {{#each users}}
        <tr>
            <td>{{username}}</td>
            <td>{{email}}</td>
        </tr>
    {{/each}}
</table>

{{else}}
    <h3>No users found!</h3>
{{/if}}

So I does find users because I do not see the "No users found!" and when I call an empty object it does show the "No users found!".

When I do not use the table and print out these users like the next example. The usersnames and mail address' will show up in my HTML.

{{#if users}}

    {{#each users}}

        {{username}}<br/>
        {{email}}<br/>

    {{/each}}


{{else}}
    <h3>No users found!</h3>
{{/if}}

Here is how my template is build in the javascript:

var htmlSource = $(data).html();
var template = Handlebars.compile(htmlSource);
var compiled = template(usersArray);
that.$el.html(compiled);

Now when I console.log the compiled object, it doesn't show the table already.

Do you know why it doesn't work and can you help me out?

EDIT:

I just tested some more and found that the data will show up in the HTML when I leave out the <table> tags. However the <tr> and <td> won't show up in html. The data in it will be shown.

EDIT 2: I found out that it seems to be a jquery issue or javascript issue. When I console.log the htmlSource the HTML template is changed to this:

{{#if users}}

    {{#each users}}

    {{/each}}


{{else}}
<h3>No users found!</h3>
{{/if}}

<table><tr>
   <td>{{username}}</td> 
   <td>{{email}}</td>
</tr></table>

As you can see the table is moved outside the if statement. I tried other jquery versions (2.0.2, 2.0.3, 2.0.1, 1.10.2) but this didn't work. I tried using the innerXHTML script however this works the same as jQuery.

So my guess is that it might be a FireFox issue (though I tried 25 and 26), Chrome does the same... maybe something in EcmaScript??

I will let you know soon...

EDIT 3:

I created an html file with the html I need. With a script I get the specific section of html I need and place this in the data variable.

Now when console logging the data (console.log(data)) there is nothing wrong. When console logging the data with jQuery, the html is altered: console.log($(data));

It seems something is going wrong there.. but only when using table tags. Is this something jQuery can't handle? I don't know. I know how to overcome this issue by using the script tag... Though I would like to load that using require ;)

P.S. nemesv thanks for you're edits ;)

like image 838
BonifatiusK Avatar asked Dec 16 '13 14:12

BonifatiusK


2 Answers

Make sure you're outputting your template in a tag so the browser don't try to parse it.

<script type="x-template" id="the-tpl">
    {{#if users}}
        <table>
            {{#each users}}
                <tr>
                    <td>{{username}}</td>
                    <td>{{email}}</td>
                </tr>
            {{/each}}
        </table>
    {{else}}
        <h3>No users found!</h3>
    {{/if}}
</script>

If there's no type attribute on the script tag, the HTML parser will find a bug and try to resolve it. From your bug details, it really looks like it is the case here.

Using require

As you noted you load using Require, than make sure you load your template using the text! plugin: require("text!path/to/template.html")

To be even fancier, you could delegate all the handlebars compilation to a handlebars template loading plugin so templates are precompiled inside your build - this is probably the best.

Either way, your issue is that your template get parsed by the HTML parser and that is breaking your template content. Make sure you load it as "text" via XMLHttpRequest or with require or inside a script tag correctly typed.

like image 114
Simon Boudrias Avatar answered Oct 17 '22 19:10

Simon Boudrias


Simon's solution is the right one and problem is discussed in various comments. I am just putting the pieces here.

as @rescuecreative pointed out some browsers remove empty <table> tags when the html is inserted in DOM.

The case here is a bit similar. The browsers do misbehave when they see invalid markup. but in this case its not because of missing tr, td inside table. its because of extra lines before tr in the table.

Your problem starts here.

var htmlSource = $(data).html();

whatever loading mechanism you are using, you inserting the template in the DOM before compiling it with handlebars.

This is what happens when you add uncompiled template to DOM.

{{#if users}}  <!-- browser does not understand this and tries to print it as it is-->
<table>  <!-- browser sees the table tag and immediately looks for child TR, tbody, thead -->
    {{#each users}}  <!-- but instead finds plain text and hence considers it as invalid markup -->
        <tr>   <!-- the same story is repeated again>
            <td>{{username}}</td>
            <td>{{email}}</td>
        </tr>
    {{/each}}  <!-- such plain strings are taken out of table markup and placed before or after depending upon the browser, in case of chrome placed before the table -->
</table>

{{else}}
    <h3>No users found!</h3>
{{/if}}

This is how chrome renders it -

{{#if users}}
{{#each users}}
{{/each}}
<table>
    <tbody>
        <tr>
            <td>{{username}}</td>
            <td>{{email}}</td>
        </tr>
    </tbody>
</table>
{{else}}
    <h3>No users found!</h3>
{{/if}}

I dont have firefox with me as of now. but i am sure firefox has its own markup correction method.

after this when you take it out of DOM using jquery and compile

var htmlSource = $(data).html();
var template = Handlebars.compile(htmlSource);

it will just output this

<table>
    <tbody>
        <tr>
            <td>{{username}}</td>
            <td>{{email}}</td>
        </tr>
    </tbody>
</table>

if you want you can dump it after compilation to see.

var compiled = template(usersArray);
console.log(compiled);
that.$el.html(compiled);

This also explains why you are getting username and email when you strip out table markup in the original template.

Now to solve this issue either use text plugin as Simon pointed out. or place the template inline.

I too use requriejs with something along the following

define([
  'underscore',   // you can replace this with handlebars
  'text!path/to/template.html'
], function(_, Tmpl){
  return _.template(Tmpl);  //and handlebar pre-compilation here
});

this way you can also replace this module with a pre-compiled template in your build process.

like image 31
Mohit Avatar answered Oct 17 '22 18:10

Mohit