对骨干集合进行排序

时间:2012-08-03 18:54:32

标签: javascript sorting backbone.js

所需的功能

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

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

我目前的做法

我在反向排序字符串上发现了以下帖子: Sorting strings in reverse order with backbone.js

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

  1. “状态”字段中可能值的自定义排序顺序
  2. 哪个字段应该用于排序
  3. 排序方向
  4. 到目前为止我收集的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();
    

    这会触发Collection上的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;
    • 实例化可扩展的集合,扩展原始集合并侦听原始集合上的相关事件。
    • 告诉Sortable Collection按'submission_timestamp'属性按降序排序。
    • 对Sortable Collection进行排序。

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

2 个答案:

答案 0 :(得分:7)

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

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

您似乎只是使用sortBy()来改变排序顺序。这可以在comparator()函数中通过在适当时将返回值乘以-1来完成。

答案 1 :(得分:0)

根据其他答案,修改sortBy方法以调用原始的Collection.sortBy方法,如下所示:

sortBy: function(){
  var models = Backbone.Collection.prototype.sortBy.apply(this, arguments);
  if (this.sortOrder != 'asc') {
    models.reverse();
  }
  return models;
}