If you have already the models you want on your collection, just use the inherited Underscore method filter() on the collection itself. It will return an Array of models, not a Backbone Collection, though.
http://underscorejs.org/#filter
Supposing filtering by attribute name:
var nameToSearch = "whatever";
var itemsByName = this.model.children.filter(function(item){
return item.get("name").indexOf(nameToSearch) >=0;
}
What I would do is isolate your getData method to cover both cases: filtering on/off.
You didn't specify how do you search, but I'll suppose you have a text input around and you want to use that value. Will that search in the top items only? A search-in-depth would be a little more complicated, involving each parent item to look for the name on its children. For the simple case that you'll be searching for files in every folder, keep the search filter in you parent View state. For that, I normally use a plain vanilla Backbone Model, just to leverage events.
var MySearchView = Backbone.View.extend({
initialize: function(options){
//I like the idea of having a ViewModel to keep state
this.viewState = new Backbone.Model({
searchQuery: ""
});
//whenever the search query is changed, re-render
this.listenTo(this.viewState, "change:searchQuery", this.render);
},
events: {
"click .js-search-button": "doSearch"
},
doSearch: function(e){
e.preventDefault();
var query = this.$(".js-search-input").val();
this.viewState.set("seachQuery", query);
},
render: function(){
var data = this.model.toJSON();
data.hasChildren = !!this.model.get('isFolder');
this.$el.html(this.template(data));
if( this.model.get('isFolder') ) {
//be careful with this, you're not removing your child views ever
if(this._listView) {
this._listView.remove();
}
this._listView = new UserListTreeView({
collection: this.model.children,
**searchQuery: this.viewState.get("searchQuery")**
});
this.$el.append(this._listView.render().el);
}
return this;
}
});
Now in your UserListTreeView, abstract the data-feeding for the template into a method that takes into account the search query:
var UserListTreeView = Backbone.View.extend({
initialize: function(options){
this.searchQuery = options.searchQuery || "";
},
...
getData: function(){
//filter your collection if needed
var query = this.searchQuery;
if(query !== ""){
return this.collection.filter(function(file){
return file.get("name").indexOf(query) >= 0;
}
else {
return this.collection.toJSON();
}
},
render: function() {
var items = this.getData(),
template = this.template(items);
this.$el.empty().append(template);
return this;
}
});
Voilá, the same view will render either the full collection or a filtered version whose items contain the searchQuery in their name. You can adjust the search method just by changing the comparison inside the filter call: you could do RegExp, search only for files starting with (indexOf(searchQuery) == 0), and so on.
Took it longer than expected, hope it helps. Another option would be to implement this in the collection itself, you can override its toJSON() method to return either all, or some items on it. If you find yourself writing another view that needs filterint, then probably it's a better idea to create a SearchableCollection and inherit both from there. Keep it DRY. :)
As a side note: you should have a look at MarionetteJS or build your own specialized views (Collection, and so on) just to save from typing the same over and over again.
renderpart of theViewfor this tree? - Mostafa