Интересная задачка) Жаль пораньше не увидел.
Можно ее решить, используя встроенную в Meteor библиотеку Underscore и рекурсию :).
Пусть имеются такие тестовые данные:
const _id1 = Items.insert({title: 'Item 1'});
const _id2 = Items.insert({title: 'Item 2'});
const _id21 = Items.insert({title: 'Item 2.1', parent: _id2});
const _id22 = Items.insert({title: 'Item 2.2', parent: _id2});
const _id221 = Items.insert({title: 'Item 2.2.1', parent: _id22});
const _id222 = Items.insert({title: 'Item 2.2.1', parent: _id22});
const _id23 = Items.insert({title: 'Item 2.3', parent: _id2});
const _id3 = Items.insert({title: 'Item 3'});
const _id31 = Items.insert({title: 'Item 3.1', parent: _id3});
const _id4 = Items.insert({title: 'Item 4'});
const _id5 = Items.insert({title: 'Item 5'});
Получаем их
var items = Items.find().fetch();
Теперь представляем эти данные в виде дерева. Каждый лист дерева - объект со значением
title
и потомками
children
. Строим такое дерево через следующую функцию:
function getAsTree(items) {
// для корневых элементов ставим _id родителя пустой строкой ''
var extItems = _.map(items, function(item){
if ('parent' in item)
return item;
else
return _.extend(item, {parent: ''});
});
// данная функция рекурсивно возвращает потомков элемента с заданным _id,
// которые в свою очередь также содержат потомков и т.д.
// если же потомка у элемента нет, то в нем {children: []}
function recBuildTree(_id){
var result = [];
return _.map(_.filter(extItems, function(item){
return item.parent === _id;
}), function(item){
return _.extend(item, {
children: recBuildTree(item._id)
});
});
}
// определяем корень дерева, который не содержит значения,
// а его потомки - корневые элементы с {parent: ''}
var tree = {
title: '',
_id: '',
children: recBuildTree('')
};
return tree;
}
Теперь создадим темплейт, использующий вспомогательный для отрисовки, затем выведем в нем сгенерированное дерево:
Template.listView.helpers({
children: function(){
return getAsTree(Items.find().fetch()).children;
}
});
<template name="listView">
<ol>
{{#each children}}
{{> _listViewItem}}
{{/each}}
</ol>
</template>
<template name="_listViewItem">
<li>
<div>{{title}}</div>
{{#if children}}
<ol>
{{#each children}}
{{> _listViewItem}}
{{/each}}
</ol>
{{/if}}
</li>
</template>
Результат:
Стоит заметить, что некорневые элементы с несуществующим родителем не будут включены в дерево.