2012-03-26 20 views
42

posso fare con successo questo:modo corretto di ordinare una collezione backbone.js al volo

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

che è bello se voglio avere una collezione che viene organizzato solo per 'lastName'. Ma ho bisogno di fare questo ordinamento fatto dinamicamente. A volte, ho bisogno di ordinare, per esempio, 'firstName' invece.

miei fallimenti pronunciano includono:

ho provato passando una variabile in più specificando la variabile a sort() su. Non ha funzionato. Ho anche provato sortBy(), che non ha funzionato neanche. Ho provato a passare la mia funzione per ordinare(), ma questo non ha funzionato neanche. Passando una funzione definita dall'utente a sortBy() solo per ottenere che il risultato non disponga di un metodo each, annullando il punto di avere una raccolta backbone appena ordinata.

Qualcuno può fornire un esempio pratico di ordinamento mediante una variabile che non è codificata in modo fisso nella funzione di confronto? O qualsiasi hack che hai funziona? In caso contrario, una chiamata sortBy() funzionante?

risposta

36

Interessante domanda. Vorrei provare una variante del modello di strategia qui. Si potrebbe creare un hash di funzioni di ordinamento, quindi impostare comparator basato sul membro selezionato del 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" 
}); 

allora si potrebbe cambiare la vostra strategia di ordinamento al volo aggiornando il valore della proprietà selectedStrategy.

EDIT: mi sono reso conto dopo che sono andato a letto :) che questo non avrebbe funzionato come ho scritto sopra, perché stiamo passando un oggetto letterale a Collection.extend. La proprietà comparator verrà valutata una volta, quando l'oggetto verrà creato, quindi non cambierà al volo a meno che non sia obbligato a farlo. C'è probabilmente un modo più pulito per fare questo, ma questo dimostra il passaggio delle funzioni di confronto al volo:

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; 

Ecco un jsFiddle che illustra questo.

La radice di tutti i problemi, penso, è che le proprietà sui valori letterali degli oggetti JavaScript vengono valutate immediatamente quando viene creato l'oggetto, quindi è necessario sovrascrivere la proprietà se si desidera modificarla. Se provi a scrivere un qualche tipo di passaggio nella proprietà stessa, verrà impostato su un valore iniziale e resterà lì.

Ecco un buon blog post che discute questo in un contesto leggermente diverso.

+0

Grazie. Implementazione e varianti suggerite non hanno avuto successo, ma sono grato per la risposta :). Penso che mi manchi qualcosa di veramente piccolo qui. Abbastanza frustrante. Mi sto appoggiando ad un altro framework per il mio lavoro javascript considerando quante difficoltà sto avendo con una semplice variante di ordinamento>. < – kikuchiyo

+0

Sto per provare a risolvere questo problema da solo. La mia app mostra una tabella. L'utente ordina i contenuti della tabella facendo clic sui nomi delle colonne nella testa della tabella. Sto considerando di rendere la testa di una regione con il proprio modello di "ordinamento". Il modello seguirà l'attuale "sortCriteria". Facendo clic sul nome di una colonna si aggiornerà sortCriteria. Avrò una funzione di confronto nella collezione. Otterrà semplicemente sortCriteria dal modello di ordinamento. –

+0

I JSFiddles devono essere aggiornati per utilizzare un Underscore e Backbone presenti sul server. Quelli su cdnjs.com sono buoni. JSFiddle usa per fornire il proprio, ma non credo più. La mia valutazione iniziale è stata un po 'dura, ma penso che 'myModel' debba essere sostituito con il parametro passato al comparatore, che dovrebbe essere ogni modello nella collezione. –

12

Quindi, questa era la mia soluzione che funzionava davvero.

App.Collection = Backbone.Collection.extend({ 

    model:App.Model, 

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

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

    }); 

Poi nella vista, devo MICKEY MOUSE in questo modo:

this.collections.sortVar = 'lastVar' 

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

Dal Josh Earl è stato l'unico a tentare ancora una soluzione e mi ha portato nella giusta direzione, Accetto la sua risposta. Grazie Josh :)

+1

Grazie per la risposta. :) Ho aggiornato il mio post originale - vedi se questo funziona per te. –

+0

Se ti capita di fare l'esercizio in [Modelli di progettazione JavaScript di Udacity] (https://www.udacity.com/course/viewer#!/c-ud989/l-3525509902/m-3574768574) dove devi cambiare dinamicamente nell'ordine di ordinamento dell'esempio ToDo, si noti che è necessario chiamare il metodo 'addAll' di app.AppView' per aggiungere nuovamente l'elenco di nuovi elementi ordinati. Oltre a re-rendering di una vista, controlla come viene compilato l'elenco. [Ecco un esempio] (https://github.com/enderandpeter/ud989-todo-app/blob/js-design-patterns/js/views/app-view.js#L119) –

16

Passare alla funzione comparatore assegnando una nuova funzione e ordinando una chiamata.

// 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

Questo è quello che ho sempre fatto anche Uso un mixin sulla collezione in modo che sia disponibile su tutte le raccolte.Attivo anche un reset sulla raccolta poiché di solito ho delle viste che sono obbligate ad aggiornare. – jhorback

+0

Ottima risposta semplice. Grazie! – Trip

+0

Potrei mancare qualcosa, ma questa sembra la soluzione migliore. –

4

Questa è una vecchia questione, ma di recente ho avuto un bisogno simile (in ordine una collezione sulla base di criteri che devono essere fornite da un clic evento utente) e ho pensato di condividere la mia soluzione per gli altri che affrontano questo problema. Non richiede model.get ("attributo") codificato.

ho praticamente usato Dave Newton approach di estendere gli array nativo JavaScript, e su misura a 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; 
      } 

     }; 
    } 

}); 

Annotare il "overloadParam". Per lo documentation, Backbone utilizza "sortBy" di Underscore se la funzione di comparatore ha un parametro singolo e un ordinamento in stile JS nativo se ha due parametri. Abbiamo bisogno di quest'ultimo, da qui il "overloadParam".

0

Questo è quello che ho fatto per l'app su cui sto lavorando attualmente. Nella mia collezione ho:

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

Ora posso aggiungere alcuni metodi diversi per la mia collezione: fooSort(), barSort(), e bazSort().
Voglio fooSort sia quella predefinita così mi sono messo che nel mio modello di stato in questo modo:

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

Ora tutto quello che devo fare è scrivere una funzione a mio avviso, che aggiorna il valore di "comparatorMethod" seconda ciò che l'utente fa clic. Ho impostato la collezione per ascoltare le modifiche e fare sort(), e ho impostato la vista per ascoltare gli eventi sort e render().

BAZINGA !!!!

3

Guardando allo source code, sembra che ci sia un modo semplice per farlo, impostando il comparatore su stringa invece che funzione. Funziona, dato Backbone.Collection mycollection:

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

Ma, non funziona se nella raccolta sono presenti valori nulli. – Jyothu

Problemi correlati