admin管理员组文章数量:1296901
The desired functionality
I'm using a Backbone.Collection to view data in a sortable list. I've got the part down where clicking on certain dom elements sets properties on my Collection to determine which field i'd like to sort, and which direction the sort should be done. A sort should then be triggered on the Collection after which the view will update.
Eventually I want to be able to sort numerical fields as well strings. Some strings should sort alphabetically, others by a custom predefined order.
My current approach
I've found the following post on reverse sorting strings: Sorting strings in reverse order with backbone.js
I tried to bine this with a parator function and the following custom properties:
- a custom sort order for the possible values in the 'status' field
- which field should be used for sorting
- which direction to sort
The Collection and Model classes I've got so far:
The Collection
var ApplicationCollection = Backbone.Collection.extend({
model: ApplicationModel,
url: 'rest/applications',
// sorting
statusOrder: ['new', 'to_pay', 'payed', 'ready'],
sortField: 'submission_timestamp',
sortOrder: 'asc',
sortBy: function() {
console.log('ApplicationCollection.sortBy', this, arguments);
console.log('thisparator', thisparator);
var models = _.sortBy(this.models, thisparator);
if (this.sortOrder != 'asc') {
models.reverse();
}
return models;
},
parator: function(application) {
console.log('ApplicationCollectionparator', this, arguments);
switch(this.sortField) {
case 'status':
return _.indexOf(this.statusOrder, application.get(this.sortField));
break;
case 'submission_timestamp':
default:
return application.get(this.sortField)
break;
}
}
});
The Model
var ApplicationModel = Backbone.Model.extend({
defaults: {
'drupal_id': 0,
'language': '',
'last_name': '',
'first_name': '',
'address': '',
'zip_code': '',
'city': '',
'country': '',
'telephone': '',
'cell_phone': '',
'email': '',
'date_of_birth': '',
'email': '',
'date_of_birth': '',
'pilot_training_institute': '',
'date_of_exam': '',
'flight_hours_total': '',
'date_of_last_flight': '',
'date_of_mcc_certificate': '',
'date_of_ir_renewel': '',
'package': '',
'submission_timestamp': '',
'status': 'new'
},
urlRoot: 'rest/applications'
});
The current (undesired) result
I've got an instance of the collection stored in 'pendingApplications' like so:
var pendingApplications = new ApplicationCollection();
pendingApplications.fetch();
This loads applications from the server, and all works as planned. I can render a view with a list of applications, all properties are there on the models, etc.
To sort the collection I do the following:
pendingApplications.sortOrder = 'asc';
pendingApplications.sortField = 'current_timestamp'; // format: YYYY-mm-dd HH:mm:ss
pendingApplications.sort();
This triggers the sort function on the Collection. The console tells me the 'ApplicationCollection.sortBy' method executes within the scope of the 'pendingApplications' instance, which is as expected.
However, the 'ApplicationCollectionparator' method executes within the global scope and I'm not sure why. Also none of the arguments on the parator method contain the 'pendingApplications' instance.
What I would like is for my parator method to be executed within the scope of the 'pendingApplications' instance instead, or at least I'd like to be able to somehow be able to access properties on the 'pendingApplications' instance.
Scope issue? Wrong approach? Any suggestions are wele...
Does anyone know how I can resolve this issue? Or am I going about this the wrong way and is there another solution to arbitrarily defining custom sorts on a Backbone.Collection?
The Solution
I ended up implementing a sorting functionality as decorator for the Backbone.Collection. The reason to do it this way is because I've also got a decorator for filtering items in a collection. By using a sorting decorator I can apply sorting to the filtered sub-set of items which is potentially faster.
/**
* returns a new Backbone.Collection which represents a sorted version of the
* data contained within the original Backbone.Collection.
*
* @param {Backbone.Collection} original
*/
SortedCollection: function(original, criteria) {
var sorted = new original.constructor(),
// sensible defaults
defaultSortCriteria = {
custom: {},
field: 'id',
direction: 'asc'
};
// configuration
sorted.sortCriteria = _.extend(defaultSortCriteria, criteria);
// do the stuff
sortedparator = function(a, b) {
// @formatter:off
var criteria = this.sortCriteria,
custom,
field = criteria.field,
direction = criteria.direction,
valA,
valB;
// @formatter:on
// custom sort
if (_.has(criteria.custom, field)) {
custom = criteria.custom[field];
// custom param is a parator itself.
if (_.isFunction(custom)) {
return custom(a, b);
}
// custom param is an example of a sorted array.
else if (_.isArray(custom)) {
valA = _.indexOf(custom, a.get(field));
valB = _.indexOf(custom, b.get(field));
}
else {
throw new Error('Invalid custom sorting criterium.');
}
}
// nothing custom here, use the field value directly.
else {
valA = a.get(field);
valB = b.get(field);
}
// pare that shizzle!
if (valA > valB) {
return (direction == 'desc') ? -1 : 1;
}
if (valA < valB) {
return (direction == 'desc') ? 1 : -1;
}
else {
if (a.get('id') > b.get('id')) {
return (direction == 'desc') ? -1 : 1;
}
else if (a.get('id') < b.get('id')) {
return (direction == 'desc') ? 1 : -1;
}
else {
return 0;
}
}
};
// update collection if original changes
original.on("add", function(model) {
sorted.add(model);
});
original.on("reset", function() {
sorted.reset(original.models);
});
original.on("remove", function(model) {
sorted.remove(model);
});
return sorted;
}
Usage of the decorator:
original = new ApplicationsCollection();
sortable = SortedCollection(original);
sortable.sortCriteria = {
sortField: 'submission_timestamp',
sortDirection: 'desc'
}
sortable.sort();
The above snippet does the following:
- Instantiates a new ApplicationsCollection;
- Instantiates a Sortable Collection that extends the original and listens to relevant events on the original collection.
- Tells the Sortable Collection to sort by 'submission_timestamp' property in descending order.
- sorts the Sortable Collection.
The new Sortable Collection also stays sorted automatically when new models are added to or removed from the original Collection, or when the original Collection is reset.
The desired functionality
I'm using a Backbone.Collection to view data in a sortable list. I've got the part down where clicking on certain dom elements sets properties on my Collection to determine which field i'd like to sort, and which direction the sort should be done. A sort should then be triggered on the Collection after which the view will update.
Eventually I want to be able to sort numerical fields as well strings. Some strings should sort alphabetically, others by a custom predefined order.
My current approach
I've found the following post on reverse sorting strings: Sorting strings in reverse order with backbone.js
I tried to bine this with a parator function and the following custom properties:
- a custom sort order for the possible values in the 'status' field
- which field should be used for sorting
- which direction to sort
The Collection and Model classes I've got so far:
The Collection
var ApplicationCollection = Backbone.Collection.extend({
model: ApplicationModel,
url: 'rest/applications',
// sorting
statusOrder: ['new', 'to_pay', 'payed', 'ready'],
sortField: 'submission_timestamp',
sortOrder: 'asc',
sortBy: function() {
console.log('ApplicationCollection.sortBy', this, arguments);
console.log('this.parator', this.parator);
var models = _.sortBy(this.models, this.parator);
if (this.sortOrder != 'asc') {
models.reverse();
}
return models;
},
parator: function(application) {
console.log('ApplicationCollection.parator', this, arguments);
switch(this.sortField) {
case 'status':
return _.indexOf(this.statusOrder, application.get(this.sortField));
break;
case 'submission_timestamp':
default:
return application.get(this.sortField)
break;
}
}
});
The Model
var ApplicationModel = Backbone.Model.extend({
defaults: {
'drupal_id': 0,
'language': '',
'last_name': '',
'first_name': '',
'address': '',
'zip_code': '',
'city': '',
'country': '',
'telephone': '',
'cell_phone': '',
'email': '',
'date_of_birth': '',
'email': '',
'date_of_birth': '',
'pilot_training_institute': '',
'date_of_exam': '',
'flight_hours_total': '',
'date_of_last_flight': '',
'date_of_mcc_certificate': '',
'date_of_ir_renewel': '',
'package': '',
'submission_timestamp': '',
'status': 'new'
},
urlRoot: 'rest/applications'
});
The current (undesired) result
I've got an instance of the collection stored in 'pendingApplications' like so:
var pendingApplications = new ApplicationCollection();
pendingApplications.fetch();
This loads applications from the server, and all works as planned. I can render a view with a list of applications, all properties are there on the models, etc.
To sort the collection I do the following:
pendingApplications.sortOrder = 'asc';
pendingApplications.sortField = 'current_timestamp'; // format: YYYY-mm-dd HH:mm:ss
pendingApplications.sort();
This triggers the sort function on the Collection. The console tells me the 'ApplicationCollection.sortBy' method executes within the scope of the 'pendingApplications' instance, which is as expected.
However, the 'ApplicationCollection.parator' method executes within the global scope and I'm not sure why. Also none of the arguments on the parator method contain the 'pendingApplications' instance.
What I would like is for my parator method to be executed within the scope of the 'pendingApplications' instance instead, or at least I'd like to be able to somehow be able to access properties on the 'pendingApplications' instance.
Scope issue? Wrong approach? Any suggestions are wele...
Does anyone know how I can resolve this issue? Or am I going about this the wrong way and is there another solution to arbitrarily defining custom sorts on a Backbone.Collection?
The Solution
I ended up implementing a sorting functionality as decorator for the Backbone.Collection. The reason to do it this way is because I've also got a decorator for filtering items in a collection. By using a sorting decorator I can apply sorting to the filtered sub-set of items which is potentially faster.
/**
* returns a new Backbone.Collection which represents a sorted version of the
* data contained within the original Backbone.Collection.
*
* @param {Backbone.Collection} original
*/
SortedCollection: function(original, criteria) {
var sorted = new original.constructor(),
// sensible defaults
defaultSortCriteria = {
custom: {},
field: 'id',
direction: 'asc'
};
// configuration
sorted.sortCriteria = _.extend(defaultSortCriteria, criteria);
// do the stuff
sorted.parator = function(a, b) {
// @formatter:off
var criteria = this.sortCriteria,
custom,
field = criteria.field,
direction = criteria.direction,
valA,
valB;
// @formatter:on
// custom sort
if (_.has(criteria.custom, field)) {
custom = criteria.custom[field];
// custom param is a parator itself.
if (_.isFunction(custom)) {
return custom(a, b);
}
// custom param is an example of a sorted array.
else if (_.isArray(custom)) {
valA = _.indexOf(custom, a.get(field));
valB = _.indexOf(custom, b.get(field));
}
else {
throw new Error('Invalid custom sorting criterium.');
}
}
// nothing custom here, use the field value directly.
else {
valA = a.get(field);
valB = b.get(field);
}
// pare that shizzle!
if (valA > valB) {
return (direction == 'desc') ? -1 : 1;
}
if (valA < valB) {
return (direction == 'desc') ? 1 : -1;
}
else {
if (a.get('id') > b.get('id')) {
return (direction == 'desc') ? -1 : 1;
}
else if (a.get('id') < b.get('id')) {
return (direction == 'desc') ? 1 : -1;
}
else {
return 0;
}
}
};
// update collection if original changes
original.on("add", function(model) {
sorted.add(model);
});
original.on("reset", function() {
sorted.reset(original.models);
});
original.on("remove", function(model) {
sorted.remove(model);
});
return sorted;
}
Usage of the decorator:
original = new ApplicationsCollection();
sortable = SortedCollection(original);
sortable.sortCriteria = {
sortField: 'submission_timestamp',
sortDirection: 'desc'
}
sortable.sort();
The above snippet does the following:
- Instantiates a new ApplicationsCollection;
- Instantiates a Sortable Collection that extends the original and listens to relevant events on the original collection.
- Tells the Sortable Collection to sort by 'submission_timestamp' property in descending order.
- sorts the Sortable Collection.
The new Sortable Collection also stays sorted automatically when new models are added to or removed from the original Collection, or when the original Collection is reset.
Share edited May 23, 2017 at 12:03 CommunityBot 11 silver badge asked Aug 3, 2012 at 18:54 Ruben VreekenRuben Vreeken 9761 gold badge13 silver badges22 bronze badges2 Answers
Reset to default 7The parator()
function is called in the scope of the collection by default, at least in the most current version of Backbone.
I suspect that you may have broken that by defining the sortBy()
function. That function is already defined by Backbone, and is used internally by Backbone's sort()
function in some cases. Try removing that function and see if it works as expected.
It appears that you're just using sortBy()
to reverse the order of your sort. That can be acplished in the parator()
function by multiplying your return value by -1 when appropriate.
As per the other answer, modify you sortBy method to call the original Collection.sortBy method like this:
sortBy: function(){
var models = Backbone.Collection.prototype.sortBy.apply(this, arguments);
if (this.sortOrder != 'asc') {
models.reverse();
}
return models;
}
本文标签: javascriptSorting Backbone CollectionsStack Overflow
版权声明:本文标题:javascript - Sorting Backbone Collections - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741628441a2389217.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论