Backbone.js mise à jour partielle du modèle

Est-il possible d'envoyer uniquement les propriétés modifiées d'un modèle lors de la sauvegarde des modifications?

BTW, Y-a-t-il un groupe "backbone.js" officiel ou une liste de diffusion pour poser ce type de questions?

Actuellement, le backbone ne supporte pas l'envoi d'une partie du modèle au serveur. Ce serait cependant un ajout intéressant.

Si vous parcourez la source, vous pouvez voir que Backbone.sync (la partie du backbone qui est responsable de la communication avec le magasin de données) est l'un des composants les plus simples de l'épine dorsale et enveloppe simplement le support ajax dans jQuery ou Zepto.

METTRE À JOUR

En commençant la version 0.9.10 du backbone, la mise à jour partielle du modèle est prise en charge de manière native via

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

Backbone ne supporte pas cela hors de la boîte, mais vous avez tous les outils nécessaires pour que cela se produise. Si vous regardez Backbone.sync, vous verrez qu'il appelle sur JSON sur votre modèle pour obtenir les données réelles à envoyer. Maintenant, vous devrez peut-être régler cela, mais voici l'essentiel:

 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 vous voulez une solution complète, vous devez appliquer la même logique pour désactiver, effacer, enregistrer, etc. Mais je suppose que vous obtenez comment faire cela. J'ai mis la réinitialisation des attributs sales dans la fonction toJSON, mais il devrait vraiment être dans le rappel de succès (lors de la sauvegarde).

MISE À JOUR: démarrage du backbone version 0.9.10, la mise à jour partielle est prise en charge de manière native via

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

Jusqu'à 0.9.9 Une approche sans éditer directement le fichier de bibliothèque backbone.js. Juste l'ajouter le code suivant dans le fichier js de l'application et le charger après que backbone.js soit chargé.

 //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 

J'ai essayé plusieurs des techniques qui ont été suggérées ici, mais finalement, j'ai décidé de modifier Backbone.Model et Backbone.sync directement. Ce que je voulais fournir était une méthode peu invasive pour fournir cette fonctionnalité qui ne nécessitait pas d'informer les développeurs de mon équipe sur les méthodes de base de base; Trop propice à l'erreur. Ma solution implique seulement de passer une option à la méthode de sauvegarde du modèle. Par exemple:

 //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} } 

Maintenant, pour activer cette fonctionnalité, j'ai apporté des modifications très mineures à Backbone.Model.save et Backbone.sync. Voici la modification de 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); 

Ce qui se passe ici, c'est que si partialUpdate est transmis en option, un nouveau membre appelé updateAttrs est créé sur le hash des options. Les options hash sont automatiquement transmises à Backbone.sync.

Pour Backbone.sync, j'ai modifié le conditionnel suivant:

 // 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()); } 

à…

 // 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()); } 

En ajoutant les contrôles conditionnels supplémentaires pour voir si partialUpdate a été défini, puis, si c'est le cas, définissez params.data dans options.updateAttrs. Cela sera transmis à la méthode jquery Ajax.

Au lieu d'écraser Backbone.sync vous pouvez simplement le faire dans la méthode Model.sync . Comme vous ne pouvez pas accéder à model.changedAttributes() , assurez-vous de toujours renvoyer des faux dans cette méthode.

 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 

Si vous devez envoyer une demande de mise à jour vers un serveur avec juste des attributs spécifiques, vous pouvez faire quelque chose de similaire:

 saveAttributes: (attributes, options={}) -> data = {} _(attributes).each (attribute) => data[attribute] = @get(attribute) params = data: $.param(data) _.extend(params, options) Backbone.sync('update', null, params) 

Plus d'informations à ce sujet: https://github.com/documentcloud/backbone/pull/573

Vous pouvez l'étendre avec _.extend Backbone.Model.prototype

La plupart des réponses ici sont directement ou indirectement modifier la fonction de sync . Voici ma petite astuce pour surmonter ceci:

Lorsque vous appelez votre model.save , vous pouvez réellement passer le deuxième paramètre qui sera transmis avec $.ajax lorsque Backbone essaie d'appeler la synchronisation. J'ai fait la mise à jour partielle comme celle-ci, plus précisément en précisant les champs à soumettre:

 /** * 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'}); } 

Cette action a mis à jour correctement mes attributs de modèle, plus l'envoi au serveur uniquement des données mentionnées dans la JSON.stringify . contentType est requis ici, mieux

C'est parce que Backbone.sync possède ces lignes , et nous l'annulons en passant data attribut data :

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

Crédit sur cette page: https://plus.google.com/103858073822961240171/posts/1gTcu6avmWQ

Remarque: ce modèle hérité du DeepModel de powmedia pour prendre en charge les attributs de modèles imbriqués

modifier

Depuis Backbone 0.9.9, l'option de patch a été ajoutée, donc cette astuce ne s'applique qu'à la version précédente.

Pour soumettre uniquement les données sales à votre serveur, fournissez {patch: true} dans votre save , de sorte que

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

Merci à @Lincoln B de l'indiquer.

En utilisant la réponse (très bonne) de Jayyy V, je l'ai ré-écris un peu pour que la fonction de synchronisation prenne une liste blanche afin que vous puissiez lui donner un ensemble de clés qui sont sauvegardées.

 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); }; 

S'appuyant sur le post de @ Julien: Vous pouvez l'ajouter à votre modèle, et il n'émettra que les attributs que vous transmettez par opposition à l'ensemble du modèle. Vous pouvez toujours utiliser save pour le comportement par défaut, et vous pouvez utiliser partialSave lorsque vous souhaitez simplement envoyer les attributs que vous transmettez en tant que paramètre. J'ai testé cela, et cela a fonctionné pour moi.

  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); }, 

En fait, il existe une façon beaucoup plus simple d'y parvenir

Si vous regardez backbone.js ligne 1145, vous verrez 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)); } 

Cela signifie que vous pouvez remplacer la partie de données de la Xbox en mettant les données dans vos options

Puisque backbone sauvegarde nécessite model.save([attributes], [options])

Mais n'oubliez pas que les attributs comme id pourraient être essentiels à une bonne économie

Exemple

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

Donc, vous devriez faire quelque chose comme ça

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

ou

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

finalement

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

Cela fait l'affaire très bien pour moi et pourrait être utilisé avec n'importe quel backbone avec xhr comme fetch, save, delete, …

Messing with save, sync ou toJSON semble si faux

J'ai créé un modèle étendu.

 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; } }); 

Exemple d'utilisation:

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

Et les champs autorisés à sauvegarder:

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