2011-03-11 12 views
33

¿Es posible enviar solo las propiedades modificadas de un modelo al guardar los cambios?Actualización de modelo parcial Backbone.js

BTW, ¿Hay alguna lista de correo/grupo Backbone.js "oficial" para hacer este tipo de preguntas?

+5

Esto es ahora compatible partir de la versión 0.9.9, si te gusto solamente de los atributos modificados para ser enviado al servidor, llame a model.save (attrs, {patch: true}). Obtendrá una solicitud HTTP PATCH al servidor con solo los atributos transferidos. http://backbonejs.org/#Model-save – Ben

Respuesta

33

Actualmente, la red troncal no admite el envío de parte del modelo al servidor. Sin embargo, sería una adición interesante.

Si browse the source se puede ver que Backbone.sync (la parte de la columna vertebral que es responsable de comunicarse con el almacén de datos) es uno de los componentes más simples de la columna vertebral y simplemente envuelve el apoyo ajax de jQuery o Zepto.


ACTUALIZACIÓN

partir columna vertebral versión 0.9.10, actualización del modelo parcial está soportado de forma nativa a través de

model.save(attrs, {patch: true}) 
+0

Gracias. Esta característica es compatible con JavascriptMVC, por ejemplo, y estoy de acuerdo en que sería una gran adición. – ggarber

+3

El parche no es una buena idea para 'crear' un objeto nuevo y 'actualizar' un objeto, no es un diseño de pozo reparador. Recomiendo anular el método' sync' – sjbwylbs

+4

Parece que la forma más fácil de enviar solo los campos modificados es 'modelo .save (model.changedAttributes(), {patch: true}); '. Eso es en Backbone 1.0.0. –

46

Backbone no soporta esta fuera de la caja, pero tiene todas las herramientas para hacer que eso suceda Si observa Backbone.sync, verá que llama a JSON en su modelo para enviar los datos reales. Ahora es posible que tenga que modificar esto, pero aquí está el quid de la cuestión:

initialize: function(){ 
    this.dirtyAttributes = {} 
}, 
set: function(attrs, options){ 
    Backbone.Model.prototype.set.call(this, attrs, options); 
    _.extend(this.dirtyAttributes, attrs); 
}, 
toJSON : function(){ 
    json = this.dirtyAttributes; 
    this.dirtyAttributes = {}; 
    return json; 
} 

Si desea una solución completa que necesita para aplicar la misma lógica a desarmado, claro, guardar, etc. Pero te supongo obtener cómo hacer esto. Puse el restablecimiento de los atributos sucios en la función toJSON, pero realmente debería estar en la devolución de llamada exitosa (cuando se llama a guardar).

+0

Gracias por su respuesta. Acepté el otro porque era más rápido :-). – ggarber

+8

@gustavogb Bueno, gracias por empeorar StackOverflow. –

+0

Iría aún más lejos y solo marcaría como atributos sucios que realmente han cambiado. Establecer el valor de un atributo en su valor original (tal como se cargó desde el servidor) debe marcarlo como limpio (ya no está sucio). ¿Tendría esto sentido en la lib? ¿Debo enviar un parche? Tenga en cuenta que el comportamiento actual de '{patch: true}' no hace esto. –

1

Si necesita enviar solicitud de actualización a un servidor con unos atributos específicos, puede hacer algo similar:

saveAttributes: (attributes, options={}) -> 
    data = {} 
    _(attributes).each (attribute) => 
    data[attribute] = @get(attribute) 

    params = 
    data: $.param(data) 

    _.extend(params, options) 

    Backbone.sync('update', null, params) 

Más información sobre eso: https://github.com/documentcloud/backbone/pull/573

puede extenderlo con _.extend Backbone.Model.prototype

+0

obtengo la propiedad 'A 'url' o la función debe ser especificada. Error: Se debe especificar una propiedad o función 'url' Backbone_rails_sync.js: 15' – lulalala

2

Probé varias de las técnicas que se sugirieron aquí, pero al final, decidí modificar Backbone.Model y Backbone.sync directamente. Lo que quería proporcionar era un método mínimamente invasivo para proporcionar esta funcionalidad que no requería instruir a los desarrolladores de mi equipo sobre la superación de los métodos principales; demasiado propenso al error. Mi solución implica solo pasar una opción al método de "guardar" del modelo. Por ejemplo:

