所需功能
我正在使用Backbone.Collection来查看可排序列表中的数据。点击某些dom元素后,我已经设置了Collection上的属性以确定应该对哪个字段进行排序,以及应该如何进行排序。
排序完成后,应该在Collection中触发一个排序事件,随后视图将更新。
最终,我希望能够对数字字段和字符串进行排序。有些字符串应按字母顺序排序,而其他字符串则按自定义预定义顺序排序。
我的当前方法
我在以下帖子中找到了反向排序字符串的方法:Sorting strings in reverse order with backbone.js
我试着将其与比较函数和以下自定义属性相结合:
- “状态”字段中可能值的自定义排序顺序
- 应该用于排序的字段
- 排序方向
我目前拥有的Collection和Model类:
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.comparator', this.comparator);
var models = _.sortBy(this.models, this.comparator);
if (this.sortOrder != 'asc') {
models.reverse();
}
return models;
},
comparator: function(application) {
console.log('ApplicationCollection.comparator', 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;
}
}
});
模型
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'
});
当前(不希望的)结果
我有一个存储在“pendingApplications”中的集合实例,如下所示:
var pendingApplications = new ApplicationCollection();
pendingApplications.fetch();
这会从服务器加载应用程序,所有的工作都按计划进行。我可以呈现一个带有应用程序列表的视图,模型上所有属性都存在等。
为了对集合进行排序,我执行以下操作:
pendingApplications.sortOrder = 'asc';
pendingApplications.sortField = 'current_timestamp'; // format: YYYY-mm-dd HH:mm:ss
pendingApplications.sort();
这会触发集合上的排序功能。控制台告诉我 'ApplicationCollection.sortBy' 方法在 'pendingApplications' 实例的范围内执行,这是预期的。
然而,'ApplicationCollection.comparator' 方法在全局范围内执行,我不知道为什么。而且比较器方法上的任何参数都不包含 'pendingApplications' 实例。
我希望我的比较器方法能够在 'pendingApplications' 实例的范围内执行,或者至少我想能够以某种方式访问 'pendingApplications' 实例的属性。
范围问题?错误方法?欢迎任何建议......
是否有人知道如何解决这个问题?或者我的方法是错误的,是否有其他解决 Backbone.Collection 自定义排序的方案?
解决方案
最终,我实现了一种用作 Backbone.Collection 装饰器的排序功能。采用这种方式的原因是,我还为集合中的项目实现了一个用于过滤的装饰器。通过使用排序装饰器,我可以将排序应用于子项的过滤子集,这可能更快。
/**
* 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.comparator = 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 comparator 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);
}
// compare 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;
}
装饰器的使用方法:
original = new ApplicationsCollection();
sortable = SortedCollection(original);
sortable.sortCriteria = {
sortField: 'submission_timestamp',
sortDirection: 'desc'
}
sortable.sort();
以上代码片段完成了以下操作:
- 实例化一个新的ApplicationsCollection;
- 实例化一个可排序的集合,该集合扩展了原集合并监听原集合上的相关事件。
- 通知可排序集合按照'submission_timestamp'属性降序排序。
- 对可排序集合进行排序。
当向原集合添加或删除模型,或者重置原集合时,新的可排序集合也会自动保持排序状态。