Ich habe eine Ember.js App, die ein Checklist
Modell hat, wo jede Checkliste viele Checkitems
hat. (Eine Variante der klassischen ToDo-App, aber mit mehreren TodoLists.)Was ist der richtige Ansatz zum Aktualisieren von jQuery-Bindungen in einer Ember.js-Ansicht?
In der obersten Ansicht sieht der Benutzer eine Liste aller verfügbaren Checklisten auf der linken Seite. Wenn eine Checkliste ausgewählt ist, erscheinen die entsprechenden Checkitems auf der rechten Seite.
Die Checkitems auf der rechten Seite sind durch Ziehen sortierbar. Ich verwende this html5sortable library, um die Drag-Sortierung zu handhaben. Es ist wie die klassische jQueryUI-Version, aber weniger klobig.
Beim ersten Laden der App funktioniert die sortierbare Liste einwandfrei. Ändert sich jedoch die Liste der Checkitems (entweder weil ein Checkitem als abgeschlossen markiert ist, ein neues Checkitem hinzugefügt wird, Änderungen an einem vorhandenen Check Item gespeichert werden oder eine andere Checkliste auf der linken Seite ausgewählt wird), geht die Bindung an html5sortable verloren.
Wenn die App zum ersten Mal geladen, habe ich eine Ansicht App.CheckitemsPendingTableView
genannt:
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.');
}
});
Die entsprechende Vorlage checkitemsPendingTable.handlebars
genannt wird, und es sieht wie folgt aus:
{{#each content}}
{{view App.CheckitemSingleView checkitemBinding="this"}}
{{/each}}
Und für eine gute Maßnahme, die Steuerung das das content
Attribut für diese Ansicht eingibt, ist 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...
});
Der Inhalt Attribut der checkitemsController
wird durch die checklistsController
angetrieben:
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')
});
(Sie haben vielleicht bemerkt, dass dieser Controller seine Daten aus einem Rails-Backend über glut-Daten zieht. Dies sollte jedoch für das aktuelle Problem keine Rolle spielen.)
Die Ansicht für das Menü auf der linken Seite lautet checklistsView
. Es hat ein Kind Blick checklistSingleView
genannt, die für jede der Checklisten wiedergegeben wird:
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...
});
Und schließlich die entsprechende Vorlage checklistSingle.handlebars
enthält einen Link, der auf die chooseList
über eine Aktion gebunden ist:
<a href="#" {{action "chooseList"}}>{{checklist.name}}</a>
So funktioniert alles oben brillant ... bis der Benutzer eine Änderung an der ul
Checkitems auf der rechten Seite verursacht. An diesem Punkt ist die Bindung an html5sortable
verloren und ich kann keinen geeigneten Ort finden, um sie zu aktualisieren.
Das Problem ist, dass didInsertElement
nicht erneut für die Ansicht aufgerufen wird, die ul
generiert (d. H. CheckitemsPendingTableView
). Wenn sich das Attribut content
des checklistControllers ändert, passen sich die untergeordneten Ansichten pflichtgemäß an die aktuell ausgewählte Liste der checkitems an. Die ursprüngliche Bindung an sortable()
ist jedoch verloren und es gibt keinen offensichtlichen Haken für das erneute Binden an sortable()
über jQuery.
Ich kann nicht auf der untergeordneten Sicht von CheckitemsPendingTableView
erneut binden, da das für jede Instanz eines checkitem in der aktuell ausgewählten Liste wiederholt werden würde.Ich kann nicht von den Controllern oder Modellen neu binden, da sie versuchen werden, zu binden, bevor das DOM-Update abgeschlossen ist.
Ich bin mir sicher, dass ich nur falsch darüber nachdenke. Ich bin neu bei Ember.js (wenn es nicht so offensichtlich ist), und ich habe Mühe zu verstehen, wie dieser Fall richtig gehandhabt wird.
UPDATE
löste ich dieses Problem, indem Sie die folgende Funktion und Beobachter zur App.CheckitemsPendingTableView
Zugabe:
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]')
ich auf this answer meine Lösung. Ich bin ein wenig besorgt, dass es keine großartige Lösung ist, da es anscheinend Annahmen über das DOM-Vervollständigen während einer Lauf-Schleifen-Iteration macht.
Danke fürs Lesen, Mike! Ich habe überlegt mit 'on' zu gehen, habe aber inzwischen einen Workaround gefunden (siehe Update zu meiner ursprünglichen Frage). – sirvine
Gern geschehen. In der Tat, der Workaround, den Sie gefunden haben, klingt wirklich beschissen ... :-P –