简体   繁体   English

在Ember.js视图中刷新jQuery绑定的正确方法是什么?

[英]What is the proper approach to refreshing jQuery bindings in an Ember.js view?

I have an Ember.js app that has a Checklist model, where each Checklist has many Checkitems . 我有一个具有Checklist模型的Ember.js应用,其中每个清单都有许多Checkitems (A variant of the classic ToDo app, but with multiple TodoLists.) (经典ToDo应用的变体,但具有多个TodoList。)

In the top-most view, the user sees a listing of all available checklists to the left. 在最上方的视图中,用户会在左侧看到所有可用清单的列表。 When a checklist is selected, the corresponding checkitems appear to the right. 选择一个清单后,相应的清单项目将显示在右侧。

The checkitems on the right side are drag sortable. 右侧的检查项可拖动排序。 I'm using this html5sortable library to handle drag sorting. 我正在使用html5sortable库来处理拖动排序。 It's like the classic jQueryUI version, but less clunky. 就像经典的jQueryUI版本一样,但不那么笨拙。

Upon initial loading of the app, the sortable list works fine. 最初加载应用程序后,可排序列表可以正常工作。 However, if the list of checkitems changes (either because a checkitem is marked as complete, a new checkitem is added, changes to an existing check item are saved, or another checklist is selected on the left), the binding to html5sortable is lost. 但是,如果检查项目列表发生更改(由于检查项目标记为已完成,添加了新检查项目,保存了对现有检查项目的更改或在左侧选择了另一个检查列表),则与html5sortable的绑定将丢失。

When the app first loads, I have a view called App.CheckitemsPendingTableView : 首次加载应用程序时,我有一个名为App.CheckitemsPendingTableView的视图:

App.CheckitemsPendingTableView = Ember.View.extend({
  templateName: 'app/templates/checkitems/checkitemsPendingTable',
  classNames: ['checkitems-list', 'sortable', 'list'],
  tagName: 'ul',

  didInsertElement: function() {
    this._super();
    $('ul.sortable').sortable();
    console.log('CheckitemsPendingTableView has been inserted in the DOM and is bound to sortable.  At this point, drag-sorting of the list is working fine.');
  }
});

The corresponding template is called checkitemsPendingTable.handlebars and it looks like this: 相应的模板称为checkitemsPendingTable.handlebars ,它看起来像这样:

