2

I am having an issue with getting drag-and-drop sort functionality with EmberJS working.

The tutorials that cover this issue don't provide any help on the initial sort, which I am doing through a computed property.

What I'm encountering seems to be a race condition between ember's rerender when the sortedItems computed property changes and jQueryUI Sortable updating the DOM. List Items get duplicated, or disappear altogether upon sorting.

Route

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return Ember.Object.create({
      id: 1,
      title: "Title",
      subtitle: "Subtitle",
      items: [
        Ember.Object.create({
          name: "Item 2",
          sortOrder: 2,
          id: 1
        }),
        Ember.Object.create({
          name: "Item 1",
          sortOrder: 1,
          id: 2
        }),
        Ember.Object.create({
          name: "Item 3",
          sortOrder: 3,
          id: 3
        })
      ]
    });
  }
});

Controller

import Ember from 'ember';

export default Ember.ObjectController.extend({

  sortProperties: [ 'sortOrder' ],
  sortedItems: Ember.computed.sort('model.items', 'sortProperties'),

  actions: {
    updateSortOrder: function(indexes) {
      this.get('items').forEach(function(item) {
        var index = indexes[item.get('id')];
        item.set('sortOrder', index);
      });
    }
  }

});

View

import Ember from 'ember';

export default Ember.View.extend({

  didInsertElement: function() {
    var controller = this.get('controller');

    this.$(".item-list").sortable({
      update: function(event, ui) {
        var indexes = {};

        $(this).find('li').each(function(index) {
          indexes[$(this).data('id')] = index;
        });

        controller.send('updateSortOrder', indexes);
      }
    })
  }
});

Template

<h1>{{ title }}</h1>
<h2>{{ subtitle }}</h2>

<ul class="item-list">
  {{#each item in sortedItems }}
    <li data-id="{{ unbound item.id }}">
      name: {{ item.name }}<br />
      sortOrder: {{ item.sortOrder }}<br />
      id: {{ item.id }}
    </li>
  {{/each}}
</ul>

Here's a Barebones Ember app that reproduces the issue: https://github.com/silasjmatson/test-sortable

Question

Is there a way to avoid this race condition or sort only once when the controller is initialized?

Apologies if there is already an answer on the interwebs for this. I haven't been able to resolve this despite a week of searching/experimenting.

1 Answer 1

4

You can sort only once when the controller is initialised:

sortedItems: Ember.computed(function() {
    return Ember.get(this, 'model.items').sortBy('sortOrder');
})

Because you're using the computed sort property it is attempting to sort the list for you whenever the sortOrder property changes on any item, which is rerendering the #each and doing something funky.

Just sort once initially using the method above and then let jQuery handle the order of items - rather than jQuery and Ember.

Sign up to request clarification or add additional context in comments.

2 Comments

That will actually sort the list when you first access the 'sortedItems' property.. So it's actually after the controller in initialized.
I'd give you 500 stackoverflow points if I could, @jmurphyau.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.