2012-04-17 12 views
15

Ho visto alcuni modi diversi per ottenere il modello successivo o precedente da una raccolta, ma mi chiedevo se qualcuno potesse offrire qualche consiglio sul modo in cui ho deciso di implementarlo. La mia collezione è ordinata, ma l'id che sto ordinando non è garantito per essere sequenziale. È garantito che sia unico. Supponiamo che gli id ​​più piccoli siano voci "più vecchie" della collezione e gli ID più grandi siano "più recenti".ottenere il modello successivo e precedente da una collezione

MyCollection = Backbone.Collection.extend({ 
    model: MyModel, 
    initialize:function(){ 
    this.getElement = this._getElement(0); 
    }, 
    comparator: function(model) { 
    return model.get("id"); 
    }, 
    _getElement: function (index){ 
    var self = this; 
    return function (what){ 
    if (what === "next"){ 
     if (index+1 >= self.length) return null; 
     return self.at(++index); 
    } 
    if (what === "prev"){ 
     if (index-1 < 0) return null; 
     return self.at(--index); 
    } 
    // what doesn't equal anything useful 
    return null; 
    }; 
    } 
}); 

Quando si utilizza getElement, faccio cose come getElement ("next") e getElement ("prev") per chiedere per il modello successivo o precedente nella mia collezione. Ciò che viene restituito da getElement è il modello attuale, non l'indice. Conosco collection.indexOf, ma volevo un modo per scorrere una collezione senza prima avere un modello da cui partire. Questa implementazione è più difficile di quanto dovrebbe essere?

risposta

23

Farei qualcosa del genere. Tieni presente che attualmente non è in corso la gestione degli errori, quindi se attualmente ti trovi nel primo modello della raccolta e provi a ottenere il precedente, probabilmente riceverai un errore.

MyCollection = Backbone.Collection.extend({ 
    model: MyModel, 
    initialize:function(){ 
    this.bindAll(this); 
    this.setElement(this.at(0)); 
    }, 
    comparator: function(model) { 
    return model.get("id"); 
    }, 
    getElement: function() { 
    return this.currentElement; 
    }, 
    setElement: function(model) { 
    this.currentElement = model; 
    }, 
    next: function(){ 
    this.setElement(this.at(this.indexOf(this.getElement()) + 1)); 
    return this; 
    }, 
    prev: function() { 
    this.setElement(this.at(this.indexOf(this.getElement()) - 1)); 
    return this; 
    } 
}); 

per avanzare al prossimo modello collection.next(). Per passare al modello successivo e restituirlo var m = collection.next().getElement();

Per spiegare un po 'meglio come funziona next/prev.

// The current model 
this.getElement(); 
// Index of the current model in the collection 
this.indexOf(this.getElement()) 
// Get the model either one before or one after where the current model is in the collection 
this.at(this.indexOf(this.getElement()) + 1) 
// Set the new model as the current model 
this.setElement(this.at(this.indexOf(this.getElement()) + 1)); 
+0

mi piacciono alcune delle tue idee, in particolare utilizzando this.at() nel metodo di inizializzazione. Tuttavia, il tuo codice è piuttosto lungo. Se nessun altro suggerisce qualcosa di meglio e/o più conciso, otterrai il controllo verde :-) Il mio metodo è più corto e impedisce di cadere dalla fine della raccolta restituendo null quando arrivi a una delle due estremità. –

+0

È possibile accorciarlo abbandonando le funzioni 'set/getElement' e semplicemente accedendo direttamente al valore' currentElement'. – abraham

+4

Se si utilizza v0.9 + è necessario avere this.setElement (this.at (0)); che è attualmente nella funzione di inizializzazione chiamata dopo aver creato la raccolta. Questo perché inizializza viene chiamato prima che la Collezione crei i modelli. ad es .: var myCollection = new MyCollection (data); myCollection.setElement (this.at (0)); –

6

Ho fatto questo leggermente in modo diverso in quanto sto aggiungendo i metodi al modello piuttosto che alla raccolta. In questo modo, posso prendere qualsiasi modello e ottenere quello successivo nella sequenza.

next: function() { 
    if (this.collection) { 
     return this.collection.at(this.collection.indexOf(this) + 1); 
    } 
}, 
prev: function() { 
    if (this.collection) { 
     return this.collection.at(this.collection.indexOf(this) - 1); 
    } 
}, 
+0

Mentre mi piace l'approccio, ci sono alcuni problemi con questo in pratica. 1. È necessario aggiungere una proprietà "raccolta" personalizzata al modello che fa riferimento alla raccolta specifica con cui si desidera lavorare. Ciò presuppone che il tuo modello sia solo un membro di una singola raccolta, il che potrebbe non essere il caso. L'architettura di Backbone mantiene il modello "al buio", per così dire, sulle collezioni e le viste che lo guardano per garantire accoppiamenti e flessibilità. 2. questi metodi generano errori se this.collection.indexOf (this) <= 0 || this.collection.indexOf (this)> = collection.size(). – 1nfiniti

+0

@mikeyUX Sono d'accordo che solo essere in grado di appartenere a una singola collezione può essere restrittivo, cioè il modo Backbone. Personalmente penso che ci dovrebbe essere qualche implementazione di List in Backbone che viene usata semplicemente per le "collezioni" di Model ma senza tutte le cose sul lato server, così puoi fare cose come più raccolte più facilmente. Inoltre, è possibile scambiarlo facilmente in modo che sia un metodo sulla raccolta che accetta un modello come argomento: '' 'collection.next (model);' '' Infine, ciò non causa errori nel scenari che hai suggerito - ritorna indefinito, che è previsto! – Tom

2

Urtando questa vecchia discussione con una soluzione un po 'più generico:

elementi da aggiungere al Collection.prototype

current: null, 

initialize: function(){ 
    this.setCurrent(0); 
    // whatever else you want to do here... 
}, 

setCurrent: function(index){ 
    // ensure the requested index exists 
    if (index > -1 && index < this.size()) 
     this.current = this.at(index); 
    else 
     // handle error... 
}, 

// unnecessary, but if you want sugar... 
prev: function() { 
    this.setCurrent(this.at(this.current) -1); 
}, 
next: function() { 
    this.setCurrent(this.at(this.current) +1); 
} 

è possibile utilizzare i metodi di zucchero per ottenere la prev/prossimo modello così ...

collection.prev(); 
collection.next(); 
0

My Backbo ne classe SelectableCollection:

Backbone.Collection.extend({ 
    selectNext: function() { 
     if(this.cursor < this.length - 1) { 
      this.cursor++; 
      this.selected = this.at(this.cursor); 
      this.trigger('selected', this.selected); 
     } 
    }, 

    selectPrevious: function() { 
     if(this.cursor > 0) { 
      this.cursor--; 
      this.selected = this.at(this.cursor); 
      this.trigger('selected', this.selected); 
     } 
    }, 
    selectById: function (id) { 
     this.selected = this.get(id); 
     this.cursor = this.indexOf(this.selected); 
     this.trigger('selected', this.selected); 
    }, 
    unselect: function() { 
     this.cursor = null; 
     this.selected = null; 
     this.trigger('selected', null); 
    } 
}); 
Problemi correlati