Soy consciente de que se puede hacer y he examinado bastantes lugares (que incluyen: ¿Las mejores prácticas para guardar una colección completa? ). Pero todavía no tengo claro "exactamente cómo" está escrito en código. (La publicación lo explica en inglés. Sería genial tener una explicación específica de JavaScript :)
Digamos que tengo una colección de modelos; los modelos en sí pueden tener colecciones anidadas. He anulado el método toJSON () de la colección principal y obtengo un objeto JSON válido. Deseo "guardar" la colección completa (JSON correspondiente), pero la red troncal no parece estar incorporada con esa funcionalidad.
var MyCollection = Backbone.Collection.extend({
model:MyModel,
//something to save?
save: function() {
//what to write here?
}
});
Sé que en algún lugar 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' ha terminado 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:
- ¿Cómo / qué escribir en el código para "conectarlo todo"?
- ¿Cuál es la ubicación 'correcta' de las devoluciones de llamada y cómo especificar una devolución de llamada de "éxito / error"? Quiero decir sintácticamente? No tengo claro la sintaxis de registrar devoluciones de llamada 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 Backbone. Sé que definitivamente me estoy perdiendo algo, la sincronización de colecciones enteras.
fuente
Respuestas:
Mi pensamiento inmediato no es anular el método en el método de guardar en Backbone.Collection, sino envolver la colección en otro Backbone.Model y anular el método toJSON en eso. Entonces Backbone.js tratará el modelo como un solo recurso y no tendrá que piratear la forma en que Backone piensa demasiado.
Tenga en cuenta que Backbone.Collection tiene un método toJSON, por lo que la mayor parte de su trabajo está hecho por usted. Solo tiene que enviar el método toJSON de su contenedor Backbone.Model a 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 } });
fuente
getURL(model)
al argumento del modelo y tampoco realiza ningún tipo de comparaciones ... parece ser intencional por diseñoUn 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 opciones normales de jQuery ajax.
fuente
return Backbone.sync..
sea más Backbonish.Terminé teniendo un método similar a 'guardar' y llamé $ .ajax dentro de él. Me dio más control sobre él sin la necesidad de agregar una clase contenedora como sugirió @brandgonesurfing (aunque me encanta la idea :) Como se 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 se tropiece con él ...
fuente
Esto realmente depende de cuál sea el contrato entre el cliente y el servidor. He aquí un ejemplo simplificado CoffeeScript donde un PUT a
/parent/:parent_id/children
la{"children":[{child1},{child2}]}
reemplazará hijos de un padre con lo que hay en el PUT y retorno{"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 se encuentran sus datos cuando se ejecuta save (), en qué estado deben estar para enviarlos al servidor y lo que da el servidor espalda.
Si su servidor está bien con un PUT de
[{child1},{child2]
, entonces su línea Backbone.sync podría cambiar aresponse = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json')
.fuente
La respuesta depende de lo que quiera hacer con la colección en el lado del servidor.
Si tiene que enviar datos adicionales con la publicación, es posible que necesite un modelo contenedor o un modelo relacional .
Con el modelo de contenedor , 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; } });
Creo que los modelos relacionales son mejores, porque puedes configurarlos más fácilmente y puedes regular con laopción includeInJSON qué atributos poner en el json que envías a tu 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 al prototipo de colección) en ese caso:
var Occupants = Backbone.Collection.extend({ url: "/concrete-house/occupants", model: Person, save: function (options) { this.sync("update", this, options); } });
fuente
También me sorprendió que las colecciones de Backbone no tengan un guardado integrado. Esto es lo que puse en mi colección de columna vertebral para hacerlo. Definitivamente no quiero iterar a través de cada modelo de la colección y guardar de forma independiente. Además, estoy usando Backbone en el backend usando Node, así que estoy anulando el nativo
Backbone.sync
para guardarlo en un archivo plano en mi pequeño proyecto, pero el código debería ser prácticamente el mismo:save: function(){ Backbone.sync('save', this, { success: function(){ console.log('users saved!'); } }); }
fuente
save: function (options) { Backbone.sync('save', this, options); }
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();
Bastante estresante hacia adelante :)
fuente
He aquí un ejemplo sencillo:
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.
fuente
Probaría 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 )
fuente
La respuesta aceptada es bastante buena, pero puedo ir un paso más allá y darle un código que garantizará que se activen los eventos adecuados para sus oyentes y al mismo tiempo le permitirá pasar las devoluciones de llamada de eventos ajax de la opción:
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); }
fuente
Para cualquiera que todavía esté usando backbone.js en 2017, la respuesta aceptada no funciona.
Intente eliminar la anulación de toJSON () en el modelo de contenedor y llame a toJSON en la colección cuando cree una instancia del contenedor de modelo.
new ModelWrapper(Collection.toJSON());
fuente