3

所需的功能

我正在使用 Backbone.Collection 来查看可排序列表中的数据。我已经完成了单击某些 dom 元素会在我的 Collection 上设置属性的部分,以确定我想要排序的字段以及应该进行排序的方向。然后应该在 Collection 上触发排序,之后视图将更新。

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

我目前的做法

我在反向排序字符串上找到了以下帖子: 使用backbone.js 以反向顺序排序字符串

我尝试将其与比较器函数和以下自定义属性结合使用:

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

到目前为止我得到的 Collection 和 Model 类:

收藏品

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”属性降序排序。
  • 对可排序集合进行排序。

当新模型被添加到原始集合中或从原始集合中删除时,或者当原始集合被重置时,新的可排序集合也会自动保持排序。

4

2 回答 2

7

默认情况下,该comparator()函数在集合范围内调用,至少在最新版本的 Backbone 中是这样。

我怀疑你可能已经通过定义sortBy()函数打破了这一点。该函数已由 Backbone 定义,并且sort()在某些情况下由 Backbone 的函数内部使用。尝试删除该功能,看看它是否按预期工作。

看来您只是sortBy()用来颠倒排序的顺序。这可以通过在comparator()适当的时候将返回值乘以 -1 在函数中完成。

于 2012-08-03T19:15:53.150 回答
0

根据另一个答案,修改您的 sortBy 方法以调用原始的 Collection.sortBy 方法,如下所示:

sortBy: function(){
  var models = Backbone.Collection.prototype.sortBy.apply(this, arguments);
  if (this.sortOrder != 'asc') {
    models.reverse();
  }
  return models;
}
于 2012-08-05T19:22:30.587 回答