I'm trying to implement a sortable list of objects with JQuery-UI in the manner described at http://differential.com/blog/sortable-lists-in-meteor-using-jquery-ui.
However, rather than sort a list of separate documents, I'm sorting a list of objects embedded within a single document. That is, I have a document like so:
{ name: "Name of this Rolodex",
cards: [{name: "...", rank: 0, id: "some-unique-id"},
{name: "...", rank: 1, id: "some-other-unique-id"}, ... ]
}
And I just want to make the cards sortable. My template is as follows -- it's passed a single Rolodex as the context:
<template name="rolodex">
Rolodex Name: {{name}}
<div class="cards-list">
{{#each sortedCards}}
{{> cardTemplate}}
{{/each}}
</div>
</template>
And the helper JS file:
Template.rolodex.helpers({
sortedCards: function() {
return this.cards.sort(function(cardA, cardB) {
return cardA.rank - cardB.rank;
});
}
});
Template.rolodex.rendered = function() {
this.$(".cards-list").sortable({
stop: function(e, ui) {
// Get dragged HTML element and one immediately before / after
var el = ui.item.get(0)
var before = ui.item.prev().get(0)
var after = ui.item.next().get(0)
// Calculate new rank based on ranks of items before / after
var newRank;
if(!before) {
// First position => set rank to be less than card immediately after
newRank = Blaze.getData(after).rank - 1;
} else if(!after) {
// Last position => set rank to be more than card immediately after
newRank = Blaze.getData(before).rank + 1;
} else {
// Average before and after
newRank = (Blaze.getData(after).rank +
Blaze.getData(before).rank) / 2;
}
// Meteor method that updates an attribute for a single card in a
// Rolodex based on IDs for the Rolodex and Card
Meteor.call('cards/update',
Blaze.getData(ui.item.parent().get(0))._id, // Rolodex ID
Blaze.getData(el).id, // Card ID
{rank: newRank});
}
});
};
The problem I'm running into is that after sorting a card into its expected position, the DOM is then updated with the card in a new, wrong position. The server has the correct rankings stored though, and refreshing the page causes the card to be listed in its correct position (at least until another sort is attempted).
My best guess as to what is happening is that Meteor's templating system doesn't seem to understand that the JQuery-UI has moved the DOM elements around and is reactively updating my template in the wrong order.
For example, suppose my cards are: A, B, C. I move C such that we now have C, A, B. JQuery-UI updates the DOM accordingly and fires an event which results in C's rank being changed to be less than A's.
However, Meteor doesn't know that the DOM has already been altered by JQuery-UI. It does, however, see the change in rank to C and reactively updates the order of the list based on prior assumptions about what the list order was. In this case, we end up with B, C, A.
Any suggestions as to what I'm doing wrong here?