只有Backbone.js和Js.Tree的树结构

时间:2013-12-11 11:42:44

标签: backbone.js jstree

  

我是骨干的新手,我被要求在我的应用程序中构建一个树结构,树实际上是递归的,即点击一个节点会渲染一些更多的子节点来调用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%' );
    }
  };

2 个答案:

答案 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