排序 Backbone 集合

3

所需功能

我正在使用Backbone.Collection来查看可排序列表中的数据。点击某些dom元素后,我已经设置了Collection上的属性以确定应该对哪个字段进行排序,以及应该如何进行排序。

排序完成后,应该在Collection中触发一个排序事件,随后视图将更新。

最终,我希望能够对数字字段和字符串进行排序。有些字符串应按字母顺序排序,而其他字符串则按自定义预定义顺序排序。

我的当前方法

我在以下帖子中找到了反向排序字符串的方法:Sorting strings in reverse order with backbone.js

我试着将其与比较函数和以下自定义属性相结合:

  1. “状态”字段中可能值的自定义排序顺序
  2. 应该用于排序的字段
  3. 排序方向

我目前拥有的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'属性降序排序。
  • 对可排序集合进行排序。

当向原集合添加或删除模型,或者重置原集合时,新的可排序集合也会自动保持排序状态。

2个回答

7
comparator() 函数默认情况下在集合的范围内调用,至少在 Backbone 的最新版本中是这样的。我怀疑你定义了 sortBy() 函数而破坏了它。这个函数已经被 Backbone 定义了,并且在某些情况下被 Backbone 的 sort() 函数内部使用。尝试移除该函数并查看是否按预期工作。
看起来你只是在使用 sortBy() 来反转排序顺序。这可以在 comparator() 函数中通过在适当时候将返回值乘以 -1 来实现。

是的,sortBy是一个下划线方法,并内置于集合中。 - Trevor
谢谢,很高兴知道比较器通常在集合的范围内执行。那么我至少不会试图破坏Backbone本身的基本功能。但是我需要能够对字符串值进行排序,而不仅仅是数字值。如果我只使用比较器,那么这是行不通的,因为我无法将字符串乘以-1。有什么想法可以修改我的sortBy方法,而不会搞乱作用域? - Ruben Vreeken
如果您的比较函数接受两个值,那么它将自动以更经典的方式运行。基本上,它会给您集合中的两个项,您需要返回一个正数或负数来指示哪个应该排在前面(如果相等则返回零)。这在文档中有详细说明:http://backbonejs.org/#Collection-comparator - Brian Reischl
抱歉回复晚了。原来带有两个参数的比较器非常好用! - Ruben Vreeken

0
根据其他答案,修改您的sortBy方法以调用原始的Collection.sortBy方法,如下所示:
sortBy: function(){
  var models = Backbone.Collection.prototype.sortBy.apply(this, arguments);
  if (this.sortOrder != 'asc') {
    models.reverse();
  }
  return models;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接