Backbone.js:如何只处理单个视图的选择

Backbone.js: How to handle selection of a single view only?

本文关键字:单个 视图 选择 处理 js Backbone      更新时间:2023-09-26

我遇到了以下问题:

我有一个模型,它有一个属性来定义它是否被明显地选中,为了这个问题的目的,我将称之为SelectModel。

SelectModel = Backbone.Model.extend({
defaults:{
 isSelected: false
}
})

现在,我真正不明白的第一部分是我应该如何处理一般的选择。如果我想使用observer模式,我的View应该监听isSelected属性的更改。但我的观点也从一开始就引发了这种情况,所以我会这样做的。

SelectView = Backbone.View.extend({
initialize: function(){
this.model.bind("change:isSelected", this.toggleSelectionVisually)
},
events: {
"click" : toggleSelection
},
toggleSelection: function(){
this.model.set({"isSelected": !this.model.get("isSelected");
},
toggleSelectionVisually:(){
//some code that shows that the view is now selected
},
})

所以这本身就已经感觉有点荒谬了,但我想我只是理解了一些错误。

但是,在不让代码变得糟糕的情况下,我真正无法实现的部分是处理多个模型的选择,即一次只选择一个模型。

SelectListView = Backbone.View.extend({
initialize: function(){
this.collection = new SelectList();
},
toggleSelection: function(){
????
}   
})

那么,谁应该通知谁选择的变更呢?哪个部分应该触发它,哪个部分应该倾听?我真的被这个卡住了。对于一个视图来说,这是可行的,对于一个集合来说,我很难过地失去了。

在看到问题的第二部分之前,我会建议对您的SelectView进行以下简化:

SelectView = Backbone.View.extend({
  events: {
    "click" : toggleSelection
  },
  toggleSelection: function(){
    this.model.set({"isSelected": !this.model.get("isSelected");
    //some code that shows whether the view is selected or not
  }
});

然而,由于isSelected属性显然是互斥的,当打开另一个属性时,可以隐式地关闭它,我认为您拥有它的方式最适合您的情况。

因此,使用现有的SelectView和,您可以得到如下的SelectListView警告:每次选择一个模型时,它都会遍历整个模型集合。如果您将有大量的模型,这将不能很好地扩展,并且您希望缓存以前选择的模型,而不是在整个集合上迭代

SelectListView = Backbone.View.extend({
  initialize: function(){
    this.collection = new SelectList();
    this.collection.bind('change:isSelected', this.toggleSelection, this);
  },
  toggleSelection: function(toggledModel){
    //A model was toggled (on or off)
    if(toggledModel.get('isSelected') {
      //A model was toggled ON, so check if a different model is already selected
      var otherSelectedModel = this.collection.find(function(model) {
        return toggledModel !== model && model.get('isSelected');
      });
      if(otherSelectedModel != null) {
        //Another model was selected, so toggle it to off
        otherSelectedModel.set({'isSelected': false});
      }
    }
  }   
});

我建议您的模型不要跟踪这一点,而是跟踪视图。

在我看来,模型与它的显示无关,而是与你正在跟踪的数据有关。该视图应该封装关于数据在哪里以及如何显示给用户的所有信息

所以我会把isSelected作为视图的一个属性。然后,编写一个切换可见性的方法是很琐碎的。如果您需要解释选择了特定视图的其他视图,您可以附加一个侦听器$(this.el).on('other_visible', toggle_show),该侦听器可以在$(this.el).trigger('other_visible')toggle_visibility方法上触发

非常接近@rrr建议的解决方案,但将逻辑从View移动到Collection,我认为它会变成:

SelectsCollection = Backbone.Collection.extend({
  initialize: function() {
    this.on( "change:selected", this.changeSelected );
  },
  changeSelected: function( model, val, opts ){
    if( val ){
      this.each( function( e ){
        if( e != model && e.get( "selected" ) ) e.set( "selected", false );
      });
    };
  },
});

有不同的方法可以做到这一点。您可以在集合本身上触发一个事件,并让所有SelectModel实例侦听它并相应地更新自己。如果集合中有很多SelectModel实例,这似乎有点浪费,因为它们中的大多数最终都不会做任何工作。我可能会做的是跟踪您视图中的最后一个SelectModel:

SelectListView = Backbone.View.extend({
  initialize: function(){
    this.collection = new SelectList();
    this.lastSelectedModel = null;
  },
  toggleSelection: function(){
    // populate newSelectedModel with the SelectedModel that you're toggling
    var newSelectedModel = getNewSelectedModel();
    if (!newSelectedModel.get('isSelected')) {
      // if the SelectModel isn't already selected, we're about to toggle it On
      // so we need to notify the previously selected SelectModel
      if (this.lastSelectedModel) {
        this.lastSelectedModel.set({isSelected: false});
      }
      this.lastSelectedModel = newSelectedModel;
    } else {
      // if the newSelectedModel we're about to toggle WAS already selected that means
      // nothing is selected now so clear out the lastSelectedModel
      this.lastSelectedModel = null;
    }
    newSelectedModel.set({isSelected: !newSelectedModel.get('isSelected')});
  }   
})