2012-03-26 10 views
42

zu sortieren ich dies erfolgreich tun können:Richtige Art und Weise eine Backbone.js Sammlung on the fly

App.SomeCollection = Backbone.Collection.extend({ 
    comparator: function(collection){ 
    return(collection.get('lastName')); 
    } 
}); 

das ist schön, wenn ich eine Sammlung haben will, die nur von ‚Nachnamen‘ sortiert. Aber ich muss diese Sortierung dynamisch durchführen lassen. Manchmal muss ich nach "firstName" sortieren.

Meine völlige Ausfälle sind:

Ich habe versucht, eine zusätzliche Variable geben die Variable sort() auf angeben. Das hat nicht funktioniert. Ich habe auch versucht sortBy(), die auch nicht funktioniert. Ich habe versucht, meine eigene Funktion zu sortieren(), aber das hat auch nicht funktioniert. Übergeben einer benutzerdefinierten Funktion an sortBy(), nur um das Ergebnis nicht eine each Methode zu haben, den Punkt zu besiegen, eine neu sortierte Rückgratsammlung zu haben.

Kann jemand ein praktisches Beispiel für die Sortierung durch eine Variable liefern, die nicht fest in die Komparatorfunktion kodiert ist? Oder irgendeinen Hack, der funktioniert? Wenn nicht, ein funktionierender sortBy() Anruf?

Antwort

36

Interessante Frage. Ich würde hier eine Variante des Strategie-Patterns ausprobieren. Sie könnten einen Hash von Sortierfunktionen erstellen, setzen Sie dann comparator basierend auf dem ausgewählten Mitglied des hash:

App.SomeCollection = Backbone.Collection.extend({ 
    comparator: strategies[selectedStrategy], 
    strategies: { 
     firstName: function() { /* first name sorting implementation here */ }, 
     lastName: function() { /* last name sorting implementation here */ }, 
    }, 
    selectedStrategy: "firstName" 
}); 

Dann könnten Sie Ihre Sortierstrategie on the fly ändern, indem Sie den Wert der selectedStrategy Eigenschaft zu aktualisieren.

EDIT: Ich erkannte, nachdem ich zu Bett ging :), dass dies nicht ganz funktionieren würde, wie ich es oben schrieb, weil wir ein Objekt Literal an Collection.extend übergeben. Die Eigenschaft comparator wird einmal beim Erstellen des Objekts ausgewertet. Daher wird sie im laufenden Betrieb nicht geändert, es sei denn, sie wird dazu gezwungen. Es ist wahrscheinlich ein sauberer Weg, dies zu tun, aber dies zeigt, on the fly die Vergleichsschaltfunktionen:

var SomeCollection = Backbone.Collection.extend({ 
    comparator: function (property) { 
     return selectedStrategy.apply(myModel.get(property)); 
    }, 
    strategies: { 
     firstName: function (person) { return person.get("firstName"); }, 
     lastName: function (person) { return person.get("lastName"); }, 
    }, 
    changeSort: function (sortProperty) { 
     this.comparator = this.strategies[sortProperty]; 
    }, 
    initialize: function() { 
     this.changeSort("lastName"); 
     console.log(this.comparator); 
     this.changeSort("firstName"); 
     console.log(this.comparator);   
    }                       
}); 

var myCollection = new SomeCollection; 

Hier ein jsFiddle ist, das dies demonstriert.

Die Wurzel all Ihrer Probleme, glaube ich, ist, dass Eigenschaften von JavaScript-Objektliteralen sofort beim Erstellen des Objekts ausgewertet werden. Sie müssen die Eigenschaft also überschreiben, wenn Sie sie ändern möchten. Wenn Sie versuchen, in die Eigenschaft selbst zu wechseln, wird sie auf einen Anfangswert gesetzt und bleibt dort.

Hier ist eine gute blog post, die dies in einem etwas anderen Kontext diskutiert.

+0

Vielen Dank. Vorgeschlagene Implementierung und Varianten waren nicht erfolgreich, aber ich bin dankbar für die Antwort :). Ich glaube, ich vermisse hier etwas sehr Kleines. Ziemlich frustrierend. Ich lehne mich auf ein anderes Rahmenwerk für meine Javascript-Arbeit ab, wie viel Schwierigkeit ich mit einer einfachen Sortiervariante> habe. < – kikuchiyo

+0

Ich bin dabei, dieses Problem selbst zu lösen. Meine App zeigt eine Tabelle an. Der Benutzer sortiert den Tabelleninhalt, indem er auf die Spaltennamen im Tabellenkopf klickt. Ich überlege, den Tabellenkopf zu einer Region mit einem eigenen "Sortier" -Modell zu machen. Das Modell wird die aktuelle "sortCriteria" verfolgen. Durch Klicken auf einen Spaltennamen wird sortCriteria aktualisiert. Ich werde eine Komparatorfunktion in der Sammlung haben. Es wird einfach die sortCriteria aus dem Sortiermodell erhalten. –

+0

Die JSFiddles müssen aktualisiert werden, um einen Underscore und einen Backbone zu verwenden, die auf dem Server vorhanden sind. Die bei cdnjs.com sind gut. JSFiddle benutzt ihre eigenen, aber ich denke nicht mehr. Meine erste Einschätzung war ein wenig hart, aber ich denke, 'myModel' sollte durch den Parameter ersetzt werden, der an den Vergleicher übergeben wird, der jedes Modell in der Sammlung sein sollte. –

12

