tildeio / htmlbars

A variant of Handlebars that emits DOM and allows you to write helpers that manipulate live DOM nodes
MIT License
1.6k stars 193 forks source link

Render recursive structures - unclosed tag error #422

Closed TheRedDevil closed 9 years ago

TheRedDevil commented 9 years ago

I have some problems rendering dropdown menu with Ember.js and HtmlBars. Data I have for menu is in array, something like this:

[{name: 'A', lvl: 0}, {name: 'B', lvl: 1}, {name: 'C', lvl: 0}, {name: 'D', lvl: 1}, , {name: 'E', lvl: 1}]

which should result in following html

<ul>
  <li> <a> A </a> 
    <ul>
      <li> <a> B </a> </li>
    </ul>
  </li>
  <li> <a> C </a> 
    <ul>
      <li> <a> D </a> </li>
      <li> <a> E </a> </li>
    </ul>
  </li>
</ul>

and it would have following structure(with css it will become nice dropdown)

The problem is that HTMLbars does not allow following structures

{{#if (condition1)}}
  <ul class="d-menu" data-role="dropdown">
{{/if}}

{{#if (condition2)}}
  </ul>
{{/if}}

Now, I am aware that this is not a bug, but project decision, but I am unsure how to overcome that limitation and to do what I need? The only option I currently see is to generate html string in js code, as I am not allowed to change data model format. If it is possible to replace HTMLbars with something different, it would be acceptable solution for me.

rlivsey commented 9 years ago

StackOverflow is a better venue for questions like this, but while we're here…

The trick is to re-structure your data so you can simply recurse through the levels, eliminating the logic from the template so you don't need to open and close tags.

Here's an example gist and it running on Ember Twiddle.

The main thing is to convert your data from a flat list into a tree, going from:

[
  {name: 'A', lvl: 0}, 
  {name: 'B', lvl: 1},
  {name: 'C', lvl: 0}, 
]

to:

[
  {
    name: 'a', 
    children: [
      {
        name: 'B'
      }
    ]
  },
  {
    name: 'C'
  }
]

Once your data is like that, then building a component which loops through and calls itself with the children is a pretty simple task.

Hope that helps.

rwjblue commented 9 years ago

Thanks @rlivsey!

TheRedDevil commented 9 years ago

Thanks, I was looking for iterative solution, but I know see that recursive approach proposed is proper way to do it.