3

I am uploading files asynchronously (using FormData) at specific index in the list of files with:

files.create({position: index + 1 }, {at: index}); // files is Backbone.Collection

The server then saves upload and shifts positions for the files after specific position to free the place for the new inserted file.

Then on client I listen to add event and use index from options to insert file view.

files.on("add", function(model, collection, options) {
    // insert file view at options.index position
})

I also update position attribute for all models in collection:

files.on("add remove", function(){ 
  this.each(function(m, index) {
    m.set({position: index + 1});
  })
});

The problem is when I upload many files at one time at the same index position, file views append to the list in the wrong order.

How to ensure correct order and position attribute for file models in backbone collection?

Andrey Kuzmin
  • 4,479
  • 2
  • 23
  • 28

2 Answers2

0

Instead of using javascript-originated position, use server generated id, for example unix timestamp, and as mu is too short suggested, sort your collection using

comparitor: function() { 
    return -this.timestamp // negative ensures latest items show up on top, and olders at the bottom 
}

if you need more specific sorting you can always ether go to a more granular microtime, or simply generate an incremental id server-side.

Stepan Mazurov
  • 1,354
  • 10
  • 15
  • I don't need chronological order. It should work with javascript position as a sorting key. Server then updates positions to ensure they are incremental. But because each file is uploaded asyncrously, files are inserted in the wrong order. I was able to override collection.create to queue Ajax requests. It seems to work. – Andrey Kuzmin Nov 29 '12 at 08:51
0

I was able to override collection.create that queues ajax requests. To do so I used the code from this answer.

var Documents = Backbone.Collection.extend({

  model: Document,

  url: "/a/documents",

  initialize: function() {
    this.on("add remove", this.updatePositions, this);
    this.ajaxQueue = $({});
  },

  updatePositions: function(model, collection, options) {
    this.each(function(m, index) {
      m.set({position: index + 1});
    })
  },

  create: function(model, options) {
    var jqXHR
      , dfd = $.Deferred()
      , promise = dfd.promise()
      , coll = this;

    // queue our ajax request
    this.ajaxQueue.queue(doRequest);

    // add the abort method
    promise.abort = function(statusText) {
      // Proxy abort to the jqXHR if it is active
      if (jqXHR) {
        return jqXHR.abort(statusText);
      }

      // If there wasn't already a jqXHR we need to remove from queue
      var queue = this.ajaxQueue.queue()
        , index = $.inArray(doRequest, queue);

      if (index > -1) {
        queue.splice(index, 1);
      }

      // Reject the deferred
      dfd.rejectWith(options.context || options,
                     [promise, statusText, ""]);

      return promise;
    };

    // Run the actual collection.create
    function doRequest(next) {
      options = options ? _.clone(options) : {};
      model = coll._prepareModel(model, options);
      if (!model) return false;
      if (!options.wait) coll.add(model, options);
      var success = options.success;
      options.success = function(nextModel, resp, xhr) {
        if (options.wait) coll.add(nextModel, options);
        if (success) {
          success(nextModel, resp);
        } else {
          nextModel.trigger('sync', model, resp, options);
        }
      };
      jqXHR = model.save(null, options)
        .done(dfd.resolve)
        .fail(dfd.reject)
        .then(next, next);
    };

    return promise;
  }

});
Community
  • 1
  • 1
Andrey Kuzmin
  • 4,479
  • 2
  • 23
  • 28