问题
I am refactoring my Backbone.js application to use Marionette.js, and I am trying to wrap my head around a CollectionView
.
Suppose I have several ItemView
s with the model Cow
:
// Declare my models.
var Cow = Backbone.Model.extend({});
var Cows = Backbone.Collection.extend({
model: Cow
});
// Make my views
var GrassPatch = Marionette.ItemView.extend({
tagName: 'div',
template: "<section class='grass'>{{name}}</section>",
})
var Pasture = Marionette.CollectionView.extend({});
// Instantiate the CollectionView,
var blissLand = new Pasture({
itemView: GrassPatch;
});
// Now, add models to the collection.
Cows.add({
name: 'Bessie',
hasSpots: true
});
Cows.add({
name: 'Frank',
hasSpots: false
});
Now here's the trick. I only want cows with spots in my pasture. How, in defining my CollectionView (Pasture), do I tell it to only pay attention to those models whose hasSpots
=== true
?
Ideally I would like to have the CollectionView filter that in all events, but minimally, how do I only render some ItemViews based on their model properties?
UPDATE
I used David Sulc's examples and this worked out to an easy solution. Here's an example implementation:
this.collection = Backbone.filterCollection(this.collection, function(criterion){
var len = String(criterion).length;
var a = criterion.toLowerCase();
return function(model){
var b = String(model.get('name')).substr(0, len).toLowerCase();
if (a === b) {
return model;
}
};
});
this.collection.add({ name: 'foo' });
this.collection.add({ name: 'foosball' });
this.collection.add({ name: 'foo bar' });
this.collection.add({ name: 'goats' });
this.collection.add({ name: 'cows' });
this.collection.filter('foo');
// -> returns the first three models
回答1:
As suggested by others, the best way to achieve this is to filter the collection to contain only the models you want to display, and pass that fitlered collection to a CollectionView for rendering.
You can see a working example here: http://davidsulc.github.io/marionette-gentle-introduction/#contacts Filter contacts with the input field on the top right to display only models containing that text (e.g. "li").
This is achieved by using a special collection type that handles filtering: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/entities/common.js
And it gets instantiated here: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L13
This code is from my book on Marionette.
回答2:
Marionette handles this for you in v2.4.1.
See the CollectionView.filter method.
Following is from the docs:
var cv = new Marionette.CollectionView({
childView: SomeChildView,
emptyView: SomeEmptyView,
collection: new Backbone.Collection([
{ value: 1 },
{ value: 2 },
{ value: 3 },
{ value: 4 }
]),
// Only show views with even values
filter: function (child, index, collection) {
return child.get('value') % 2 === 0;
}
});
// renders the views with values '2' and '4'
cv.render();
// change the filter
cv.filter = function (child, index, collection) {
return child.get('value') % 2 !== 0;
};
// renders the views with values '1' and '3'
cv.render();
// remove the filter
// note that using `delete cv.filter` will cause the prototype's filter to be used
// which may be undesirable
cv.filter = null;
// renders all views
cv.render();
回答3:
@Will M's suggestion to filter the collection is the appropriate way to do this.
回答4:
Sometimes you can not filter your collection due to some custom logic and you want those models to be in a collection, but you don't want them to be rendered. To achieve it, you can:
var Pasture = Marionette.CollectionView.extend({
addChild: function(child, ChildView, index) {
if(child.get('hasSpots')) {
return Marionette.CollectionView.prototype.addChild.call(this, child, ChildView, index);
}
}});
Although I agree that filtering a collection is a much better way to do this.
来源:https://stackoverflow.com/questions/18604233/marionette-js-collectionview-only-render-specific-models