Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handlebars doesn't work in tables #604

Closed
matrym opened this issue Aug 15, 2013 · 38 comments
Closed

Handlebars doesn't work in tables #604

matrym opened this issue Aug 15, 2013 · 38 comments

Comments

@matrym
Copy link

matrym commented Aug 15, 2013

http://jsfiddle.net/cwYhN/3/

Notice that the same handlebars #each succeeds when outside tables. My jsfiddle includes two bordered outputs demonstrating this.

@matrym
Copy link
Author

matrym commented Aug 15, 2013

For what it's worth, if the {{each}} tags are placed outside of the table, the other tags work. This of course has the undesirable effect if duplicating the table repeatedly, but perhaps it helps narrow down the problem:

http://jsfiddle.net/cwYhN/6/

@doowb
Copy link

doowb commented Aug 15, 2013

This looks like it might be an issue with jQuery or something else... when you do console.log(frag); you see that the {{#each}} helper is pushed outside of the table even before attempting to compile it with handlebars...

I updated the jsfiddle to put the template inside a <script type="text/x-handlebars-template"></script> tag and it seems to be working fine...

http://jsfiddle.net/doowb/6GdPy/1/

@matrym
Copy link
Author

matrym commented Aug 15, 2013

Yeah, I just discovered that myself:
http://jsfiddle.net/cwYhN/10/

@matrym
Copy link
Author

matrym commented Aug 15, 2013

For what it's worth, I think it's the browser throwing out what it considers to be invalid html, rather than a jquery flaw.

@doowb
Copy link

doowb commented Aug 15, 2013

Yeah, I wasn't sure if it would be a jQuery things or the native browser function that jQuery calls. Either way, it doesn't like the {{ }} between tbody and tr.

@matrym
Copy link
Author

matrym commented Aug 15, 2013

It would be nice if handlebars built in some sort of way to operate within this constraint. For example, being able to place the "each" within a tag while having the implicit understanding that it iterates over the contents of the tag. I might imagine it looks like:

<table>
  <tbody handlebars="{{each}}">
    ... these get iterated ...
  </tbody>
</table>

Allowing for something like this would prevent the browser from stripping out the important each statement, and it could piggyback on the closing tag that it belongs to (/tbody)

@kpdecker
Copy link
Collaborator

As you guys have already alluded to, the HTML parser is confused by the handlebars markup and comes up with this as the DOM structure that you try to convert to a handlebars template:

            {{#each activity}}

            {{/each}}
        <table style="border:1px solid #ccc;">
        <thead>
            <tr><th>Month</th>
            <th>Imps</th>
            <th>Clicks</th>
            <th>Spend</th>
        </tr></thead>
        <tbody><tr>
                <th>{{month}}</th>
                <td>{{impressions}}</td><td>{{clicks}}</td><td>{{spend}}</td>
            </tr></tbody>
    </table>

Should use something like the script behavior above, javascript strings, or precompilation to work around this.

Closing this out as this issue is more of a browser implementation detail than a bug in handlebars itself.

@preichelt
Copy link

Just wasted half a day before finding out that {{#each}} doesn't work in tables. It would be really helpful if this had simply been stated in the documentation.

@kpdecker
Copy link
Collaborator

kpdecker commented Nov 5, 2013

Each works perfectly fine in tables if loaded properly. How are you loading your templates?

@matrym
Copy link
Author

matrym commented Nov 5, 2013

@preichelt You should be loading handlebars from within script tags to prevent the browser from messing up {{#each}} in tables. You can read more on stack overflow:

http://stackoverflow.com/questions/15386276/why-should-we-wrap-our-templates-inside-script-blocks

@christianxiao
Copy link

This bug wasted two hours of my life and now it is 10:30pm at Saturday, I am working in my office just to solve this bug until I found this issue and [Official Block Doc]
(http://handlebarsjs.com/builtin_helpers.html) says nothing about that.

@stevenvachon
Copy link

stevenvachon commented Jul 15, 2017

In case it wasn't clear, this is not the fault of handlebars, but yours for not knowing how HTML works: https://html.spec.whatwg.org/multipage/syntax.html#an-introduction-to-error-handling-and-strange-cases-in-the-parser

@christianxiao
Copy link

So, you assume everyone who tries to use Handlebarsjs should have a good understanding of how HTML parser works? And if not, then it is the user's fault, not the fault of the clearness of documentation? This assumption does not make sense. Another heavy and complex template engine, Angularjs, use Html compatible syntax as their template syntax, for example, if I need to loop table, I can use

<tr ng-repeat="dto in tableData">

And Angularjs keeps the internal status of loop by using Html comments:

<!-- ngRepeat: column in row -->
<tr ng-repeat="dto in tableData">

Angualrjs tries to respect and follow the syntax of Html as much as possible and avoid the assumption successfully. But Handlebarsjs's template syntax does not so it exposes the Html parser problem to users.
If you developer can't solve the problem at your side and then you have the duty to tell your users to be aware of the problem if you really care about your project and your users from all over the world.
Anyway, I will open a document issue because I believe I am not the first one who came across this headache issue and I will not be the last one.

@stevenvachon
Copy link

Angular runs in the DOM, so it is DOM aware. Handlebars works with strings only and has no concept of the DOM.

@nknapp
Copy link
Collaborator

nknapp commented Jul 17, 2017

@profullstack: As @stevenvachon said, Handlebars does not work with HTML, but only with text. But this may not be obvious from the docs, since all examples are HTML. It personally use it also to generate markdown...

If you could tell me, what kind of text you would have needed in the docs, I would be happy to add it. However, the general usage instructions (including the part with the <script>-tag are already there on the main page of http://handlebarsjs.com/. That's why I'm not sure what your need is here.

@ErisDS
Copy link
Collaborator

ErisDS commented Jul 17, 2017

@profullstack I get that you're frustrated about having "wasted time", but in my opinion, you've got this entirely backwards.

No open source project nor contributor has any duty to you. Tools like handlebars are created and maintained for free and with almost no thanks, to try to making other developers lives easier. If you were not able to use any open source projects what-so-ever, how long would your work have taken?

I can tell you, first hand, that coming along and writing comments with a tone such as this will have the opposite to the desired effect. Simple carrot vs stick, if you want someone to do something for you, ask nicely or even better raise a PR and do it yourself.

@christianxiao
Copy link

christianxiao commented Jul 17, 2017

@ErisDS Thank you for your advice. Maybe my first comment was too strong, but I totally agree with your say about:

to try to making other developers lives easier

And why I drop Angularjs to use Handlebarsjs? Because I believe Handlebarsjs will make my life easier than Angularjs.
So, just kindly add some notice words in the documentation to help your thousands of open source users to avoid the potential problem is against your will of "making other developers lives easier"? I guess it's no.
For:

No open source project nor contributor has any duty to you.

Yes, it's true. You can start an open source project and then drop it, it's totally OK. But when your project grows, you spend more time and heart on your project, you are loving more about your project and you also want to share it with the world. At this time you will start to care about your users and care about your documentation. Handlebarsjs is at this stage, this is not a small project, it's a popular project and used by programmers all over the world, even in South Korea, in China and so on.
As far as I know, most open source programmers on Github are proud of their project and their README.md file, and wish more and more people to use their tools and codes.
So if:

  • You don't want to care about your open source users
  • I am the last one who will come across this issue
    You can totally ignore my technical analysis in the first comment and user-kindly discussion in this comment.
    Besides, open source project is not something like charity you give the world, it's the communication bridge between you and other programmers.

@christianxiao
Copy link

@nknapp Thanks for your reply.
Maybe my first comment is too strong and I apologize for that and to people who read my comment.
From the first sight, Handlebars works simply like a text replacement tool and it's really easy to understand. So when I write something like this:

{{#each myListData}}
                        <tr>{{name}}</tr>
{{/each}}

If it is just simply replacement, I would expect something like this:

<tr>nameA</tr>
<tr>nameB</tr>

But when came across HTML, it behaves something different, and it is because of the natual of HTML parser just as @stevenvachon kindly noted.
So I guess just one sentence will be enough below the "The each block helper" section:

WARNING: please use script block when you try to use #each block inside table, due to the know issue of [How HTML parse works(thanks for @stevenvachon's reference](https://html.spec.whatwg.org/multipage/syntax.html#an-introduction-to-error-handling-and-strange-cases-in-the-parser)

I really appreciate it if you will share your opinion. Thanks.

@nknapp
Copy link
Collaborator

nknapp commented Jul 19, 2017

The thing is, that this has nothing to do with the #each-helper. You should just never write a template directly into HTML. An statement like this should be on the main page, but the question is: Would you have read it there?

Maybe we could a a warning like yours below the example on the landing-page...

Just another note:

I would not use the script-blocks method in production. Instead I would use Webpack or another tool to precompile the templates.

@christianxiao
Copy link

christianxiao commented Jul 19, 2017

@nknapp Yes, I guess your opinion is right: we should never write a template directly into HTML. So I guess on the landing-page, the first "Getting Started" section should use script as example and in the below gray block, we can change

You **can** deliver a template to the browser by including it in a <script> tag.

to

You **should always** deliver a template to the browser by including it in a <script> tag.

I guess your suggestion:

**never** write a template directly into HTML

is totally right, and should be put at the landing-page to let everyone follow this and it can avoid many problems. I guess it's a good idea, do you think so?

@stevenvachon
Copy link

stevenvachon commented Jul 19, 2017

That explanation doesn't work, though, because they might comprehend it as:

<script type="text/x-handlebars">
  {{#each list as |item|}}
    <td>{{item}}</td>
  {{/each}}
</script>

@christianxiao
Copy link

@stevenvachon Yes, just putting templates inside the script is not enough in your example, maybe we need to add more notes about the right way to use a template, we would like to hear your suggestions about this.

@stevenvachon
Copy link

Perhaps:

For most production applications, you will not want to use the parser and string templates. Look into precompilation.

@christianxiao
Copy link

christianxiao commented Jul 19, 2017

@stevenvachon Thanks for your comments and I guess it will be better if we put your HTML parser link together with your comments:

For most production applications, you will not want to use the parser and string templates. Look into precompilation. There are some known issues if you use a template directly into HTML, for example, the #each block helper won't work inside tables due to how HTML parser works.

@nknapp
Copy link
Collaborator

nknapp commented Jul 19, 2017

It should probably be split into

  1. Directly below the <script>-tag example

It is important that you put the template inside a <script>-tag. Do not put it into the HTML directly or the HTML-parser might modify it (for example, if it contains a table)

  1. Change the bullet point about precompilation to.

Please note that this approach is not recommended for production applications. It is also possible to precompile your templates. [...]

@nknapp
Copy link
Collaborator

nknapp commented Jul 19, 2017

Actually, I personally would not use Handlebars in the browser anymore: There are many other frameworks out there (not only Angular and React, but also Vue and Ractive). This video is pretty cool and shows the problem...

I use Handlebars on the server and as static-page generator, but for browser-rendering, I think there are better ways.

@stevenvachon
Copy link

@nknapp I started writing handlebars-html-parser for use in bringing it to the browser through VDOMs, but it met resistance.

@christianxiao
Copy link

@nknapp Thanks for your reply. Your comments include:

  1. The importance of putting template inside a <script>-tag.
  2. The table issue and HTML parser reference from @stevenvachon .
  3. The importance of precompilation.

It contains all the important details and it will be fine if we put your comments on the site.
@stevenvachon, do you think it is ok if we make it final changes and do we miss something important? Thanks.

@stevenvachon
Copy link

@profullstack I'm not a maintainer this project.

nknapp added a commit to wycats/handlebars-site that referenced this issue Jul 21, 2017
- Mention the importance of using script-tags and no
  other DOM elements as container for templates
- Recommendation to precompile template (rather than
  just saying "you could")

see handlebars-lang/handlebars.js#604
@nknapp
Copy link
Collaborator

nknapp commented Jul 21, 2017

I have made the changes.

@christianxiao
Copy link

@nknapp Thank you, I believe a lot of people will benefit from your changes.

@gurpreetatwal
Copy link

gurpreetatwal commented Oct 15, 2018

I know this issue is quite old, but I ran into this recently due to Sendgrid's email template editor not following these best practices. If anyone else ends up on this issue from Google for that reason, a workaround is to place the handlebars code in a HTML comment.

<table >
  <tbody>
    <!-- {{#each items}} -->
    <tr>
      <td>{{this.key}}</td>
      <td>{{this.value}}</td>
    </tr>
    <!-- {{/each}} -->
  </tbody>
</table>

that produces the following output

<table>
  <tbody>
    <!--  -->
    <tr>
      <td>my key</td>
      <td>my value</td>
    </tr>
    <!--  -->
    <!--  -->
    <tr>
      <td>my key 2</td>
      <td>my value 2</td>
    </tr>
    <!--  -->
  </tbody>
<table>

(NOTE: I have not verified this with Handlebars directly, just on Sendgrid's template editor)

@DanaEpp
Copy link

DanaEpp commented Feb 16, 2019

Thanks for that tip @gurpreetatwal . You saved me hours of debugging this evening with that tidbit for SendGrid!

@Lazarencjusz
Copy link

@gurpreetatwal but why?

@gurpreetatwal
Copy link

@Lazarencjusz why in regards to why the fix works or why is SendGrid email template editor doesn't work correctly?

@leventefarkas
Copy link

Hi! When you use div elements instead of a table, will be work.
Also rewrite all table elements to div.

@mattdrewitt
Copy link

@gurpreetatwal 's Solution works for me on Mailchimp/Mandrills templating for handlebars. Thanks for saving me a ton of trouble!

@zezix
Copy link

zezix commented Dec 22, 2021

I know this issue is quite old, but I ran into this recently due to Sendgrid's email template editor not following these best practices. If anyone else ends up on this issue from Google for that reason, a workaround is to place the handlebars code in a HTML comment.

<table >
  <tbody>
    <!-- {{#each items}} -->
    <tr>
      <td>{{this.key}}</td>
      <td>{{this.value}}</td>
    </tr>
    <!-- {{/each}} -->
  </tbody>
</table>

that produces the following output

<table>
  <tbody>
    <!--  -->
    <tr>
      <td>my key</td>
      <td>my value</td>
    </tr>
    <!--  -->
    <!--  -->
    <tr>
      <td>my key 2</td>
      <td>my value 2</td>
    </tr>
    <!--  -->
  </tbody>
<table>

(NOTE: I have not verified this with Handlebars directly, just on Sendgrid's template editor)

Thanks man!! This really helps a lot! Kudos!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests