2012-03-26 80 views
42

排序Backbone.js的集合,我可以成功地做到这一点。但我需要动态地完成这个排序。有时,我需要按照“firstName”来排序。正确的方式来对飞

我彻底的失败者包括:

我想传递一个额外的变量指定变量sort()上。这没有用。我也试过sortBy(),这也没有工作。我尝试将自己的函数传递给sort(),但是这也不起作用。将用户定义的函数传递给sortBy()只是为了让结果没有each方法,从而破坏了新分类的骨干网集合点。

有人可以提供一个实际的例子排序通过一个变量,而不是硬编码到比较函数?或者你有任何破解工作?如果没有,请拨打sortBy()电话?

回答

36

有趣的问题。我会在这里尝试一个关于战略模式的变体。您可以创建排序功能的哈希值,然后基于散列的选定成员上设置comparator

App.SomeCollection = Backbone.Collection.extend({ 
    comparator: strategies[selectedStrategy], 
    strategies: { 
     firstName: function() { /* first name sorting implementation here */ }, 
     lastName: function() { /* last name sorting implementation here */ }, 
    }, 
    selectedStrategy: "firstName" 
}); 

然后,你可以通过更新selectedStrategy属性的值更改动态的排序策略。

编辑:我意识到我睡觉后:)这不会像我上面写的那样工作,因为我们将对象字面值传递给Collection.extend。在创建对象时,comparator属性将被评估一次,因此除非被迫这样做,否则它不会随时更改。有可能是一个更清洁的方式做到这一点,但是这证明了在飞行中切换比较功能:

var SomeCollection = Backbone.Collection.extend({ 
    comparator: function (property) { 
     return selectedStrategy.apply(myModel.get(property)); 
    }, 
    strategies: { 
     firstName: function (person) { return person.get("firstName"); }, 
     lastName: function (person) { return person.get("lastName"); }, 
    }, 
    changeSort: function (sortProperty) { 
     this.comparator = this.strategies[sortProperty]; 
    }, 
    initialize: function() { 
     this.changeSort("lastName"); 
     console.log(this.comparator); 
     this.changeSort("firstName"); 
     console.log(this.comparator);   
    }                       
}); 

var myCollection = new SomeCollection; 

这里有一个jsFiddle演示这一点。

我认为,所有问题的根源在于创建对象时立即评估JavaScript对象文字的属性,因此如果要更改它,必须覆盖该属性。如果您尝试写入某种切换到属性本身,它将被设置为初始值并保持在那里。

下面是一个很好的blog post,在略有不同的上下文中讨论这个问题。

+0

谢谢。建议的实现和变体已经失败,但我很感谢回应:)。我想我在这里错过了一些非常小的东西。非常令人沮丧。我倾向于另一个我的javascript工作框架,考虑了我使用简单的排序方式难度有多大。< – kikuchiyo 2012-03-26 04:59:44

+0

我即将自行解决此问题。我的应用程序显示一个表。用户将通过单击表格中的列名称来对表格内容进行排序。我正在考虑让桌面成为一个拥有自己的“排序”模型的区域。该模型将跟踪当前的“sortCriteria”。点击列名将更新sortCriteria。我将在集合中有一个比较器功能。它只会从排序模型中获取sortCriteria。 – 2014-07-16 17:34:36

+0

需要更新JSFiddles以使用服务器上存在的Underscore和Backbone。 cdnjs.com上的那些很好。 JSFiddle用来提供自己的,但我猜不再了。 我的初步评估有点苛刻,但我认为'myModel'应该用传递给比较器的参数来替换,比较器应该是集合中的每个模型。 – 2016-03-06 19:21:04

12

所以,这是我的解决方案,实际工作。

App.Collection = Backbone.Collection.extend({ 

    model:App.Model, 

    initialize: function(){ 
     this.sortVar = 'firstName'; 
    }, 

    comparator: function(collection){ 
     var that = this; 
     return(collection.get(that.sortVar)); 
    } 

    }); 

然后在视图中,我有米老鼠这样的:

this.collections.sortVar = 'lastVar' 

