Jquery-UI可排序列表与Meteor模板中的响应更新不一致

时间:2014-11-15 09:26:25

标签: javascript jquery-ui meteor meteor-blaze

我试图以http://differential.com/blog/sortable-lists-in-meteor-using-jquery-ui中描述的方式使用JQuery-UI实现可排序的对象列表。

但是,我不是对单独文档的列表进行排序,而是对单个文档中嵌入的对象列表进行排序。也就是说,我有一个这样的文件:

{  name: "Name of this Rolodex",
   cards: [{name: "...", rank: 0, id: "some-unique-id"},
           {name: "...", rank: 1, id: "some-other-unique-id"}, ... ]
}

我只想让这些卡片可以分类。我的模板如下 - 它传递了一个Rolodex作为上下文:

<template name="rolodex">
  Rolodex Name: {{name}}
  <div class="cards-list">
    {{#each sortedCards}}
      {{> cardTemplate}}
    {{/each}}
  </div>
</template>

帮手JS文件:

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});
    }
  });
};

我遇到的问题是,在将卡片分类到其预期位置之后,然后使用卡片在新的错误位置更新DOM。服务器存储了正确的排名,刷新页面会使卡列在正确的位置(至少在尝试另一种排序之前)。

我最好的猜测是,Meteor的模板系统似乎并没有理解JQuery-UI已经移动了DOM元素并且以错误的顺序反复更新我的模板。

例如,假设我的卡片是:A,B,C。我移动C使得我们现在有C,A,B。JQuery-UI相应地更新DOM并触发导致C&#39; s的事件等级被改为小于A

然而,Meteor并不知道DOM已经被JQuery-UI改变了。但是,它确实将排名变为C,并根据先前关于列表顺序的假设反应性地更新列表的顺序。在这种情况下,我们最终得到B,C,A。

有关我在这里做错了什么的建议吗?

2 个答案:

答案 0 :(得分:13)

Meteor / Blaze使用_id属性来标识数据对象并将它们链接到DOM元素。这不仅适用于Collection光标返回的文档数组,也适用于任何对象数组。因此,在上述问题中,问题是我使用id值来识别每张卡而不是_id。将id切换为_id可以解决问题,并允许Blaze正确更新DOM,即使DOM之前已被JQuery-UI的可排序插件修改过。

答案 1 :(得分:3)

Meteor反应力迫使您选择负责DOM更新的人员。

虽然可以让Blaze渲染DOM然后使用第三方库(通常是在.rendered()方法中调用的jQuery插件)来操作它,但是现在你处于Blaze没有的状态。 ;知道你的DOM发生了什么,所以每次后续的反应更新都不一致。

这就是为什么对于交互式和被动接口元素,我们需要一类新的插件/组件/包Meteor-aware(或更好的反应感知)。例如,请参阅将difficulties移植到Meteor与Meteoric datatables.netreactive-tables

所有这一切,我克服这个问题的黑客就是编写一些反应代码,在DB更新时负责破坏和重建插件。这样,你恢复了Blaze所知道的原始DOM,让他更新它,然后重新调用你的jQuery插件。这是一个远非(非常远)的最佳解决方案,但却节省了我几天的时间。

对于.sortable(),最好的解决方案是禁用{reactive: false}中选项Collection.find()的反应,这样当您使用Meteor.call更新卡片的属性时,不需要重绘但你的界面已经一致了。