Also, das war meine Lösung, die tatsächlich funktioniert hat.

App.Collection = Backbone.Collection.extend({ 

    model:App.Model, 

    initialize: function(){ 
     this.sortVar = 'firstName'; 
    }, 

    comparator: function(collection){ 
     var that = this; 
     return(collection.get(that.sortVar)); 
    } 

    }); 

Dann in der Ansicht, ich habe es wie folgt MICKEY MOUSE:

this.collections.sortVar = 'lastVar' 

this.collections.sort(this.comparator).each(function(){ 
    // All the stuff I want to do with the sorted collection... 
}); 

Da Josh Earl der einzige war, selbst eine Lösung zu versuchen, und er hat mich in die richtige Richtung führen, Ich akzeptiere seine Antwort. Danke Josh :)

+1

Danke für die Antwort. :) Ich habe meinen ursprünglichen Beitrag aktualisiert - sehen Sie, ob das für Sie funktioniert. –

+0

Wenn Sie die Übung in [Udacitys JavaScript Design Patterns] (https://www.udacity.com/course/viewer#!/c-ud989/l-3525509902/m-3574768574) ausführen, müssen Sie sich dynamisch ändern Beachten Sie, dass Sie die 'addAll'-Methode' app.AppView' aufrufen müssen, um die Liste der neu sortierten Elemente erneut hinzuzufügen. Überprüfen Sie nicht nur, ob eine Ansicht erneut gerendert wird, sondern auch, wie die Liste gefüllt wird. [Hier ist ein Beispiel] (https://github.com/enderandpeter/ud989-todo-app/blob/js-design-patterns/js/views/app-view.js#L119) –

16

Wechsel zur Komparatorfunktion durch Zuweisung einer neuen Funktion und Aufruf von sort.

// Following example above do in the view: 

// Assign new comparator 
this.collection.comparator = function(model) { 
    return model.get('lastname'); 
} 

// Resort collection 
this.collection.sort(); 

// Sort differently 
this.collection.comparator = function(model) { 
    return model.get('age'); 
} 
this.collection.sort(); 
+0

Das ist, was ich immer habe auch fertig. Ich verwende ein Mixin für die Sammlung, so dass es in allen Sammlungen verfügbar ist.Ich trigger auch ein Reset auf die Sammlung, da ich normalerweise Ansichten habe, die gebunden sind, die auch aktualisiert werden müssen. – jhorback

+0

Große einfache Antwort. Vielen Dank! – Trip

+0

Ich vermisse etwas, aber das scheint die beste Lösung zu sein. –

4

Dies ist eine alte Frage, aber ich hatte vor kurzem ein ähnliches Bedürfnis (Art eine Sammlung anhand von Kriterien, die von einem Benutzer Click-Ereignisse geliefert werden) und dachte, ich würde meine Lösung für die andere teile dieses Problem anzugehen. Erfordert kein hardcoded model.get ('Attribut').

ich Dave Newton im Grunde approach fast zu einem nativen JavaScript-Arrays verwendet, und maßgeschneiderte es Backbone:

MyCollection = Backbone.Collection.extend({ 

    // Custom sorting function. 
    sortCollection : function(criteria) { 

     // Set your comparator function, pass the criteria. 
     this.comparator = this.criteriaComparator(criteria); 
     this.sort(); 
    }, 

    criteriaComparator : function(criteria, overloadParam) { 

     return function(a, b) { 
      var aSortVal = a.get(criteria); 
      var bSortVal = b.get(criteria); 

      // Whatever your sorting criteria. 
      if (aSortVal < bSortVal) { 
       return -1; 
      } 

      if (aSortVal > bSortVal) { 
       return 1; 
      } 

      else { 
       return 0; 
      } 

     }; 
    } 

}); 

Beachten Sie die "overloadParam". Pro documentation, Backbone verwendet Underscore "sortBy", wenn Ihre Vergleichsfunktion einen einzelnen Parameter und eine native JS-Stil Sortierung, wenn es zwei Parameter hat. Wir brauchen Letzteres, daher das "overloadParam".

0

Dies ist, was ich getan habe für die App, an der ich gerade arbeite. In meiner Sammlung habe ich:

comparator: function(model) { 
    var methodName = applicationStateModel.get("comparatorMethod"), 
     method = this[methodName]; 
    if (typeof(method === "function")) { 
     return method.call(null, model); 
    } 
} 

Jetzt kann ich einige verschiedene Methoden zu meiner Sammlung hinzufügen: fooSort(), barSort() und bazSort().
Ich mag fooSort der Standard sein, so dass ich wie in meinem Zustand Modell so eingestellt, dass:

var ApplicationState = Backbone.Model.extend({ 
    defaults: {    
     comparatorMethod: "fooSort" 
    } 
}); 

Alles was ich jetzt tun muß, ist eine Funktion in meiner Sicht schreiben, die den Wert von „comparatorMethod“ aktualisiert in Abhängigkeit von worauf der Benutzer klickt Ich setze die Sammlung, um diese Änderungen zu hören, und sortiere(), und ich setze die Ansicht, um nach Sortierereignissen zu horchen und render().

BAZINGA !!!!

3

Mit Blick auf die source code, es scheint, es gibt eine einfache Möglichkeit, es zu tun, Komparator auf String statt Funktion. Dies funktioniert, gegeben Backbone.Collection mycollection:

 mycollection.comparator = key; 
     mycollection.sort(); 
+0

Aber es funktioniert nicht, wenn wir null Werte in der Sammlung haben. – Jyothu