//Note that this could be from your view or anywhere you're invoking model.save 
saveToModel : function() { 
    this.model.save({ 
     field1 : field1Value, 
     field2 : field2Value, 
     field3 : field3Value 
    }, {partialUpdate : true} 
} 

Ahora, para habilitar esta funcionalidad, he hecho algunas modificaciones muy menores a Backbone.Model.save y Backbone.sync. Aquí está el cambio a Backbone.Model.save:

//If a partialUpdate is required, create a member on the options 
//hash called updateAttrs and set it to attrs 
if (options.partialUpdate != "undefined" && options.partialUpdate) { 
    options.updateAttrs = attrs; 
} 
//--->>>Put the block above right above the return line 
return (this.sync || Backbone.sync).call(this, method, this, options); 

¿Qué ocurre aquí es que si partialUpdate se pasa como una opción, a continuación, un nuevo miembro llamado updateAttrs se crea en el hash de opciones. El hash de opciones se pasa automáticamente a Backbone.sync.

Para Backbone.sync, he cambiado la siguiente condicional:

// Ensure that we have the appropriate request data. 
if (!params.data && model && (method == 'create' || method == 'update')) { 
    params.contentType = 'application/json'; 
    params.data = JSON.stringify(model.toJSON()); 
} 

a ...

// Ensure that we have the appropriate request data. 
if (!params.data && model && (method == 'create' || method == 'update')) { 
    params.contentType = 'application/json'; 

    //If doing a partial model update, then grab the updateAttrs member 
    //from options. Will not interfere with line directly below as params.data 
    //will have been set. 
    params.data = (options.partialUpdate != "undefined" && options.partialUpdate) 
       ? params.data = JSON.stringify(options.updateAttrs) 
       : params.data = JSON.stringify(model.toJSON()); 
} 

Adición de las comprobaciones condicionales adicionales para ver si se ajusta partialUpdate, a continuación, si es así, establece params.data a options.updateAttrs. Esto se pasará al método jquery Ajax.

6

ACTUALIZACIÓN: a partir columna vertebral versión 0.9.10, actualización parcial es compatible de forma nativa a través de

model.save(attrs, {patch: true}) 


Hasta 0.9.9 Un enfoque sin necesidad de editar directamente el archivo de biblioteca Backbone.js. Solo agregue el siguiente código en el archivo js de la aplicación y cárguelo después de que backbone.js se cargue.

//override the Backbone.sync to send only the changed fields for update (PUT) request 
var Original_BackboneSync = Backbone.sync; 

Backbone.sync = function(method, model, options) { 
    /* just handle the data picking logic for update method */ 
    if (!options.data && model && method == 'update') { 
     options.contentType = 'application/json'; 
     options.data = JSON.stringify(model.changedAttributes() || {}); 
    } 

    //invoke the original backbone sync method 
    return Original_BackboneSync.apply(this, arguments); 
}; 

//Tested in Backbone.js 0.9.1 
+0

Esa es una técnica genial. –

+0

¡Esta versión parece funcionar mejor que la nativa! – exussum

+0

'changedAttributes' solo contiene los cambios desde la última llamada a' .set() 'por lo que esto no funcionaría si hiciera algo como' m.set ('p1', 1); m.set ('p2', 2); m.save(); 'solo enviaría' {p2: 2} 'no todos los campos actualizados. – CodingWithSpike

2

En lugar de sobrescribir Backbone.sync que sólo podría hacerlo dentro del método Model.sync. Como no puede acceder al model.changedAttributes() allí, asegúrese de devolver siempre falso dentro de este método.

sync: (method, model, options) -> 
    if method is "update" 
    options.contentType = 'application/json' 
    changedData = {} 
    for attr in _.keys(options.changes) 
     changedData[attr] = model.get(attr) 
    options.data = JSON.stringify changedData 

    Backbone.sync method, model, options 
0

Uso de la (muy bien) Respuesta de Jayyy V, i volvió a escribir un poco para hacer la función de sincronización tomar una lista blanca para que pueda darle una serie de teclas que se salvan.

var Original_BackboneSync = Backbone.sync; 
Backbone.sync = function(method, model, options) { 
    /* check if a whitelist was in options */ 
    if (options.whitelist) { 
     options.contentType = 'application/json'; 
     /* use underscore method for picking only whitelisted attributes to save */ 
     options.data = JSON.stringify(_.pick(model.attributes, options.whitelist)); 
    } 

    //invoke the original backbone sync method 
    return Original_BackboneSync.apply(this, arguments); 
}; 
1

La mayoría de las respuestas aquí son ya sea directa o indirectamente modificar la función sync. Aquí está mi pequeño truco para superar esto:

Cuando llame a su model.save, puede pasar el segundo parámetro que se pasará junto con el $.ajax cuando Backbone está intentando llamar a la sincronización. Hice la actualización parcial de esta manera, más de especificar explícitamente los campos a presentar:

/** 
* On user clicking on "mark important" 
*/ 
onMarkImportantBtnClick: function() { 
    var marked = this.model.get('UserFeed.marked_important'), 
     data = { 
      UserFeed: { 
       marked_important: !marked 
      } 
     }; 
    this.model.save(data, {data: JSON.stringify(data), contentType: 'application/json'}); 
} 

Esta acción actualiza los atributos de mi modelo correctamente, además de enviar al servidor sólo los datos que se menciona en el JSON.stringify. contentType se requiere aquí, mejor

Esto se debe a Backbone.synchas these lines, y estamos negando que al pasar data atributo:

if (!options.data && model && (method == 'create' || method == 'update')) { 
    params.contentType = 'application/json'; 
    params.data = JSON.stringify(model.toJSON()); 
} 

crédito a esta página: https://plus.google.com/103858073822961240171/posts/1gTcu6avmWQ

Nota: Este modelo heredado de powmedia's DeepModel para admitir atributos de modelos anidados


Editar

Desde Backbone 0.9.9, se ha añadido la opción de patch por lo que este truco sólo es aplicable a la versión anterior.

Para presentar sólo los datos sucios de nuevo a su servidor, el suministro {patch: true} en su save, de tal manera que

this.model.save(modifiedData, {patch: true}); 

Gracias @Lincoln B por señalarlo.

+1

La mejor respuesta aquí, gracias por mirar la fuente. Backbone 0.9.9 agregó {path: true} para guardar, pero si no puede actualizar en este momento, esto es lo ideal. Definí personalmente un parche separado como parche: (attributes, options = {}) -> this.save (attributes, {data: JSON.stringify (attributes)}) –

+0

Sí. Esta solución solo se aplica a la versión antigua de Backbone. ¡El nuevo 'parche' es genial! –

0

Basándose en la publicación de @ Julien: Puede agregar esto a su modelo, y solo enviará los atributos que pase en lugar de todo el modelo. Todavía puede usar guardar para el comportamiento predeterminado, y puede usar partialSave cuando quiera simplemente enviar los atributos que transfiere como parámetro. He probado esto, y funcionó para mí.

partialSave: function(attr, options) { //use this method instead of save() 
    this.dirtyAttributes = attr; 
    this.save(attr, options); 
    }, 

    toJSON: function() { //overrides Backbone.Model.prototype.toJSON 
    if (this.dirtyAttributes) { 
     var attr = this.dirtyAttributes; 
     this.dirtyAttributes = null; 
     return attr; 
    } 
    return Backbone.Model.prototype.toJSON.apply(this, arguments); 
    }, 
0

De hecho hay una manera mucho más sencilla de lograr esto

si nos fijamos en la línea Backbone.js 1145 verá que

// Ensure that we have the appropriate request data. 
    if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { 
     params.contentType = 'application/json'; 
     params.data = JSON.stringify(options.attrs || model.toJSON(options)); 
    } 

Lo que significa que es posible anular los datos parte de la xhr, poniendo los datos en las opciones de

Desde columna vertebral Guardar requiere model.save([attributes], [options])

Pero recuerde que atribuye como ID puede ser esencial para el ahorro adecuado

Ejemplo

model.save({}, { data: JSON.stringify(data) }) ; 

Así que debería estar haciendo algo como esto

var data = { id : model.id , otherAttributes : 'value' } ; 

o

var data = model.toJSON() ; 
remove data.tempData ; 

Finalmente

model.save({}, { data : JSON.stringify(data) }); 

Esto hace el truco muy bien para mí y podría ser utilizado con cualquier cadena principal con xhr tales como buscar, guardar, borrar, ...

Messing con guardar, sincronizar o toJSON ven tan mal

0

Creé el modelo extendido.

var CModel = Backbone.Model.extend({ 
    save: function(attributes, options) { 
     if(_.isUndefined(options)) { 
      options = {}; 
     } 

     var isNeedAttrsRefresh = false, 
      basicAttributes = null; 

     if(!_.isUndefined(options.fields)) { 
      basicAttributes = _.clone(this.attributes); 
      var newAttributes = {}; 
      _.each(this.attributes, function(value, name) { 
       if(options.fields.indexOf(name) > -1) { 
        newAttributes[name] = value; 
       } 
      }); 
      this.attributes = newAttributes; 
      isNeedAttrsRefresh = true; 
     } 

     this.isSaving = true; 
     var result = Backbone.Model.prototype.save.apply(this, arguments); 
     this.isSaving = false; 

     if(isNeedAttrsRefresh) { 
      this.attributes = basicAttributes; 
     } 

     return result; 
    } 
}); 

Ejemplo de uso:

var CommentModel = CModel.extend({ ... } 

Y campos permitidos para ahorrar:

comment.save(null, { fields: ['message', 'entry_id', 'module_id', 'parent_id'] }); 
Cuestiones relacionadas