You have a list of items with id, parentid, and message properties. parentid references other item in the same list. There is no limit on nested levels. How would you implement this?

In other words, how to take this:

var data = [
    {
        id: 1,
        parentid: 0,
        message: "I'm 1, and I have no parent."
    }, {
        id: 2,
        parentid: 1,
        message: "I'm 2, and 1 is my parent."
    }, {
        id: 3,
        parentid: 1,
        message: "I'm 3, and 1 is my parent."
    }, {
        id: 4,
        parentid: 0,
        message: "I'm 4, and I have no parent."
    }, {
        id: 5,
        parentid: 1,
        message: "I'm 5, and 2 is my parent."
    }
];

And turn it into this:

<ul>
    <li>
        I'm 1, and I have no parent.
        <ul>
            <li>
                I'm 2, and 1 is my parent.
                <ul>
                    <li>
                        I'm 5, and 2 is my parent.
                    </li>
                </ul>
            </li>
            <li>
                I'm 3, and 1 is my parent.
            </li>
        </ul>
    </li>
    <li>
        I'm 4, and I have no parent.
    </li>
</ul>

Is there a nice and fast way how to do this in a template? Without looping through the whole array, building a new one, and rendering that?

And even than, what is the best implementation of a template that is recursively rendering itself until there is nothing left?

有帮助吗?

解决方案

With ractive.js, you could use javascript to filter the array using a partial and an expression helper function (see http://jsfiddle.net/pUf5P/1/ for full example)

Template:

{{# { index: 0 } }}
{{>lister}}
{{/ }}

<!-- {{>lister}} -->
  <ul>
  {{# filter(list, .index) }}
    <li>{{message}}
      {{# { index: id } }}
         {{>lister}}
      {{/ }}
   </li>
  {{/ }}
  </ul>
<!-- {{/lister}} -->

Setup:

new Ractive({
    el: 'body',
    template: '#template',
    data: { 
        list: data,
        filter: function(list, parent){
            return list.filter(function(item){
                return item.parentid === parent
            })
        }      
    }
})

Or for a pure template solution (see http://jsfiddle.net/pUf5P/2/), you can do:

{{# { index: 0 } }}
{{>lister}}
{{/ }}

<!-- {{>lister}} -->
  <ul>
    {{#list}}
    {{# .parentid === index}}
    <li>{{message}} 
      {{# { index: .id } }}  
        {{>lister}}
      {{/ }}   
    </li>
    {{ /}}
  {{/list }}
  </ul>
<!-- {{/lister}} -->

UPDATE: I know you wanted to do this "Without looping through the whole array, building a new one", but realize that you are looping through the whole array at each level to find the matching children. You don't need to build a hierarchical structure, just bucket the items based on parent id (see http://jsfiddle.net/pUf5P/3/):

var byParent = {}
data.forEach(function(item){
    if(!byParent[item.parentid]){ byParent[item.parentid] = [] }
    byParent[item.parentid].push(item)
})

Then use a variant of the above template strategy:

{{# { index: 0 } }}
{{>lister}}
{{/ }}

<!-- {{>lister}} -->
  <ul> 
    {{# { list: byParent[index]} }}
      {{#list}}
      <li> {{(.message)}}
        {{# { index: .id } }}
        {{>lister}}
        {{/ }}            
      </li>
      {{/list}}
    {{/ }}

  </ul>
<!-- {{/lister}} -->
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top