web-dev-qa-db-fra.com

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 l'enregistrement des modifications?

BTW, y at-il un groupe/liste de diffusion "officiel" Backbone.js pour poser ce genre de questions?

34
ggarber

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

Si vous naviguez dans les sources , vous pouvez constater que Backbone.sync (la partie du réseau fédérateur responsable de la communication avec le magasin de données) est l’un des composants les plus simples du réseau central et englobe simplement le support ajax dans jQuery ou Zepto.


UPDATE

à partir de la version 0.9.10 du réseau principal, la mise à jour partielle du modèle est prise en charge de manière native via 

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

Backbone ne prend pas cela en charge immédiatement, mais vous avez tous les outils pour le faire. Si vous regardez Backbone.sync, vous verrez qu'il appelle toJSON sur votre modèle pour obtenir les données réelles à envoyer. . Maintenant, vous devrez peut-être modifier 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ésélectionner, effacer, enregistrer, etc. Mais je suppose que vous savez comment faire. Je mets la réinitialisation des attributs sales dans la fonction toJSON, mais cela devrait vraiment être dans le rappel de succès (lors de l'appel de sauvegarde).

46
Julien

UPDATE: à partir du backbone version 0.9.10, la mise à jour partielle est supportée de manière native via 

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


Jusqu'au 0.9.9 Une approche sans éditer directement le fichier de bibliothèque backbone.js. Ajoutez simplement le code suivant dans le fichier js de l’application et chargez-le après le chargement de backbone.js.

//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
6
Jay Kumar

Au lieu d'écraser Backbone.sync, vous pouvez simplement le faire dans la méthode Model.sync. Etant donné que vous ne pouvez pas accéder à model.changedAttributes(), assurez-vous de toujours renvoyer false 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
2
mateusmaso

J'ai essayé plusieurs des techniques suggérées ici, mais j'ai finalement décidé de modifier Backbone.Model et Backbone.sync directement. Ce que je voulais fournir, c’était une méthode peu invasive pour fournir cette fonctionnalité, qui ne nécessitait pas d’instruire les développeurs de mon équipe en matière de substitution des méthodes dorsales; trop sujette à l'erreur. Ma solution consiste uniquement à passer une option à la méthode "save" 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 le changement à 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 passé en tant qu'option, un nouveau membre appelé updateAttrs est créé sur le hachage des options. Le hachage des options est automatiquement passé à 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());
}

L'ajout des vérifications conditionnelles supplémentaires pour voir si partialUpdate a été défini. Si c'est le cas, définissez params.data sur options.updateAttrs. Cela sera ensuite passé à la méthode jQuery Ajax.

2
Brendan Delumpa

La plupart des réponses fournies ici modifient directement ou indirectement la fonction sync. Voici mon petit truc pour surmonter cela:

Lorsque vous appelez votre model.save, vous pouvez réellement transmettre le deuxième paramètre qui sera transmis avec le $.ajax lorsque Backbone tente d'appeler la synchronisation. J'ai fait la mise à jour partielle comme celle-ci, plus de spécifier explicitement 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 les attributs de mon modèle et envoyé au serveur uniquement les données mentionnées dans le JSON.stringify. _ {contentType est requis ici, mieux} _

Ceci parce que Backbone.synca ces lignes , et nous le nions en passant l'attribut data:

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

Credit à cette page: https://plus.google.com/103858073822961240171/posts/1gTcu6avmWQ

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


Edit

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

Pour ne renvoyer que les données altérées à votre serveur, indiquez {patch: true} dans votre save, de sorte que

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

Merci @Lincoln B pour l'avoir signalé.

1
Lionel Chan

Si vous devez envoyer une demande de mise à jour à un serveur avec uniquement des attributs spécifiques, vous pouvez effectuer 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

1
Dmitry Polushkin

En utilisant la (très bonne) réponse de Jayyy V, je l’ai réécrite un peu pour que la fonction de synchronisation prenne une liste blanche afin que vous puissiez lui donner un tableau de clés qui seront 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);
};
0
Bastian Albers

En fait, il existe un moyen beaucoup plus simple d’atteindre cet objectif. 

si vous regardez la ligne 1145 de backbone.js, 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));
    }

Ce qui signifie que vous pouvez remplacer la partie data de la xhr en plaçant des données dans vos options

Étant donné que la sauvegarde dans le circuit principal requiert model.save([attributes], [options])

Mais rappelez-vous que des attributs comme id peuvent être essentiels à une bonne sauvegarde 

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 très bien l'affaire pour moi et peut être utilisé avec n'importe quelle colonne vertébrale avec xhr telle que chercher, sauvegarder, supprimer, ...

Les erreurs avec save, sync ou toJSON semblent si mauvaises 

0
Pascal

S'appuyant sur le message de @ Julien: Vous pouvez l'ajouter à votre modèle et il n'enverra que les attributs que vous transmettez, contrairement au modèle entier. Vous pouvez toujours utiliser enregistrer pour le comportement par défaut et 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);
  },
0
Daniel

J'ai créé le 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 à enregistrer:

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