所以我正在使用Backbone.js,在我的一个主要观点中,我遇到了一个非常奇怪的错误,我不能为我的生活找出如何解决。
该视图看起来像是新的Twitter布局。它接收一组对象,每个对象描述一个集合并查看作用于该集合的元素。每个集合由视图中的一个选项卡表示。我视图上的render()方法接受这个集合对象数组,如果tabContainer DOM元素尚未为空,则清除它们,渲染选项卡然后将事件绑定到每个选项卡。
现在在我的代码中,我有了渲染选项卡的方法,以及按顺序将点击处理程序绑定到这些选项卡的方法。这在我第一次执行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;
即使这与执行顺序的工作基本相同。
有没有人知道我做错了什么以及我应该做些什么才能使其正确(在行为和编码风格方面)?
答案 0 :(得分:1)
在视图的渲染功能中,return this.delegateEvents();
我认为您在渲染中丢失了事件绑定,您需要重新建立它们。
请参阅此链接以获取该功能的backbone.js文档: backbone.js - delegateEvents
答案 1 :(得分:0)
当您切换标签时,您不仅仅是显示/隐藏正在销毁的内容并重建dom元素,因此您还会销毁附加到它们的事件依赖者。这就是为什么事件只工作一次以及为什么将_bindTabEvents添加到渲染中的原因,因为每次都要重新附加事件。
此行执行时:this.tabContainer.html("");
poof ...不再有标签,也没有标签事件。