2012-09-05 12 views
10

Ho una collezione Backbone con un carico di modelli.Salva diversi modelli Backbone in una volta

Ogni volta che un attributo specifico viene impostato su un modello e viene salvato, vengono caricati un carico di calcoli e i responsabili dell'interfaccia utente.

Ma, voglio essere in grado di impostare attributi su più modelli contemporaneamente e solo fare il salvataggio e il rerendering una volta che sono tutti impostati. Ovviamente non voglio fare più richieste http per un'operazione e sicuramente non voglio dover ripetere l'interfaccia dieci volte.

Speravo di trovare un metodo di salvataggio su Backbone.Collection che avrebbe risolto quali modelli haChanged(), li ha colpiti insieme come json e inviati al back-end. Il rerendering potrebbe quindi essere attivato da un evento sulla raccolta. Non molta fortuna.

Questo sembra un requisito abbastanza comune, quindi mi chiedo perché Backbone non implementa. Questo va contro un'architettura RESTful, per salvare diverse cose in un singolo endpoint? Se è così, allora? In nessun modo è pratico fare 1000 richieste per mantenere 1000 piccoli oggetti.

Quindi, è l'unica soluzione per aumentare Backbone.Collection con il mio metodo di salvataggio che itera su tutti i suoi modelli e crea il json per tutti quelli che sono cambiati e li invia al back-end? o qualcuno ha una soluzione migliore (o mi manca qualcosa!)?

+3

Correlato: http://stackoverflow.com/questions/11819662/why-does-backbone-not-have-a-save-put-post-method-for-its-collections-is-i/11819965#11819965 | http://stackoverflow.com/questions/11042292/extend-backbone-sync-to-batch-sync | http://stackoverflow.com/questions/511281/patterns-for-handling-batch-operations-in-rest-web-services | http://stackoverflow.com/questions/8529919/how-to-update-a-set-of-records-using-backbone-js-rails – fguillen

risposta

4

Ho finito per aumentare Backbone.Collection con un paio di metodi per gestire questo.

saveChangeMethod crea un modello fittizio da passare a Backbone.sync. Il metodo di sincronizzazione di tutti i backbone di un modello è la sua proprietà url e il metodo toJSON, quindi possiamo facilmente metterlo in discussione.

Internamente, il metodo toJSON di un modello restituisce solo una copia dei suoi attributi (da inviare al server), quindi possiamo tranquillamente usare solo un metodo toJSON che restituisce semplicemente l'array di modelli. Backbone.sync lo stringe, il che ci dà solo i dati degli attributi.

In caso di successo, saveChanged spegne gli eventi sulla raccolta da gestire una volta. Hanno buttato un po 'di codice che lo fa sparare eventi specifici una volta per ciascuno degli attributi che sono cambiati in uno dei modelli del lotto.

Backbone.Collection.prototype.saveChanged = function() { 
    var me = this, 
     changed = me.getChanged(), 
     dummy = { 
      url: this.url, 
      toJSON: function() { 
       return changed.models; 
      } 
     }, 
     options = { 
      success: function (model, resp, xhr) { 
       for (var i = 0; i < changed.models.length; i++) { 
        changed.models[i].chnageSilently(); 
       } 
       for (var attr in changed.attributes) { 
        me.trigger("batchchange:" + attr); 
       } 
       me.trigger("batchsync", changed); 
      } 
     }; 
    return Backbone.sync("update", dummy, options); 
} 

Abbiamo poi solo bisogno il metodo getChanged() su una collezione. Ciò restituisce un oggetto con 2 proprietà, una matrice dei modelli modificati e un contenenti contrassegni oggetto attributi sono cambiati:

Backbone.Collection.prototype.getChanged = function() { 
    var models = [], 
     changedAttributes = {}; 
    for (var i = 0; i < this.models.length; i++) { 
     if (this.models[i].hasChanged()) { 
      _.extend(changedAttributes, this.models[i].changedAttributes()); 
      models.push(this.models[i]); 
     } 
    } 
    return models.length ? {models: models, attributes: changedAttributes} : null; 
} 

Anche se questo è piccolo abuso di destinazione del 'mutato modello' dorsali paradigma, il punto del batching è che non vogliamo che accada qualcosa (cioè eventi da sparare) quando un modello viene cambiato.

Dobbiamo quindi passare {silent: true} al metodo set() del modello, quindi è logico utilizzare hasChanged() del backbone per contrassegnare i modelli in attesa di essere salvati. Ovviamente questo sarebbe problematico se stessimo cambiando modelli in silenzio per altri scopi - collection.saveChanged() salverebbe anche questi, quindi vale la pena prendere in considerazione l'impostazione di un flag alternativo.

In ogni caso, se stiamo facendo in questo modo, al momento del salvataggio, dobbiamo assicurarci che backbone pensi che i modelli non siano cambiati (senza attivare i loro eventi di modifica), quindi dobbiamo manipolare manualmente il modello come se non era stato cambiato. Il saveChanged() metodo itera oltre i nostri modelli cambiato e chiama questo metodo changeSilently() sul modello, che è fondamentalmente solo model.change di Backbone() metodo senza i trigger:

Backbone.Model.prototype.changeSilently = function() { 
    var options = {}, 
    changing = this._changing; 
    this._changing = true; 
    for (var attr in this._silent) this._pending[attr] = true; 
    this._silent = {}; 
    if (changing) return this; 

    while (!_.isEmpty(this._pending)) { 
     this._pending = {}; 
     for (var attr in this.changed) { 
     if (this._pending[attr] || this._silent[attr]) continue; 
     delete this.changed[attr]; 
     } 
     this._previousAttributes = _.clone(this.attributes); 
    } 
    this._changing = false; 
    return this; 
} 

Usage:

model1.set({key: value}, {silent: true}); 
model2.set({key: value}, {silent: true}); 
model3.set({key: value}, {silent: true}); 
collection.saveChanged(); 

RE. RESTfulness .. Non è del tutto corretto fare un PUT all'endpoint della collezione per cambiare "alcuni" dei suoi record. Tecnicamente un PUT dovrebbe sostituire l'intera collezione, anche se fino a quando la mia applicazione non avrà mai realmente bisogno di sostituire un'intera collezione, sono felice di adottare l'approccio pragmatico.

1

È possibile definire una nuova risorsa per eseguire questo tipo di comportamento, è possibile chiamarlo MyModelBatch.

è necessario implementare una nuova risorsa in te lato server che è in grado di digerire una Array di modelli ed eseguire l'azione appropriata: CREATE, UPDATE e DESTROY.

Inoltre è necessario implementare un Model nel vostro lato client Backbone con uno attributo che è l'Array di modelli e uno speciale url che non fa uso del id.

Circa l'ri-renderizzare cosa che vi consiglio di cercare di avere una vista da ogni modello quindi non ci sarà tanto rende come modelli sono cambiati ma saranno dettaglio ri-rendering senza duplicazione.

+0

Grazie. Sulla base del tuo suggerimento di utilizzare un modello temporaneo, noto che Backbone.sync esamina solo la proprietà url dei modelli e il metodo toJSON, quindi piuttosto che usare un modello reale, possiamo facilmente falsarne uno per la sincronizzazione. ToJSON restituisce solo una copia degli attributi dei modelli da stringificare, quindi possiamo usarlo per restituire la nostra gamma di modelli. Vedi la mia risposta. – hacklikecrack

Problemi correlati