私はChecklist
モデルを持つEmber.jsアプリを持っています。各チェックリストには多くのCheckitems
があります。 (古典的なToDoアプリの亜種ですが、複数のTodoListsがあります)Ember.jsビューでjQueryバインディングをリフレッシュする適切な方法は何ですか?
一番上のビューでは、利用可能なすべてのチェックリストのリストが左側に表示されます。チェックリストを選択すると、対応するチェック項目が右側に表示されます。
右側のチェック項目はドラッグ可能です。私はドラッグのソートを処理するためにthis html5sortable libraryを使用しています。これは、古典的なjQueryUIのバージョンのようですが、あまりclunky。
アプリの最初の読み込み時に、ソート可能なリストは正常に機能します。ただし、チェック項目の一覧が変更された場合(チェック項目が完了とマークされているか、新しいチェック項目が追加されているか、既存のチェック項目の変更が保存されているか、左側に別のチェックリストが選択されているため)、html5sortableへのバインディングは失われます。
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.');
}
});
対応するテンプレートがcheckitemsPendingTable.handlebars
と呼ばれ、それはこのようになりますされています:
{{#each content}}
{{view App.CheckitemSingleView checkitemBinding="this"}}
{{/each}}
そして、良い対策のため、コントローラのアプリが最初にロード、私はApp.CheckitemsPendingTableView
ビューと呼ばれている
そのビューの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('[email protected]'),
...snip...
});
checkitemsController
のコンテンツ属性は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')
});
(あなたはこのコントローラは、エンバー、データを介してRailsのバックエンドからデータを引っ張ることに気づいたかもしれません。これは現在の問題ではありません)。
左側のメニューのビューはchecklistsView
と呼ばれています。これは、チェックリストごとにレンダリングされる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...
});
をそして、最終的には、対応するテンプレートchecklistSingle.handlebars
は、アクションの方法によりchooseList
に結ばれたリンクが含まれています
<a href="#" {{action "chooseList"}}>{{checklist.name}}</a>
したがって、上記のすべてがうまく機能します...ユーザーが右側のチェック項目のul
に変更を加えるまで。その時点で、html5sortable
へのバインディングは失われ、私はそれをリフレッシュするための便利な場所を見つけることができません。
問題は、didInsertElement
が、ul
(つまり、CheckitemsPendingTableView
)を生成するビューに対して再度呼び出されないということです。 checklistControllerのcontent
属性が変更されると、子ビューは現在選択されているチェック項目のリストを反映するように忠実に調整されます。しかし、sortable()
への元のバインディングは失われており、jQuery経由でsortable()
に再バインドするための明確なフックはありません。
CheckitemsPendingTableView
という子ビューで再バインドすることはできません。これは、現在選択されているリストのチェック項目のすべてのインスタンスで繰り返されるためです。コントローラやモデルからは再バインドできません。DOMの更新が完了する前にバインドを試みるためです。
私はこれについて間違って考えていると確信しています。私はEmber.jsを新しくしました(それがはっきりとわからない場合)。そして、このケースがどのように正しく処理されているかを理解するのに苦労しています。
UPDATE私はApp.CheckitemsPendingTableView
に次の関数と観測者を追加することによって、この問題を解決:私はthis answerに私の解決策をベース
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('[email protected]')
。実行ループの反復中にDOMが完了することを前提にしているように思われるので、それは大きな解決策ではないことを少しは心配しています。
マイクをお読みいただきありがとうございます。私は 'on'で行くと考えましたが、その間に回避策を見つけました(私の元の質問への更新を参照)。 – sirvine
ようこそ。確かに、あなたが見つけた回避策は本当に泥酔しています... :-P –