{{#each content}}
  {{view App.CheckitemSingleView checkitemBinding="this"}}
{{/each}}

And for good measure, the controller that feeds the content attribute for that view is App.checkitemsController.remainingItems : 出于良好的考虑,为该视图提供content属性的控制器是App.checkitemsController.remainingItems

App.checkitemsController = Ember.ArrayProxy.create({
  content:[],

  ...snip...

  remainingItems: function() {
    var checkitems = this.get('content');
    var sortedCheckitems = checkitems.filterProperty('isDone', false).sort(function(a,b) {
                            return a.get('position') - b.get('position');
                          });
    return sortedCheckitems;
  }.property('content.@each.isDone'),

  ...snip...

});

The content attribute of the checkitemsController is driven by the checklistsController : checkitemsController的content属性由checklistsController驱动:

App.checklistsController = Ember.ArrayProxy.create({
  content: App.store.findAll(App.Checklist),

  selectedChanged: function() {
    var checklist = this.get('selected');
    var checkitems = checklist.get('checkitems');
    App.checkitemsController.set('checklist', checklist);
    App.checkitemsController.set('content', checkitems);
  }.observes('selected')
});

(You may have noticed that this controller pulls its data from a Rails backend via ember-data. This shouldn't matter for the current issue, though.) (您可能已经注意到,该控制器通过ember-data从Rails后端提取其数据。不过,这对于当前问题不重要。)

The view for the left-hand side's menu is called checklistsView . 左侧菜单的视图称为checklistsView It has a child view called checklistSingleView that is rendered for each of the checklists: 它具有一个称为checklistSingleView的子视图,该视图为每个检查表呈现:

App.ChecklistSingleView = Ember.View.extend({
  templateName: 'app/templates/checklists/checklistSingle',
  classNames:   ['menu-item'],
  tagName: 'tr',

  ...snip...

  chooseList: function() {
    var checklists = App.checklistsController.get('content');
    checklists.setEach('isActive', false);
    var checklist = this.get('checklist');
    checklist.set('isActive', true);
    App.checklistsController.set('selected', checklist);
  }

  ...snip...

});

And, finally, the corresponding template checklistSingle.handlebars contains a link that is tied to the chooseList by way of an action: 最后,相应的模板checklistSingle.handlebars包含一个通过操作绑定到chooseList的链接:

<a href="#" {{action "chooseList"}}>{{checklist.name}}</a>

So, everything above works brilliantly...until the user causes a change to the ul of checkitems on the right. 所以,上面的一切工作出色......直到用户引起变化的ul右侧checkitems的。 At that point, the binding to html5sortable is lost, and I cannot find a convenient place to refresh it. 那时,与html5sortable的绑定丢失了,我找不到方便的位置来刷新它。

The problem is that didInsertElement is not called again for the view that generates that ul (ie, CheckitemsPendingTableView ). 的问题是, didInsertElement没有为生成该视图中再次调用该ul (即, CheckitemsPendingTableView )。 When the checklistController's content attribute changes, the child views dutifully adjust to reflect the currently-selected list of checkitems. 当checklistController的content属性更改时,子视图将忠实地调整以反映当前选择的清单列表。 However, the original binding to sortable() is lost, and there is no apparent hook for re-binding to sortable() via jQuery. 但是,与sortable()的原始绑定丢失了,并且没有明显的挂钩可以通过jQuery重新绑定到sortable()

I can't re-bind on the child view of CheckitemsPendingTableView , since that would repeat for every instance of a checkitem in the currently-selected list. 我无法在CheckitemsPendingTableView的子视图上重新绑定,因为该操作将对当前所选列表中每个checkitem实例重复。 I can't rebind from the controllers or models, since they will attempt to bind before the DOM update is completed. 我无法从控制器或模型重新绑定,因为它们将在DOM更新完成之前尝试进行绑定。

I'm sure I'm just thinking about this incorrectly. 我确定我只是在错误地考虑这个问题。 I'm new to Ember.js (if it isn't wildly obvious), and am struggling to understand how this case is properly handled. 我是Ember.js的新手(如果不是很明显),并且正在努力了解如何正确处理此案。

UPDATE 更新

I solved this problem, by adding the following function and observer to the App.CheckitemsPendingTableView : 通过将以下函数和观察器添加到App.CheckitemsPendingTableView ,我解决了此问题:

resetSortableTable: function() {
    $('.sortable').unbind('sortable');
    $('.sortable').sortable();
    console.log('Sort reset by CheckitemsPendingTableView');
  },

itemsChanged: function() {
    console.log('itemsChanged caught in CheckitemsPendingTableView');
    // flush the RunLoop so changes are written to DOM
    Ember.run.sync();
    Ember.run.next(this, function() {
        this.resetSortableTable();
    });
  }.observes('content.@each')

I based my solution on this answer . 我的解决方案基于此答案 I'm a little worried that it's not a great solution, since it seems to be making assumptions about the DOM completing during a run loop iteration. 我有点担心这不是一个很好的解决方案,因为它似乎在假设运行循环迭代期间DOM完成。

Whoa! 哇! Kind of detailed question... 有点详细的问题...

I think your issue comes from html5sortable plugin: it uses JQuery's bind method to attach handlers directly on living elements. 我认为您的问题来自html5sortable插件:它使用JQuery的bind方法将处理程序直接附加到活动元素上。 As those elements disappear/evolve under Ember control, the binding are lost. 当这些元素在Ember的控制下消失/进化时,绑定就消失了。

The plugin should better use JQuery's on method to bind handlers, as specified in the documentation . 插件应该更好地使用JQuery的on方法来绑定处理程序,如文档中所指定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM