我是骨干的新手,我被要求在我的应用程序中构建一个树结构,树实际上是递归的,即点击一个节点会渲染一些更多的子节点来调用rest api。我该怎么做?
模板就像这样
<script id="listtemplate" type="text/template">
<span></span>
</script>
模型定义如下
Model = function(){
var sportModel;
sportModel = Backbone.Model.extend({});
return{
newInstance : function(){return sportModel;}}})();
集合定义如下
Collection = (function(){
var Model = Model.newInstance();
var Hierarchy = Backbone.Collection.extend({
model: sportModel,
url: function () {
return this.urlParam;
},
initialize: function (models, options) {
this.urlParam = options.urlParam || "";
},
sync: mySync,
parse: function (response) {
return $.map(response.getElementsByTagName('Child'), function (Xml) {
return $.xml2json(Xml);
});
}
});
return{
newInstance : function(models,options) { return new Hierarchy(models,options); }
};
})();
我有两个观点主视图和子视图
MasterView = (function() {
'use strict';
var masterView;
masterView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, "render");
},
render: function(){
this.collection.each(this.addOne, this);
return this;
},
addOne: function(Model){
//creating a new child view
// console.log(Model.toJSON().title);
var taskView = new ChildView.newInstance({model: Model});
//appending to the root element
this.$el.append(taskView.render().el);
}
});
return {
newInstance : function(options) { return new masterView(options); }};})();
ChildView= (function() {
'use strict';
var
htmlTemplate = _.template( $('#eventAccordionTemplate').html() ), // See templatesSearch.jsp
expanded = true, // By default the Events Accordion extends to the bottom of the browser window.
BackboneView, applyStyles;
/**
* Apply CSS specific to this view
* Unfortunately, this View needs to modify its parent wrapper element.
* Otherwise the layout will break when it's resized. See templatesSearch.jsp.
* @param {Object} $elmt
* @param {Boolean} expand
*/
applyStyles = function( $elmt, expand ) {
var
top = '2px',
left = '2px',
pos = 'absolute',
right = '2px';
if ( expand ) {
$elmt.css({
"position" : pos,
"top" : top,
"left" : left,
"right" : right,
"bottom" : "2px"
});
$elmt.parent().css( 'bottom', '2px' );
} else {
$elmt.css({
"position" : pos,
"top" : top,
"left" : left,
"right" : right,
"bottom" : "50%"
});
$elmt.parent().css( 'bottom', '50%' );
}
};
答案 0 :(得分:1)
如果经常需要在Backbones中实现树(任何层次结构),我通常最终只使用一个Backbone.Collection(树)和一个Backbone.Model(Node)。
除了内容之外,节点还具有parent_id属性和children_ids数组。它们还有许多方法,例如isRoot()(parent_id为null),isLeaf(children_ids为空),children()方法(collection.filter - &gt; children.id中的node.id),parent()方法(collection.get parent_id) )等等。
树有很少的方法,主要是帮助程序,例如rootNodes()(在集合上过滤node.isRoot())。
您实际上并不需要children_ids数组,但它确实使事情变得更容易。例如,如果你的后端是一个类似act_as_tree gem的Rails后端,那么服务这种Node模型将非常容易。
根据您的树大小,您甚至可以考虑对children()使用promises,以便在尚未以对视图不那么烦恼的方式加载时捕获缺失的id。但在我看来,这有点过分。单击“展开节点”(或任何您称之为“展开节点”)时,您始终可以只执行currentModel.fetch()。然后,在服务器端的/ node /:id路由中,您可以为所有直接子节点和Node模型(客户端)提供模型,您可以使用解析函数在服务器发送时对子节点进行侧载它的回复。
有数百万种方法可以解决这个问题,它主要取决于您的树大小/单个节点的复杂性/大小。如果您只有几十个节点只有少量数据,那么最好立即加载整个层次结构。还要记住,你可以有一个Tree / Node结构来处理节点之间的层次结构......然后是另一个集合,它只是NodeContent节点的一个普通集合(一个'数组'),它可能更重,并且有更多的逻辑居中在显示/编辑单个节点上。如果需要知道它在树中的位置,则对节点进行反向引用。
这是一个非常广泛的问题,所以我希望这会有所帮助,如果你有一个代码的工作基础,也许有人可以帮助改进它。
答案 1 :(得分:1)
希望这会给你一些想法/见解,因为它不是一个完整的解决方案。
当我们谈论树层次结构时,我们谈论的是复合模式。引用维基百科:
“在处理Tree结构化数据时,程序员经常需要区分Item-node和branch.This使代码更复杂,因此容易出错。解决方案是一个允许处理复杂和原始对象的接口均匀“。
考虑到这一点,这是我将如何去做的基本概述:
/**
* Item type primitive.
* A branch or Item can be an item in the tree.
*/
var Item = function (obj) {
this.children = [];
_.extend(this, obj); // Bit of a hack to do this blindly.
};
var p = Item.prototype;
/**
* Nest items within an item.
* Takes in an array of items and an iterator function for parsing them.
*/
p.add = function (items, func) {
_.each(items, function (item) {
var inner = (_.isFunction(func)) ? func(item) : item;
this.children.push(new Item(inner));
}, this);
};
/**
* Patch _.each into prototype as a useful shortcut.
*/
p.each = function () {
var args = [].slice.call(arguments);
args.unshift(this.children);
return _.each.apply(_, args);
};
/**
* Pick a single child by criteria.
*/
p.find = function () {
var args = [].slice.call(arguments);
args.unshift(this.children);
return _.where.apply(_, args).shift();
};
});
一般用法:
// Build the tree, starting from the root.
var root = new Item(),
types = _.uniq(_.pluck(collection, 'name'));
// Build a basic root from collection.
root.add(types, function (type) {
return _.find(collection, function (t) {
return t.name === type;
});
});
所以你有每个项目的包装器,它有自己的属性等,但也有一个子数组。我们可以将其扩展为获取子项,因为从视图中选择了父项:
var Item = function (obj) {
this.children = [];
this.getChildren = function () {
// Something creative here.
};
_.extend(this, obj);
};
总是让我对这样的复合模式感到困惑的一件事是,它应该在数据级别完成,即你是否应该有Composited Data和Views?或两者? Backbone Marionette的复合视图依赖于我所知道的集合:
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.compositeview.md