排序骨干集合 [英] Sorting Backbone Collections

查看:112
本文介绍了排序骨干集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

的期望的功能

我使用的是Backbone.Collection在排序列表来查看数据。我已经得到了部分下降,其中点击某些DOM元素上设置我的收藏属性来决定,我想其中排序字段,哪个方向的排序应该做的。
A排序然后应在采集之后,就会更新触发。

最后,我希望能够以数字字段以及字符串进行排序。一些字符串应该由定制predefined顺序按字母顺序排序,等等。

我目前的做法

我发现逆向排序字符串以下职位:
<一href=\"http://stackoverflow.com/questions/5636812/sorting-strings-in-reverse-order-with-backbone-js\">Sorting字符串相反的顺序Backbone.js的

我想这与比较功能和以下自定义属性结合在一起:


  1. 在状态字段的自定义排序顺序可能值

  2. 哪些领域应该用于分选

  3. 要排序的方向

收集和模型类到目前为止,我有:

收集

  VAR ApplicationCollection = Backbone.Collection.extend({
  模型:应用模型,
  网址:'休息/应用,  //排序
  statusOrder:['新','to_pay','祈祷,','准备好'],
  的SortField:submission_timestamp',
  中将sortOrder:ASC,
  sortBy:功能(){
    的console.log('ApplicationCollection.sortBy,这一点,参数);
    的console.log('this.comparator',this.comparator);
    VAR模型= _.sortBy(this.models,this.comparator);
    如果(this.sortOrder!='递增'){
      models.reverse();
    }
    回归模型;
  },
  比较:功能(应用程序){
    的console.log('ApplicationCollection.comparator,这一点,参数);
    开关(this.sortField){
      案状态:
        返回_.indexOf(this.statusOrder,application.get(this.sortField));
        打破;
      案submission_timestamp:
      默认:
        返回application.get(this.sortField)
        打破;
    }
  }
});

示范

  VAR应用模型= Backbone.Model.extend({
  默认值:{
    'drupal_id':0,
    '语言': '',
    '姓': '',
    '名字': '',
    '地址': '',
    zip_ code':'',
    城市:,
    '国家':'',
    '电话':'',
    '手机': '',
    电子邮件:,
    '出生日期': '',
    电子邮件:,
    '出生日期': '',
    pilot_training_institute':'',
    date_of_exam':'',
    flight_hours_total':'',
    date_of_last_flight':'',
    date_of_mcc_certificate':'',
    date_of_ir_renewel':'',
    '包':'',
    submission_timestamp':'',
    状态:新
  },
  urlRoot:'休息/应用程序
});

当前(不需要的)结果

我有存储在pendingApplications像这样收集的一个实例:

  VAR pendingApplications =新ApplicationCollection();
pendingApplications.fetch();

这从加载服务器应用程序,以及所有作品按计划进行。我可以渲染的应用程序的列表视图,所有属性都出现在模型等。

要排序集合我做到以下几点:

  pendingApplications.sortOrder ='ASC';
pendingApplications.sortField ='CURRENT_TIMESTAMP'; //格式:YYYY-MM-DD HH:MM:SS
pendingApplications.sort();

这触发集合的排序功能。控制台告诉我,'ApplicationCollection.sortBy'法'pendingApplications实例的范围,这是预期内执行。

然而,ApplicationCollection.comparator的方法在全球范围内执行,我不知道为什么。也没有对比较方法的参数包含'pendingApplications实例。

我想是的'pendingApplications实例的范围之内,而不是执行我比较的方法,或者至少我希望能够以某种方式能够对'pendingApplications实例访问性能。

范围的问题?错误的做法?任何建议,欢迎...

有谁知道我怎么能解决这个问题?或者,我要对这个错误的方式,并有另一种解决方案,以任意定义上Backbone.Collection?

自定义排序

解决方案

我最终实现排序功能修饰为Backbone.Collection。
之所以做这种方式是因为我也有一个装饰的集合中筛选项目。
通过使用排序装饰我可以申请排序项的滤波后的子集是潜在的更快。

  / **
 *返回一个新的Backbone.Collection从而重新presents的排序的版本
 *包含原始Backbone.Collection内的数据。
 *
 * @参数{} Backbone.Collection原
 * /
SortedCollection:功能(原来的标准){
  VAR分类=新original.constructor()  //合理的默认值
  defaultSortCriteria = {
    定制:{},
    现场:'ID',
    方向:ASC
  };  //配置
  sorted.sortCriteria = _.extend(defaultSortCriteria,标准);  //做的东西
  sorted.comparator =函数(A,B){
    // @formatter:关闭
    VAR标准= this.sortCriteria,
        定制,
        字段= criteria.field,
        方向= criteria.direction,
        VALA,
        值Valb;
        // @formatter:上    //定义排序
    如果(_.has(criteria.custom,场)){
      定制= criteria.custom [现场]      //自定义的参数是一个比较本身。
      如果(_.isFunction(自定义)){
        返回定制(A,B);
      }
      //自定义的参数是一个有序数组的一个例子。
      否则,如果(_.isArray(自定义)){
        瓦拉= _.indexOf(自定义,a.get(场));
        值Valb = _.indexOf(自定义,b.get(场));
      }
      其他{
        抛出新的错误(无效的自定义排序绕圈。');
      }
    }
    //没什么这里定制,直接使用字段值。
    其他{
      瓦拉= a.get(场);
      值Valb = b.get(场);
    }    //比较一下shizzle!
    如果(VALA&GT;值Valb){
      返回(方向=='说明')? -1:1;
    }
    如果(VALA&LT;值Valb){
      返回(方向=='说明')? 1:-1;
    }
    其他{
      如果(a.get(ID)GT; b.get(ID)){
        返回(方向=='说明')? -1:1;
      }
      否则,如果(a.get(ID)LT; b.get(ID)){
        返回(方向=='说明')? 1:-1;
      }
      其他{
        返回0;
      }
    }
  };  //更新集合,如果原来的变化
  original.on(增加,函数(模型){
    sorted.add(模型);
  });
  original.on(复位,函数(){
    sorted.reset(original.models);
  });
  original.on(删除功能(模型){
    sorted.remove(模型);
  });  返回排序;
}

装饰的用法:

 原=新ApplicationsCollection();
排序= SortedCollection(原件);
sortable.sortCriteria = {
  的SortField:submission_timestamp',
  sortDirection:递减
}
sortable.sort();

上面的代码中执行以下操作:


  • 实例化一个新的ApplicationsCollection;

  • 实例化,它扩展了原有的和听的原始集合相关事件的可分类收集。

  • 告知可排序的收集由submission_timestamp属性降序排列。

  • 排序可排序的集合。

新的可排序的集合也保持自动排序时,新机型中添加或从原来的集合,或者当原始集合复位。

删除
解决方案

比较()函数被调用集合的默认范围,至少在最新版本的骨干。

我怀疑你可能已经打破了通过定义 sortBy()功能。这一功能已经被定义骨干,并通过骨干内部使用排序()函数在某些情况下。尝试删除该功能,看看它是否工作正常。

看样子,你仅仅使用 sortBy()来扭转你的排序顺序。可以在你的返回值乘以比较()功能-1在适当的时候来完成。

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 combine this with a comparator function and the following custom properties:

  1. a custom sort order for the possible values in the 'status' field
  2. which field should be used for sorting
  3. 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.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;
    }
  }
});

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.comparator' method executes within the global scope and I'm not sure why. Also none of the arguments on the comparator method contain the 'pendingApplications' instance.

What I would like is for my comparator 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 welcome...

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.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;
}

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 comparator() 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 accomplished in the comparator() function by multiplying your return value by -1 when appropriate.

这篇关于排序骨干集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