web-dev-qa-db-de.com

Backbone.js teilweise Modellaktualisierung

Kann beim Speichern der Änderungen nur die geänderten Eigenschaften eines Modells gesendet werden?

BTW, gibt es eine "offizielle" Backbone.js-Gruppe/Mailingliste, um diese Art von Fragen zu stellen?

34
ggarber

Derzeit unterstützt das Backbone das Senden eines Teils des Modells an den Server nicht. Es wäre jedoch eine interessante Ergänzung.

Wenn Sie die Quelle durchsuchen sehen Sie, dass Backbone.sync (der Teil des Backbones, der für die Kommunikation mit dem Datenspeicher verantwortlich ist) eine der einfachsten Komponenten im Backbone ist und die Unterstützung für Ajax einfach in jQuery oder Zepto umschließt.


UPDATE

ab Backbone Version 0.9.10 wird ein teilweises Modellupdate nativ über unterstützt 

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

Backbone unterstützt dies nicht sofort, aber Sie haben alle Werkzeuge, um dies zu ermöglichen. Wenn Sie Backbone.sync betrachten, werden Sie feststellen, dass es toJSON in Ihrem Modell aufruft, um die eigentlichen Daten zu senden . Jetzt müssen Sie dies möglicherweise optimieren, aber hier ist der Kernpunkt:

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

Wenn Sie eine vollständige Lösung wünschen, müssen Sie dieselbe Logik anwenden, um Unscharfstellen, Löschen, Speichern usw. durchzuführen. Aber ich schätze, Sie bekommen eine Anleitung dazu. Ich setze das Zurücksetzen der Dirty-Attribute in die toJSON-Funktion ein, aber es sollte wirklich im Erfolgsrückruf (beim Aufruf von save) sein.

46
Julien

UPDATE: Ab Backbone Version 0.9.10 wird ein teilweises Update nativ über unterstützt 

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


Bis 0.9.9 Eine Vorgehensweise, ohne die Bibliotheksdatei backbone.js direkt zu bearbeiten. Fügen Sie einfach den folgenden Code in der Anwendungs-js-Datei hinzu und laden Sie ihn, nachdem backbone.js geladen wurde.

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

Anstatt Backbone.sync zu überschreiben, können Sie dies einfach mit der Model.sync-Methode tun. Da Sie dort nicht auf model.changedAttributes() zugreifen können, geben Sie in dieser Methode immer false zurück.

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

Ich habe einige der hier vorgeschlagenen Techniken ausprobiert, aber am Ende habe ich beschlossen, Backbone.Model und Backbone.sync direkt zu modifizieren. Was ich wollte, war eine minimal-invasive Methode, um diese Funktionalität bereitzustellen, ohne dass Entwickler in meinem Team angewiesen wurden, Backbone-Methoden zu überschreiben. einfach zu fehleranfällig. Meine Lösung besteht darin, nur eine Option an die "save" -Methode des Modells zu übergeben. Zum Beispiel:

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

Um diese Funktionalität zu aktivieren, habe ich einige geringfügige Änderungen an Backbone.Model.save und Backbone.sync vorgenommen. Hier ist die Umstellung auf 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);

Hier passiert, dass bei der Übergabe von partialUpdate als Option ein neues Member mit dem Namen updateAttrs für den Optionshash erstellt wird. Der Optionshash wird automatisch an Backbone.sync übergeben.

Für Backbone.sync habe ich folgende Bedingung geändert:

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

zu...

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

Fügen Sie die zusätzlichen bedingten Prüfungen hinzu, um festzustellen, ob partialUpdate festgelegt wurde. Wenn dies der Fall ist, setzen Sie params.data auf options.updateAttrs. Diese wird dann an die Ajax-Methode von Jquery übergeben.

2
Brendan Delumpa

Bei den meisten Antworten handelt es sich entweder direkt oder indirekt um die sync-Funktion. Hier ist mein kleiner Trick, um das zu überwinden:

Wenn Sie Ihren model.save aufrufen, können Sie tatsächlich den zweiten Parameter übergeben, der zusammen mit dem $.ajax übergeben wird, wenn Backbone versucht, die Synchronisierung aufzurufen. Ich habe das partielle Update so durchgeführt, indem ich explizit spezifizierte, welche Felder eingereicht werden sollen:

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

Diese Aktion hat meine Modellattribute korrekt aktualisiert, und es wurden nur die Daten an den Server gesendet, die im JSON.stringify angegeben sind. contentType ist hier erforderlich, besser

Dies liegt daran, dass Backbone.syncdiese Zeilen hat und wir negieren es, indem wir das data-Attribut übergeben:

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

Dank an diese Seite: https://plus.google.com/103858073822961240171/posts/1gTcu6avmWQ

Hinweis: Dieses Modell wurde von powmedias DeepModel zur Unterstützung geschachtelter Modellattribute übernommen


Bearbeiten

Seit Backbone 0.9.9 wurde die Option patch hinzugefügt, sodass dieser Trick nur für die vorherige Version gilt.

Um nur die fehlerhaften Daten an Ihren Server zurückzuschicken, geben Sie {patch: true} in Ihrer save an

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

Vielen Dank @ Lincoln B für den Hinweis.

1
Lionel Chan

Wenn Sie eine Aktualisierungsanforderung nur mit bestimmten Attributen an einen Server senden müssen, können Sie Folgendes tun:

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

  params =
    data: $.param(data)

  _.extend(params, options)

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

Weitere Informationen dazu: https://github.com/documentcloud/backbone/pull/573

Sie können es mit _.extend Backbone.Model.prototype erweitern

1

Mit der (sehr guten) Antwort von Jayyy V habe ich sie ein wenig umgeschrieben, damit die Synchronisierungsfunktion eine Whitelist verwendet, sodass Sie ihr ein Array von Schlüsseln geben können, die gespeichert werden.

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

Tatsächlich gibt es einen viel einfacheren Weg, dies zu erreichen 

wenn Sie sich Backbone.js Zeile 1145 ansehen, werden Sie das sehen 

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

Das bedeutet, dass Sie den Datenteil von xhr überschreiben können, indem Sie Daten in Ihre Optionen setzen

Da Backbone Save model.save([attributes], [options]) benötigt

Denken Sie jedoch daran, dass Attribute wie id möglicherweise für das korrekte Speichern von Bedeutung sind 

Beispiel 

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

Also solltest du so etwas tun

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

oder 

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

Endlich 

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

Das macht den Trick ganz gut für mich und könnte mit jedem Backbone mit Xhr wie Abrufen, Speichern, Löschen, ... verwendet werden.

Mit Save, Sync oder toJSON spielen sieht also falsch aus 

0
Pascal

Aufbauend auf dem Beitrag von @ Julien: Sie können dies zu Ihrem Modell hinzufügen und es werden nur die Attribute gesendet, die Sie übergeben, im Gegensatz zum gesamten Modell. Sie können save weiterhin für das Standardverhalten verwenden, und Sie können partialSave verwenden, wenn Sie nur die Attribute senden möchten, die Sie als Parameter übergeben. Ich habe das getestet und es hat für mich funktioniert.

  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

Ich habe ein erweitertes Model erstellt.

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

Verwendungsbeispiel:

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

Und erlaubte Felder zum Speichern:

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