2012-04-03 8 views
2

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.

Antwort

0

Whoa! Art der detaillierten Frage ...

Ich denke, Ihr Problem kommt von html5sortable Plugin: Es verwendet JQuery bind Methode, um Handler direkt auf lebenden Elemente anhängen. Wenn diese Elemente unter Ember-Kontrolle verschwinden/sich entwickeln, gehen die Bindungen verloren.

Das Plugin sollte JQuerys on-Methode verwenden, um Handler zu binden, wie angegeben in the documentation.

+0

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

+0

Gern geschehen. In der Tat, der Workaround, den Sie gefunden haben, klingt wirklich beschissen ... :-P –

Verwandte Themen