this.collections.sort(this.comparator).each(function(){ 
    // All the stuff I want to do with the sorted collection... 
}); 

由于乔希·厄尔是连尝试的解决方案是唯一一个和他领我在正确的方向,我接受他的回答。谢谢乔希:)

+1

感谢您的回答。 :)我更新了我原来的帖子 - 看看是否适合你。 – 2012-03-26 11:52:56

+0

如果您恰好在[Udacity的JavaScript设计模式](https://www.udacity.com/course/viewer#!/c-ud989/l-3525509902/m-3574768574)中进行练习,您必须在其中动态更改ToDo示例的排序顺序,请注意,您必须调用'app.AppView'的'addAll'方法来重新添加新排序项目的列表。除了重新呈现视图之外,还要检查列表的填充方式。 [这是一个例子](https://github.com/enderandpeter/ud989-todo-app/blob/js-design-patterns/js/views/app-view.js#L119) – 2016-03-06 19:24:01

16

通过分配一个新的函数和调用排序来改变比较函数。

// Following example above do in the view: 

// Assign new comparator 
this.collection.comparator = function(model) { 
    return model.get('lastname'); 
} 

// Resort collection 
this.collection.sort(); 

// Sort differently 
this.collection.comparator = function(model) { 
    return model.get('age'); 
} 
this.collection.sort(); 
+0

这就是我一直也完成了。我在集合上使用了mixin,因此它可用于所有集合。我也触发集合的重置,因为我通常拥有需要更新的视图。 – jhorback 2013-09-15 00:22:55

+0

非常简单的答案。谢谢! – Trip 2015-01-07 10:10:39

+0

我可能会错过一些东西,但这似乎是最好的解决方案。 – 2015-08-13 00:39:20

4

这是一个老问题,但我最近也有类似的需要(排序由用户点击事件中提供基于标准的集合),我想我会分享我为别人解决这个问题的解决方案。不需要硬编码的model.get('attribute')。

我基本上是用戴夫牛顿approach延长本地JavaScript阵列,并针对骨干:

MyCollection = Backbone.Collection.extend({ 

    // Custom sorting function. 
    sortCollection : function(criteria) { 

     // Set your comparator function, pass the criteria. 
     this.comparator = this.criteriaComparator(criteria); 
     this.sort(); 
    }, 

    criteriaComparator : function(criteria, overloadParam) { 

     return function(a, b) { 
      var aSortVal = a.get(criteria); 
      var bSortVal = b.get(criteria); 

      // Whatever your sorting criteria. 
      if (aSortVal < bSortVal) { 
       return -1; 
      } 

      if (aSortVal > bSortVal) { 
       return 1; 
      } 

      else { 
       return 0; 
      } 

     }; 
    } 

}); 

注意“overloadParam”。根据documentation,Backbone使用Underscore的“sortBy”(如果您的比较器函数具有单个参数),如果它具有两个参数,则使用原生JS风格的排序。我们需要后者,因此是“overloadParam”。

0

这就是我最终为我正在开发的应用程序所做的事情。在我的收藏,我有:

comparator: function(model) { 
    var methodName = applicationStateModel.get("comparatorMethod"), 
     method = this[methodName]; 
    if (typeof(method === "function")) { 
     return method.call(null, model); 
    } 
} 

现在,我可以添加一些不同的方法来我的收藏:fooSort(),barSort(),和bazSort()。
我想foo​​Sort是默认的,所以我设置在我的状态模型,如下所示:

var ApplicationState = Backbone.Model.extend({ 
    defaults: {    
     comparatorMethod: "fooSort" 
    } 
}); 

现在我所要做的就是写在我看来,一个函数依赖于一个更新“comparatorMethod”的价值用户点击了什么。我将集合设置为侦听这些更改并执行sort(),并将视图设置为侦听排序事件并执行render()。

BAZINGA !!!!

3

看着source code,似乎有一个简单的方法来做到这一点,将比较器设置为字符串而不是函数。这工作,给定Backbone.Collection mycollection:

 mycollection.comparator = key; 
     mycollection.sort(); 
+0

但是,如果我们在集合中有空值,它将不起作用。 – Jyothu 2016-07-01 05:41:57