“Cómo” guardar una colección completa en Backbone.js – Backbone.sync o jQuery.ajax?

Soy muy consciente de que se puede hacer y he analizado bastantes lugares (entre ellos: ¿ práctica recomendada para guardar una colección completa? ). Pero todavía no estoy claro “exactamente cómo” está escrito en el código? (la publicación lo explica en inglés. Sería genial tener una explicación específica de JavaScript 🙂

Supongamos que tengo una colección de modelos: los propios modelos pueden tener colecciones anidadas. He reemplazado el método toJSON () de la colección padre y estoy obteniendo un objeto JSON válido. Deseo “guardar” toda la colección (JSON correspondiente), pero la red troncal no parece estar integrada con esa funcionalidad.

var MyCollection = Backbone.Collection.extend({ model:MyModel, //something to save? save: function() { //what to write here? } }); 

Sé que en algún lado tienes que decir:

 Backbone.sync = function(method, model, options){ /* * What goes in here?? If at all anything needs to be done? * Where to declare this in the program? And how is it called? */ } 

Una vez que la ‘vista’ se realiza con el procesamiento, es responsable de decirle a la colección que se “guarde” en el servidor (capaz de manejar una solicitud de actualización / creación masiva).

Preguntas que surgen:

  1. ¿Cómo / qué escribir en el código para “conectarlo todo junto”?
  2. ¿Cuál es la ubicación “correcta” de las devoluciones de llamada y cómo especificar una callback “éxito / error”? Me refiero sintácticamente? No tengo clara la syntax de registrar devoluciones de llamadas en la red troncal …

Si de hecho es un trabajo complicado, ¿podemos llamar a jQuery.ajax dentro de una vista y pasar el this.successMethod o this.errorMethod como devoluciones de llamada de éxito / error? ¿Funcionará?

Necesito sincronizarme con la forma de pensar de la red troncal: sé que definitivamente me falta algo, sincronización de colecciones enteras.

Mi pensamiento inmediato no es anular el método de guardar en Backbone.Collection, sino ajustar la colección en otro Backbone.Model y anular el método toJSON en eso. Entonces, Backbone.js tratará el modelo como un único recurso y no tendrá que hackear el camino de regreso. Piensa demasiado.

Tenga en cuenta que Backbone.Collection tiene un método toJSON, por lo que la mayor parte de su trabajo está hecho para usted. Solo tiene que utilizar el método toJSON de su contenedor Backbone.Model para Backbone.collection.

 var MyCollectionWrapper = Backbone.Model.extend({ url: "/bulkupload", //something to save? toJSON: function() { return this.model.toJSON(); // where model is the collection class YOU defined above } }); 

Un muy simple …

 Backbone.Collection.prototype.save = function (options) { Backbone.sync("create", this, options); }; 

… le dará a sus colecciones un método de guardado. Tenga en cuenta que esto siempre publicará todos los modelos de la colección en el servidor independientemente de lo que haya cambiado. las opciones son solo jQuery ajax opciones normales.

Terminé teniendo un método de “guardar” y llamé $ .ajax dentro de él. Me dio más control sobre él sin la necesidad de agregar una clase contenedora como @brandgonesurfing sugirió (aunque me encanta la idea 🙂 Como mencioné ya que ya tenía el método collection.toJSON () anulado todo lo que hice fue usarlo en la llamada ajax …

Espero que esto ayude a alguien que tropieza con eso …

Esto realmente depende de lo que el contrato es entre el cliente y el servidor. Aquí hay un ejemplo simplificado de CoffeeScript donde un PUT a /parent/:parent_id/children con {"children":[{child1},{child2}]} reemplazará los hijos de un padre con lo que está en el PUT y devolverá {"children":[{child1},{child2}]} :

 class ChildElementCollection extends Backbone.Collection model: Backbone.Model initialize: -> @bind 'add', (model) -> model.set('parent_id', @parent.id) url: -> "#{@parent.url()}/children" # let's say that @parent.url() == '/parent/1' save: -> response = Backbone.sync('update', @, url: @url(), contentType: 'application/json', data: JSON.stringify(children: @toJSON())) response.done (models) => @reset models.children return response 

Este es un ejemplo bastante simple, puede hacer mucho más … realmente depende de en qué estado estén sus datos cuando se ejecute save (), en qué estado debe estar para enviarlo al servidor y qué da el servidor espalda.

Si su servidor está bien con un PUT de [{child1},{child2] , entonces su línea Backbone.sync podría cambiar a response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json') .

La respuesta depende de lo que quieras hacer con la colección del lado del servidor.

Si tiene que enviar datos adicionales con la publicación, es posible que necesite un modelo de contenedor o un modelo relacional .

Con el modelo de envoltura siempre debe escribir su propio método de análisis :

 var Occupants = Backbone.Collection.extend({ model: Person }); var House = Backbone.Model.extend({ url: function (){ return "/house/"+this.id; }, parse: function(response){ response.occupants = new Occupants(response.occupants) return response; } }); 

Los modelos relacionales son mejores, creo, porque puede configurarlos más fácilmente y puede regular con la opción includeInJSON , que se le asigna al json que envía a su servicio de descanso.

 var House = Backbone.RelationalModel.extend({ url: function (){ return "/house/"+this.id; }, relations: [ { type: Backbone.HasMany, key: 'occupants', relatedModel: Person, includeInJSON: ["id"], reverseRelation: { key: 'livesIn' } } ] }); 

Si no envía datos adicionales , puede sincronizar la colección . Debe agregar un método de guardado a su colección (o el prototipo de la colección) en ese caso:

 var Occupants = Backbone.Collection.extend({ url: "/concrete-house/occupants", model: Person, save: function (options) { this.sync("update", this, options); } }); 

También me sorprendió que las colecciones Backbone no tengan un guardado integrado. Esto es lo que puse en mi colección de la columna vertebral para hacerlo. Definitivamente no quiero iterar a través de cada modelo en la colección y guardar de forma independiente. Además, estoy usando Backbone en el backend usando Node, así que estoy redefiniendo el Backbone.sync nativo para guardarlo en un archivo plano en mi proyecto pequeño, pero el código debería ser el mismo:

  save: function(){ Backbone.sync('save', this, { success: function(){ console.log('users saved!'); } }); } 

Viejo hilo que sé, lo que terminé haciendo es lo siguiente:

 Backbone.Collection.prototype.save = function (options) { // create a tmp collection, with the changed models, and the url var tmpCollection = new Backbone.Collection( this.changed() ); tmpCollection.url = this.url; // sync Backbone.sync("create", tmpCollection, options); }; Backbone.Collection.prototype.changed = function (options) { // return only the changed models. return this.models.filter( function(m){ return m.hasChanged() }); }; // and sync the diffs. self.userCollection.save(); 

Pretty straint adelante 🙂

Intentaré algo como:

 var CollectionSync = function(method, model, [options]) { // do similar things to Backbone.sync } var MyCollection = Backbone.Collection.extend({ sync: CollectionSync, model: MyModel, getChanged: function() { // return a list of models that have changed by checking hasChanged() }, save: function(attributes, options) { // do similar things as Model.save } }); 

( https://stackoverflow.com/a/11085198/137067 )

Aquí hay un ejemplo simple:

 var Books = Backbone.Collection.extend({ model: Book, url: function() { return '/books/'; }, save: function(){ Backbone.sync('create', this, { success: function() { console.log('Saved!'); } }); } }); 

Cuando llame al método save () en su colección, enviará una solicitud de método PUT a la URL definida.

La respuesta aceptada es bastante buena, pero puedo ir un paso más allá y darle un código que asegurará que se activen los eventos correctos para sus oyentes, al mismo tiempo que le permite pasar opciones de devoluciones de eventos ajax:

 save: function( options ) { var self = this; var success = options.success; var error = options.error; var complete = options.complete; options.success = function( response, status, xhr ) { self.trigger('sync', self, response, options); if (success) return success.apply(this, arguments); }; options.error = function( response, status, xhr ) { self.trigger('error', self, response, options); if (error) return error.apply(this, arguments); }; options.complete = function( response, status, xhr ) { if (complete) return complete.apply(this, arguments); } Backbone.sync('create', this, options); } 

Para cualquiera que todavía esté usando backbone.js en 2017, la respuesta aceptada no está funcionando.

Intente eliminar la sobrescritura de toJSON () en el modelo de envoltura y llame a JSON en la colección cuando cree una instancia del envoltorio de modelo.

 new ModelWrapper(Collection.toJSON());