2012-03-06 13 views
6

C'è un modo semplice per inserire un nuovo elemento del modello nel mezzo di un backbone.js Collection e quindi aggiornare la collezione View per includere il nuovo elemento nella posizione corretta?Inserimento di un elemento in una raccolta backbone.js

Sto lavorando a un controllo per aggiungere/eliminare elementi da un elenco. Ogni elemento dell'elenco ha il proprio Model e View e io ho uno View per l'intera collezione.

Ogni vista articolo ha un pulsante Duplicate che clona il modello dell'articolo e lo inserisce nella raccolta nella posizione dell'indice sotto l'elemento su cui è stato fatto clic.

L'inserimento dell'elemento nella raccolta è stato semplice, ma non riesco a capire come aggiornare la vista di raccolta. Ho provato qualcosa di simile:

ListView = Backbone.View.extend({ 
    el: '#list-rows', 
    initialize: function() { 
     _.bindAll(this); 
     this.collection = new Items(); 
     this.collection.bind('add', this.addItem); 
     this.render(); 
    }, 
    render: function() { 
     this.collection.each(this.addItems); 
     return this; 
    }, 
    addItem: function (item) { 
     var itemView = new ItemView({ model: item }), 
      rendered = itemView.render().el, 
      index = this.collection.indexOf(item), 
      rows = $('.item-row'); 

     if (rows.length > 1) { 
     $(rows[index - 1]).after(rendered); 
     } else { 
     this.$el.append(rendered); 
     } 
    } 
} 

Questa implementazione è una sorta di opera, ma sto ottenendo strani insetti quando aggiungo un nuovo elemento. Sono sicuro di poterli ordinare, ma ...

C'è una voce nella mia testa che continua a dirmi che c'è un modo migliore per farlo. Dover determinare manualmente dove inserire un nuovo ItemView sembra davvero hacky - la vista della raccolta non dovrebbe sapere come eseguire il rerender della raccolta?

Qualche suggerimento?

risposta

4

Il solito modo che sto facendo è lasciare il rendering ListView ogni ItemView nella sua funzione render. Poi ho solo legano l'evento add alla funzione render, in questo modo:

ListView = Backbone.View.extend({ 
    el: '#list-rows' 
    , initialize: function() { 
    _.bindAll(this); 
    this.collection = new Items(); 
    this.collection.bind('add', this.render); 
    this.render(); 
    } 
    , render: function() { 
    this.$el.empty(); 
    var self = this; 
    this.collection.each(function(item) { 
     self.$el.append(new ItemView({ model: item }).render().el); 
    }); 
    return this; 
    } 
} 

Ogni volta che chiamate this.collection.add(someModel, {at: index}), la vista verrà nuovamente reso di conseguenza.

+0

Ho dovuto aggiungere una chiamata a '(.item-row) .remove()' nel metodo 'render' per farlo funzionare, ma è una buona soluzione. (Altrimenti gli oggetti rerender sono stati aggiunti alla fine degli oggetti renderizzati esistenti.) Grazie per l'aiuto! –

+0

Oh, puoi semplicemente fare 'this. $ El.empty()' per cancellare '$ el' prima di passare alla raccolta :) – sntran

+0

Questo è esattamente il modo in cui sto aggiungendo elementi alla raccolta al momento. Sta mettendo il nuovo oggetto nel punto giusto della collezione.Ho notato che ho un paio di refusi nel mio commento sopra: sto chiamando '$ ('. Item-row'). Remove()' per cancellare gli elementi che sono stati creati l'ultima volta che la raccolta è stata renderizzata. Sta semplicemente ripristinando la vista. Vieni a pensarci, c'è un metodo 'reset' che potrebbe fare ciò che voglio. Proverò a chiamarlo e poi a chiamare 'render'. –

9

Non penso che il re-rendering dell'intera collezione quando si aggiunge un nuovo elemento sia la soluzione migliore. È più lento che inserire il nuovo oggetto nel posto giusto, specialmente se l'elenco è lungo.

Inoltre, considerare il seguente scenario. Carichi alcuni articoli nelle raccolte e quindi aggiungi altri n elementi (ad esempio, l'utente fa clic su un pulsante "carica altro"). Per fare ciò, chiamare il metodo fetch() che passa a add: true come una delle opzioni. Quando i dati vengono ricevuti dal server, l'evento "aggiungi" viene generato n volte e si finisce per ripetere il rendering dell'elenco n volte.

realtà sto usando una variante del codice nella tua domanda, ecco la mia callback per l'evento 'aggiungere':

var view, prev, prev_index; 
view = new ItemView({ model: new_item }).render().el; 
prev_index = self.model.indexOf(new_item) - 1; 
prev = self.$el.find('li:eq(' + prev_index + ')'); 
if (prev.length > 0) { 
    prev.after(view); 
} else { 
    self.$el.prepend(view); 
} 

Quindi, in sostanza, sto solo usando il selettore :eq() jQuery invece di ottenere tutti gli elementi come te, dovrebbero usare meno memoria.

+0

Funziona alla grande, ma visualizzo i numeri accanto a ciascun elemento nell'elenco, quindi devo eseguire nuovamente il rendering per mantenere i numeri sincronizzati. Non riesco a vedere in alcun modo intorno a ciò. Bummer .... Mi piace la semplicità qui. – byron

Problemi correlati