Search code examples
backbone.js

Backbone: How to change the order of models


In a collection, how can we change the order of models?

This is not about sorting the models, or writing a comparator function to achieve a sorting order.

When a collection is built from an array of objects, the order of models will be the insertion order.

Now my question is: After the collection is built, how to change the position of the models as per my preference.

For example, When the collection is built, order of models as per the original array like: 0, 1, 2, 3, 4, 5. Now, I want to change the order like 2, 4, 0, 1, 3, 5.


Solution

  • I know this is an old post, but I had the same one and came up with a solution. In short, my solution is to replace the collection.models list with a new list containing the same models but in the order I wanted it to be in... Below is my use case and example (using coffee script).

    NOTE: I am not 100% sure this will not be buggy since this changes collection's list rather than sorting the original list in-place. But I have not yet found a failure mode and I've tested several.

    My use case was a DOM list that is sortable (via jquery.ui/sortable). I need the collection's order to reflect what is shown on the DOM.

    1. Each sortable DOM element has a data-cid attribute corresponding to the model, I did this in the initialize method of each Backbone.View list item.

      class ListItem extends Backbone.View
        ... 
        initialize: (options = {}) ->
          @$el.attr "data-cid", @model.cid
      
    2. The parent Backbone.View (an unordered list) listens to a sortupdate event (the user sorted something), then constructs a list of cids with the new order, and passes this into the collection to be sorted.

      class List extends Backbone.View
        ...
        tagName: "ul"
        events:
          "sortupdate" : "sort"
      
        render: ->
          @$el.html("") # reset html
          @collection.each (model) =>
            view = new ListItem model: model
            @$el.append view.render().$el
          return @
      
        sort: (e, ui) ->
          e.stopPropagation()
          orderByCid = @$el.children().map(-> $(@).attr "data-cid")
          @collection.orderModels orderByCid
      
    3. The collection makes a new list of models in the same order as the cids using Backbone.Collection::get and then replaces its model list with this.

      class DataList extends Backbone.Collection
        ...
        orderModels: (cids = []) ->
          # Add validation that all the cids exist in the collection
          # and that list and collection are the same length!
          result = []
          result.push @get(cid) for cid in cids
          @models = result