I am rendering a Backbone collection as a select
element, and I want it to have a blank option
at the top.
I am a bit unsure how to best do this using a Marionette CollectionView
. (I am aware of the CompositeView
but I don't really feel it fits this usecase).
So far I've tried using the onBeforeRender
hook to add the option
onBeforeRender: function() {
var option = $('<option>').html('')
this.$el.append(option);
}
The problem I'm facing is that render is actually being called twice, since my CollectionView
is inside a LayoutView
which calls the render function when it shows the view
this.getRegion('body').show(new SelectView({
collection: collection
}));
And then render is triggered again when its collection is reset
collection.reset([{ name: 'a' },{ name: 'b' }]);
I've verified that this is what's happening by using the debugger and looking at the callstack.
Complete snippet:
$(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>
Now in this snippet I could fix the issue by simply resetting the collection at the start of the script instead of at the end, but in my application that is not a possible solution.
I can think of a few solutions to this, but they all feel a bit wrong, I most certainly don't want to maintain any state for this in my view. The least worst thing I could think of was to change to
onBeforeRender: function() {
this.$el.html('')
var option = $('<option>').html('')
this.$el.append(option);
}
But since the framework doesn't seem to empty the $el
itself I am guessing there's a reason for that.
Is this a legit usecase for CompositeView
or is there some other smart way to do this that I have missed?
In the case that CompositeView
is OK for this usecase, the solution would be to change the CollectionView
into
var SelectView = Backbone.Marionette.CompositeView.extend({
template: _.template('<select><option></option></select>'),
childView: OptionView,
childViewContainer: 'select'
});
As demonstrated in this snippet:
$(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>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.