-1

I am trying to sync a sorted Backbone.Collection with another list, e.g. a JavaScript Array. I am using the following code (jsfiddle):

var coll = new Backbone.Collection();
coll.comparator = "label";

var list = []

coll.on("add", function (model, collection, options) {
    list.splice(collection.indexOf(model), 0, model.get("label"));
});

coll.add([
    {label: "1"},
    {label: "3"},
    {label: "5"},
    {label: "4"},
    {label: "2"}
]);

In this example, this results in the following list: 1, 2, 3, 5, 4. The root cause is, that within the add event handler the Backbone.Collection is already filled with all models, while the JS array is not. Since the add events are triggered in insertion order, not sorted order, this leads to the wrong order in the array.

How would I change my sync approach/add handler to make things work?

Gregor Schmidt
  • 639
  • 4
  • 11

2 Answers2

0

It sounds like your error occurs during initialization:

var coll = new Backbone.Collection([
    {label: "1"},
    {label: "3"},
    {label: "5"},
    {label: "4"},
    {label: "2"}
], { comparator: 'label' )

var list = coll.pluck('label')

// Assuming your add-handler works, you can keep that around for future add's
coll.on("add", function (model, collection, options) {
    list.splice(collection.indexOf(model), 0, model.get("label"));
});

If this does not suffice, then you'll need to do some work involving the sync event, which occurs after the add's. However, I would recommend that your view of li's just listens to coll then completely re-renders whenever coll fires off add, remove, or sort. This would completely prevent attempting to keep two arrays in sync with one another, which is a no-no in programming. Generally, you would create a data structure to handle that interaction, but your collection should be enough:

var LabelsView = Backbone.Collection.extend({
    el: '#my-list',
    template: '<% coll.each(function(model){ %> <li><%- model.get("label") %></li><% }); %>',
    initialize: function(){
        this.listenTo(this.collection, 'add remove sort', this.render)
    }
    render: function(){
        this.$el.html( this.template({ coll: this.collection }) )
    }
}) 

var labelView = new LabelsView({ collection: coll })
ncksllvn
  • 5,699
  • 2
  • 21
  • 28
  • The error does not only occur during initialization. I happens whenever I add a set of unsorted models within one add call to a collection. Therefore your first proposal will not be sufficient. As I pointed out in another [answer's comment](http://stackoverflow.com/questions/25992107/synching-an-array-with-a-sorted-backbone-collection/26004958?iemail=1&noredirect=1#comment40706037_25992898), re-rendering the view is not feasible as well, since it contains sub-views, which have internal state. This state would be lost, whenever an unrelated element is added to the collection. – Gregor Schmidt Sep 24 '14 at 07:11
  • These are all important details that should have been included in your question. – ncksllvn Sep 24 '14 at 12:35
0

First you need listen to sort event

    this.listenTo(this.collection, 'sort', this.sortList);

Then add data-id identifier to each list item

    var li = $("<li>", { "data-id": model.get("label") }).html(model.get("label"));

And then sort html items based on not equal position

    sortList: function(collection, options) {
        collection.forEach(function(model, index) {
            var li1 = this.$el.children().eq(index),
                li2;
            if (model.get("label") != li1.data("id")) {
                li2 = this.$el.children("[data-id=" + model.get("label") + "]");
                li1.before(li2);
            }
        }, this);
    },

jsfiddle link http://jsfiddle.net/n0fdmb8a/3/

Eugene Glova
  • 1,543
  • 1
  • 9
  • 12
  • Thanks for your reply, but as I pointed out in another [answer's comment](http://stackoverflow.com/questions/25992107/synching-an-array-with-a-sorted-backbone-collection/26004958?iemail=1&noredirect=1#comment40706037_25992898), I do not want to recreate the list on each change. – Gregor Schmidt Sep 24 '14 at 07:07
  • Thanks for your suggestion. Based on this approach, I have further simplified the code. Since this seems to be the only viable approach, I am accepting this answer. JS Fiddle for ul/li (http://jsfiddle.net/schm/n0fdmb8a/4/) – Gregor Schmidt Sep 24 '14 at 09:32
  • Great, I forgot about `model.cid`, wanted firstly to use `model.id` but it doesn't exist so I didn't want to add it in collection and decided to use `label` as id. – Eugene Glova Sep 24 '14 at 09:38
  • That's a lot of jQuery for a Backbone app. – ncksllvn Sep 24 '14 at 14:19