如何向 Marionette CollectionView 生成的 select 添加空白选项?

How do I add a blank option to a select generated by a Marionette CollectionView?

我正在将 Backbone 集合渲染为 select 元素,我希望它的顶部有一个空白 option

我有点不确定如何使用 Marionette CollectionView 最好地做到这一点。 (我知道 CompositeView 但我真的觉得它不适合这个用例)。

到目前为止,我已经尝试使用 onBeforeRender 挂钩来添加选项

onBeforeRender: function() {
  var option = $('<option>').html('')
  this.$el.append(option);
}

我面临的问题是 render 实际上被调用了两次,因为我的 CollectionViewLayoutView 中,它在显示视图时调用 render 函数

this.getRegion('body').show(new SelectView({
  collection: collection
}));

然后在重置其集合时再次触发渲染

collection.reset([{ name: 'a' },{ name: 'b' }]);

我已经通过使用调试器和查看调用堆栈验证了这就是正在发生的事情。

完整片段:

$(document).ready(function() {
  
var collection = new Backbone.Collection({
  model: Backbone.Model
});


var Layout = Backbone.Marionette.LayoutView.extend({
  
  regions: {
    body: '#main'
  },
  
  template: false,
  
  onRender: function() {
    this.getRegion('body').show(new SelectView({
      collection: collection
    }));
  }
});

var OptionView = Backbone.Marionette.ItemView.extend({
  tagName: 'option',
  template: _.template('<%= name %>')
});
  
var SelectView = Backbone.Marionette.CollectionView.extend({
   tagName: 'select',
   childView: OptionView,
   onBeforeRender: function() {
     var option = $('<option>').html('')
     this.$el.append(option);
   }
});

var layout = new Layout({
  el: $('#app')
});
layout.render();
var app = new Marionette.Application();

app.start();
Backbone.history.start();

collection.reset([{ name: 'a' },{ name: 'b' }]);
});
<script data-require="underscore.js@1.6.0" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>  
<script data-require="jquery@2.1.3" data-semver="2.1.3" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="backbone.js@1.1.2" data-semver="1.1.2" src="http://backbonejs.org/backbone-min.js"></script>
<script data-require="marionette.js@2.2.2" data-semver="2.2.2" src="http://cdnjs.cloudflare.com/ajax/libs/backbone.marionette/2.2.2/backbone.marionette.js"></script>

<body>
  <div id="app">
    <div id="main"></div>
  </div>
</body>

现在,在这个片段中,我可以通过在脚本开始而不是结束时简单地重置集合来解决这个问题,但在我的应用程序中这不是一个可能的解决方案。

我可以想到一些解决方案,但他们都感觉有点不对劲,在我看来,我当然不想为此保持任何状态。我能想到的最不糟糕的事情是改为

onBeforeRender: function() {
  this.$el.html('')
  var option = $('<option>').html('')
  this.$el.append(option);
}

但是由于框架似乎没有清空 $el 本身,我猜这是有原因的。

这是 CompositeView 的合法用例,还是我错过了其他一些聪明的方法?

如果 CompositeView 适用于此用例,解决方案是将 CollectionView 更改为

var SelectView = Backbone.Marionette.CompositeView.extend({
   template: _.template('<select><option></option></select>'),
   childView: OptionView,
   childViewContainer: 'select'
});

如这段代码所示:

$(document).ready(function() {
  
var collection = new Backbone.Collection({
  model: Backbone.Model
});


var Layout = Backbone.Marionette.LayoutView.extend({
  
  regions: {
    body: '#main'
  },
  
  template: false,
  
  onRender: function() {
    this.getRegion('body').show(new SelectView({
      collection: collection
    }));
  }
});

var OptionView = Backbone.Marionette.ItemView.extend({
  tagName: 'option',
  template: _.template('<%= name %>')
});
  
var SelectView = Backbone.Marionette.CompositeView.extend({
  template: _.template('<select><option></option></select>'),
  childView: OptionView,
  childViewContainer: 'select'
});

var layout = new Layout({
  el: $('#app')
});
layout.render();
var app = new Marionette.Application();

app.start();
Backbone.history.start();

collection.reset([{ name: 'a' },{ name: 'b' }]);
});
<script data-require="underscore.js@1.6.0" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>  
<script data-require="jquery@2.1.3" data-semver="2.1.3" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="backbone.js@1.1.2" data-semver="1.1.2" src="http://backbonejs.org/backbone-min.js"></script>
<script data-require="marionette.js@2.2.2" data-semver="2.2.2" src="http://cdnjs.cloudflare.com/ajax/libs/backbone.marionette/2.2.2/backbone.marionette.js"></script>

<body>
  <div id="app">
    <div id="main"></div>
  </div>
</body>