First of all, I want to talk about the topic of this article, because I don't know what name it is. Rendering list is the most common operation in our application. In backbone applications, we usually use the list as a collcetion, and then specify a view to display this collection, which is very convenient. But what if you need to filter a set and only show the parts that meet the criteria in the collection?
Rendering a collection in the memory is undoubtedly very simple, because backbone provides some operation methods. However, after filtering a collection, you may encounter unexpected difficulties in rendering the filtered results. This article will look for simple code to solve this problem.
Basic Filter Operations
Backbone. Collection has some methods for filtering and searching. Most of them are inherited from underscore. JS, but some are not. However, no matter where they come from, most of these methods have one thing in common: returnModels Array.
For examplewhere
Method. When this method is called, a model array with the attribute value you specified is returned.
var testCollection = new Backbone.Collection([ {name: "joy", age: 21}, {name: "jack", age: 20}, {name: "pater", age: 32}, {name: "daivd", age: 22}, {name: "gina", age: 21}]);var results = testCollection.where({ age : 21});
results
It will be an array with all{age: 21}
Ofmodel
Inconsole
Let's take a look:
Filter by View Control
It is quite easy to display the Filter list. The only problem is that the filter operation does not return a collection, but a model array. This is often surprising. How to deal with this array? How to display a backbone. View? In addition, when you need to display that the view of the array after the filtering operation has pointed to an original backbone. Collection, this problem will become more difficult.
You may say that this is very easy. Let's take a look at the following operations:
var FilteredView = Backbone.View.extend({ events: { "click #run": "runFilter" }, runFilter: function(e){ e.preventDefault(); this.filter = { // ... get the filter info from the DOM }; this.render(); }, render: function(){ var html = [], template = _.template($("tmpl-demo").html()); var filteredList = this.collection.where(this.filter); _.each(filteredList, function(item){ html.push(template(item.toJSON()); }); this.$el.html(html); return this; } });
It looks good to some extent. The code is short, easy to understand, and solves the problem.
However, the above Code is only applicable to some simple scenarios. If you are dealing with simple situations, there may be no mistakes. But there are latent defects. When you continue to use the filter operation, you will find that the above Code has not been further filtered.
How can this problem be solved? Let's go back to the origin. We can perform various filtering operations on Backbone. Collection, right? Therefore, we need to make the filter operation return not an array, but a backbone. Collection object. But how?
Anti-pattern of Self-Filtering
If you try to filter the original collection and then update the collection, you will eventually get into a bad situation, or even worse than filtering in the view.
However, let's take a look at how to implement the above method:
var testCollection = Backbone.Collection.extend({ customFilter: function(filters){ var results = this.where(filters); this.reset(results); }});var testCollection = new Backbone.Collection([ {name: "joy", age: 21}, {name: "jack", age: 20}, {name: "pater", age: 32}, {name: "daivd", age: 22}, {name: "gina", age: 21}]);// filter the collectiontestCollection.customFiler({ age : 21});
Once you use a custom method to filter collections, there are only two items in the collection that meet the conditions. This seems to be an ideal result. If you want to use a new filter, it will only further filter the last result because the previous collection has been updated, this is not the result we want most of the time.
Create a filtered collection
If you want to use backbone. collection to process filtered arrays, you must convert the array to backbone. collection. There will be two collections: the original collection and the filtered collection. When you have the filtered collection, you can use backbone. View for direct rendering.
var results = testCollection.where({ age: 21});var filteredCollection = new Backbone.Collection(results);
Yes, it's that simple. Happy, but it can still be more convenient.
Returns the same collection type.
You may want to return an instance of the same type from a custom backbone. collection.
var testCollection = Backbone.Collection.extend({ customFilter: function(filters){ var results = this.where(filters); return new testCollection(results); }});var filteredCollection = testCollection.customFilter({ age: 21});
Update filter operation
When the filter condition changes, you need to update the collection and re-render it. The best operation is to instantiate only one filtered collection, and render the correspondingfilterView
.
var FilteredView = Backbone.View.extend({ initialize: function(){ this.listenTo(this.collection, "reset", this.render, this); } });var filteredCollection = new Backbone.Collection();var filteredView = new FilteredView({ collection: filteredCollection});$("#run").click(function(e){ var filter = { // ... new filters }; var results = myCollection.where(filter); filteredCollection.reset(results);});
Addition
Add an underscore.
Underscoreinvoke
The method is a very useful method. Let's look at it:
var demoCollection = Backbone.Collection.extend({ model: demoModel, selected : function() { return this.filter(function(itm) { return itm.get(‘selected‘); }); }});var selected = demoCollection.selected();
I have already explained howfilter
The returned array becomes the backbone. Collection instance, but it is obvious that the array is returned here.
The following is generally the case when batch operations are performed. For example, if batch deletion is performed, We need to delete the selected batch operations. We may need to access an API and pass the ID to it.
Ifselected
The returned backbone. Collection instance object can be directly used.pluck
Method:
VaR IDs = selected. Pluck ('id'); // returns the array of IDs.
Ifselected
What about the model array? How can I obtain the ID array?
You may passeach
Loop:
var ids = [];_.each(selected, function(itm){ ids.push(itm.get(‘id‘));});
If you want to return more than an array of IDS,each
The method is actually very suitable. If you want to return an array, there is actually a simpler method.
If you have any, go to the backbone source codepluck
Method, you will find that this method actually calls underscoreinvoke
So easy:
ids = _.invoke(selected, ‘get‘, ‘id‘);
If you want to delete them in batches from the collection, you can also directly:
_.invoke(selected, ‘destroy‘);//http://underscorejs.org/#invoke_.invoke(list, methodName, *arguments)
Therefore, multiple parameters can be passed, but an array is returned.
In fact, let's look at the underscore source code,invoke
The_.map
, So you can also usemap
Method.
How to filter backbone. Collection