Backbone.Collection.reset() => 子视图与父视图不同步

Backbone.Collection.reset() => child view is out of sync with parent

我有一个物品清单。它们存储在 backbone 可分页集合中。

它们是这样显示的

|---item1----------------------------|
|---item2----------------------------|
|---item3----------------------------|
|---item4----------------------------|
|---item5----------------------------|
|---item6----------------------------|
|---item7----------------------------|
<< 1,2,3...结束 >>

用户可以单击单个项目以在单独的页面中打开详细信息视图。详细视图已初始化侦听器 创建时。这些听众绑定到项目模型。

由于详细视图 很大 ,我通过切换可见性将其缓存在 DOM 中。 随后单击该项目将切换缓存视图。

------问题来了-----

当项目列表切换到另一页时,集合被重置(通过分页器)。之前存储在集合中的所有模型都被取消引用并且 创建了一组新模型。所以页面来回切换后,之前打开的item存储了自己的不同副本 在集合中。所以当我在详细视图(在视图缓存中)中更改项目的名称时,项目列表中的名称没有更改。

视图不同步!因为它们引用的是不同的模型。

不确定以前是否有人遇到过这种情况。如果有,请与我分享你是如何解决的。

非常感谢。

在页面更改时在列表视图项和相应的详细视图之间保持最新引用的最直接的方法是重新呈现详细视图。但我假设这个选项在你的项目范围内是不可接受的。

当我的任务是在逻辑上独立的视图中形成关系时,我经常做的是使用监听器。只要视图共享一个唯一标识符(例如,它们都共享一个模型,或者至少是相同的模型 ID),我总是可以发送一条消息来到达我感兴趣的视图。

为此,您需要一个集中式事件中心,Backbone 很容易生成它。在一些适当的全局变量(例如,MyApp)中,我们只需做:

MyApp.EventBus = _.extend({}, Backbone.Events);

设置详细视图

在详细视图初始化函数中,我将删除此侦听器,

initialize: function () {
  // Listen to a toggle visibility on this view
  this.listenTo(MyApp.EventBus, 'detail-view:toggle-view', toggleView);
},

toggleView: function (id) {
  if (this.model.id == id) {
     // Show this view if I have the passed id
     this.$el.show()
     // Notify the parent list item view that its detail view exists
     MyApp.EventBus.trigger('detail:view:exists', true);
  } else {
    // Hide all other views
    this.$el.hide();
  }
},

changeName: function () {
  // logic that parses DOM user input to 
  // local variable name

  // We now trigger an event 'detail-view:change:name', and we send as 
  // parameters our model's id and the new name
  MyApp.EventBus.trigger('detail-view:change:name', this.model.id, name);
}

设置列表项视图

列表项视图将希望监听名称更改(或您希望列表项知道的详细视图中的任何其他模型 属性)。因此,我们将为 'detail-view:change:name' 事件设置一个处理程序。

我们还想连接我们的点击处理程序来切换列表项详细视图的可见性。棘手的部分是处理尚未呈现视图的事件(我假设您正在延迟加载详细视图)。因此,我们为 detail:view:exists 事件设置了第二个侦听器,详细信息视图在捕获 detail-view:toggle-view 事件时触发。如果我们没有及时从目标详细视图中听到 detail:view:exists 事件(我使用的是 100 毫秒,但您可以根据需要调整它),那么我们将渲染视图。

initialize: function () {
  // Listen to when the detail associated with this list item changes
  // the the list item name
  this.listenTo(MyApp.EventBus, 'detail-view:change:name', onNameChange);
  // Set a property in this view if its detail view exists
  this.listenTo(MyApp.EventBus, 'detail:view:exists', 
    _.bind(function () { this.detailViewExists = true; }, this));
  // Create a debounced function that tests whether this view's
  // detail view exists
  _.debounce(_.bind(this.handleViewState, this), 100);
},

events {
  click: 'toggleDetailView'
},

toggleDetailView: function (id) {
  MyApp.EventBus.trigger('detail-view:toggle-view', this.model.id);
  this.handleViewState();
},

// Debounced function that will wait 100ms asynchronously for the 
// detail view to respond. If the detailViewExists bit is not set to true 
// then we assume the view does not exist and we render it
handleViewState: function () {
  if (!this.detailViewExists) 
     // The view does not exist, render and attach the view

  // Set the bit to false to allow testing in the event that the detail view
  // is destroyed in the future
  this.detailViewExists = false;
},

changeName: function (id, newname) {
  if (this.model.id == id) {
     // Change the name of this list item view
     this.$('.item-name').text(newname);
}

外卖

现在,这两个不同视图之间的引用是共享的唯一标识符。因为,根据设计,这两个标识符在它们的范围内是唯一的,不应该改变,并且假设细节视图已经被渲染并附加到 DOM,那么无论渲染它的状态如何,列表项视图将始终能够与其详细视图进行通信。