0

我正在使用 Backbone.js,在我的一个主要观点中,我遇到了一个非常奇怪的错误,我一生都无法弄清楚如何解决。

该视图看起来像新的 Twitter 布局。它接收一个对象数组,每个对象描述一个集合并查看作用于该集合的元素。每个集合由视图中的一个选项卡表示。我的视图中的方法采用这个集合对象数组,如果 DOM 元素不是空的,则render()清除它,呈现选项卡,然后将事件绑定到每个选项卡。tabContainer

现在,在我的代码中,我拥有了呈现选项卡的方法以及将点击处理程序按顺序绑定到这些选项卡的方法。这在我第一次执行时工作正常render(),但在随后的调用中render(),点击处理程序没有绑定。这是相关的代码片段:

initialize: function() {
  // Context on render(), _addAllTabs and _bindTabEvents is set correctly to 'this'
  _.bindAll(this, 'render', 'openModel', 'closeModel', 'isOpen', 'addAllModels', '_switchTab',
                  'addOneModel', '_addTab', '_removeTab', '_addAllTabs', '_loadCollection', 
                  '_renderControls', '_setCurrentCollection', '_loadModels', '_bindTabEvents');
                
  this.template = JST['ui/viewer'];
  $(this.el).html(this.template({}));
  
  // The tabContainer is cached and always available
  this.tabContainer           = this.$("ul.tabs");
  this.collectionContainer    = this.$("#collection_container");
  this.controlsContainer      = this.$("#controls");
  this.showMoreButton         = this.$("#show_more_button");
},

render: function(collections, dashboard) {
  // If _bindTabEvents has been called before, then this.tab exists. I 
  // intentionally destroy this.tabs and all previously bound click handlers.
  if (this.tabs) this.tabContainer.html("");
  if (collections) this.collections = collections;
  if (dashboard) this.$("#dashboard").html(dashboard.render().el);
  // _addAllTabs redraws each of the tabs in my view from scratch using _addTab
  this._addAllTabs();
  // All tabs *are* present in the DOM before my _bindTabEvents function is called
  // However these events are only bound on the first render and not subsequent renders
  this._bindTabEvents();
  var first_tab = this.collections[0].id;
  this.openTab(first_tab);
  return this;
},

openTab: function (collectionId, e) {
  // If I move _bindTabEvents to here, (per my more thorough explanation below)
  // my bug is somehow magically fixed. This makes no friggin sense.
  if (this.isTabOpen(collectionId)) return false;
  this._switchTab(collectionId, e);
},

_addAllTabs: function() {
  _.each(this.collections, this._addTab );
},

_bindTabEvents: function() {
  this.tabs = _.reduce(_.pluck(this.collections, "id"), _.bind(function (tabEvents, collectionId) {
    var tabId = this.$("#" + collectionId + "_tab");
    tabEvents[collectionId] = tabId.click(_.bind(this._switchTab, this, collectionId));
    return tabEvents
  }, this), {});
},

_addTab: function(content) {
  this.tabContainer.append(
    $('<li/>')
      .attr("id", content.id + "_tab")
      .addClass("tab")
      .append($('<span/>')
        .addClass('label')
        .text(content.name)));
  //this._loadCollection(content.id);
  this.bind("tab:" + content.id, this._loadCollection);
  pg.account.bind("change:location", this._loadCollection); // TODO: Should this be here?
},

etc..

正如我所说,render()这里的方法确实有效,但只是第一次。奇怪的是,如果我移动该行this._bindTabEvents();并将其设置为openTab()方法的第一行,就像下面的代码片段一样,那么整个事情就可以完美地工作:

openTab: function (collectionId, e) {
  this._bindTabEvents();
  if (this.isTabOpen(collectionId)) return false;
  this._switchTab(collectionId, e);
},

当然,那行代码与该方法无关,但它确实使整个事情工作正常,这让我问为什么它在那里工作,但不像这样按顺序工作:

this._addAllTabs();
this._bindTabEvents();

这对我来说毫无意义,因为如果我把它放在这一行之后,它也不起作用:

var first_tab = this.collections[0].id;

即使就执行顺序而言,这基本上与起作用的方法相同。

有谁知道我做错了什么以及我应该做些什么来使它正确(在行为和编码风格方面)?

4

2 回答 2

1

在您视图的渲染功能中,return this.delegateEvents(); 我认为您在渲染中丢失了事件绑定,您需要重新建立它们。

有关该功能的主干.js 文档,请参阅此链接: 主干.js - delegateEvents

于 2011-04-30T04:21:39.720 回答
0

当您切换选项卡时,您不仅仅是显示/隐藏您正在破坏和重建 dom 元素的内容,因此您也在破坏附加到它们的事件监听器。这就是为什么事件只工作一次以及为什么将 _bindTabEvents 添加到渲染中的原因,因为您每次都重新附加事件。

当这一行执行时:this.tabContainer.html("");噗……没有更多的选项卡,也没有更多的选项卡事件。

于 2011-04-24T21:19:26.897 回答