Reputation: 31
Is it possible in Marionette to generate a flat list out of a nested list of models and collections?
For instance i would like to generate a select element with each node as an option (child options should be indented):
[
{
nodeName: "top level 1",
nodes: [
{
nodeName: "2nd level, item 1",
nodes: [
{ nodeName: "3rd level, item 1" },
{ nodeName: "3rd level, item 2" },
{ nodeName: "3rd level, item 3" }
]
},
{
nodeName: "2nd level, item 2",
nodes: [
{ nodeName: "3rd level, item 4" },
{ nodeName: "3rd level, item 5"},
{ nodeName: "3rd level, item 6" }
]
}
]
}
]
This should become something like:
<select>
<option>top level 1</option>
<option> 2nd level, item 1</option>
<option> 3nd level, item 1</option>
<option> 3nd level, item 2</option>
<option> 3nd level, item 3</option>
<option> 2nd level, item 2</option>
...
</select>
So far I have been trying with nested composite views (as in this fiddle http://jsfiddle.net/hoffmanc/NH9J6/), but I haven't been able to make it work properly.
Regards
Upvotes: 0
Views: 135
Reputation: 5880
Have you considered using underscore.js's _.flatten() method? It could be much simpler. flattening nested arrays/objects in underscore.js
Upvotes: 0
Reputation: 2921
Sure, first recursively iterate over nested and create plain JSON with node text and nest level it has for padding:
var flat = [];
function parseJson(nodes){
parseJson.level = parseJson.level || 0;
nodes.forEach(function(node, i){
flat.push({ text:node.name, level: parseJson.level});
if(node.nodes){
parseJson.level++;
parseJson(node.nodes);
}
(i == nodes.length -1) && parseJson.level--;
})
}
/* or you can get flat array with reduce */
var rawJson = [/* Your json here */],
flat = rawJson.reduce(function rec(memo, item, i, array){
rec.level = rec.level || 0;
memo.push({ text: item.n, level: rec.level });
if(item.nodes){
rec.level++;
item.nodes.reduce(rec, memo);
}
(i == array.length -1) && rec.level--;
return memo;
}, []);
nodes
is your raw json. In flat
you will have elements like this:
{
text: 'node text',
level: 2
}
Next, create Model and Collection for view rendering:
var NodeModel = Backbone.Model.extend({
defaults: {
name: 'unnamed',
level: 0
}
}),
NodeCollection = Backbone.Collection.extend({
model: NodeModel
});
Then Views:
var NodeView = Marionette.ItemView.extend({
tagName: 'option',
template: '#node-template'
}),
NodeCollectionView = Marionette.CollectionView.extend({
itemView: NodeView /* or childView if Marionette v 2.0.*/
});
Then template:
<% _.times(level, function(){ %> <% }); %> <%= text %>
And finally:
(new NodeCollectionView({
collection: new NodeCollection(flat),
el: 'body'
})).render();
thats all) Hope this help;
Upvotes: 1