2

I call set method multiple times and change several attributes. Then I want to send the changed data to the server with {patch: true}.

I can use model.save(attrs, {patch: true});, but I do not know attrs. I can't use model.toJSON() (unneeded fields) or model.changedAttributes() (only last set) to obtain attrs.

How can I do this?

T J
  • 41,966
  • 13
  • 81
  • 134
psixdev
  • 131
  • 1
  • 8
  • What do you exactly want to send the server, only the changed attribute or all attributes present in model or some of attributes – Kenny Jan 03 '17 at 09:18
  • Can you show us in example – Kenny Jan 03 '17 at 09:20
  • Only changed attributes – psixdev Jan 03 '17 at 09:20
  • You can bind listener on the model..... Listener function will be execute as soon as model value changes with changed attribute. You can send that to the server – Kenny Jan 03 '17 at 09:22
  • For example: Model is `{_id: 1, field1: 0, field2: 0, field3: 0}` I execute `model.set({field2: 2}); <...> model.set({field1: 1}); <...> model.save(...);`. I want to send to server `{field2: 2, field1: 1}` – psixdev Jan 03 '17 at 09:23
  • There is a simpler solution, provided backbone.js? – psixdev Jan 03 '17 at 09:27
  • Please look into my updated answer I think this is what you want – Kenny Jan 03 '17 at 09:36

3 Answers3

2

According to changedAttributes:

Optionally, an external attributes hash can be passed in, returning the attributes in that hash which differ from the model.

So you could try caching the state of model using toJSON before you start modifying. Once your modifications are done, pass the new state to changedAttributes method to retrieve changed attributes hash and then send a patch request. Something like

var oldAttrs = model.toJSON();

// ...do modifications here

var changedAttrs = model.changedAttributes(oldAttrs);
dataTosend = model.pick(_.keys(changedAttrs));
model.save(dataTosend, {patch: true});
Emile Bergeron
  • 16,148
  • 4
  • 74
  • 121
T J
  • 41,966
  • 13
  • 81
  • 134
0

Bind a listener for model

If you are setting value in view, listener should be like (better write it in initialize function)

  this.listenTo(this.model, "change", this.onModelValueChange);

And your listener function

  onModelValueChange: function(model, args) {
      model.save(args.changed, {patch: true});
  }
Kenny
  • 5,407
  • 4
  • 19
  • 37
  • 1
    But I want to call `save` only once – psixdev Jan 03 '17 at 09:46
  • I think that it is necessary to create issue on the backbone github... To my opinion. I think it would be good if `save` without attrs on first parameter save all changed attributes. – psixdev Jan 03 '17 at 09:54
0

While TJ has the right answer, I have a better way to achieve what he suggest.

Instead of making a raw clone of the attributes, I prefer to keep a master copy of the model at the start of the app or the initialize of the view.

this.master = this.model.clone();
// ... then changes are made to this.model

When ready to save, use the master model to compare the attributes and retrieve the changes directly.

var changes = this.master.changedAttributes(this.model.attributes);
if (changes !== false) this.model.save(changes, { patch: true });

With this, you can skip the dataTosend = model.pick(_.keys(changedAttrs)) altogether since changes already is an object of all the differences with the initial state of the master model.

If it's a view that is re-used after the model save:

var View = Backbone.View.extend({
    initialize: function() {
        this.updateMaster();
    },

    saveChanges: function() {
        var changes = this.master.changedAttributes(this.model.attributes);
        if (changes !== false) {
            this.model.save(changes, {
                patch: true,
                context: true,
                success: this.updateMaster
            });
        }
    },

    updateMaster: function() {
        this.master = this.model.clone();
    },
});
Community
  • 1
  • 1
Emile Bergeron
  • 16,148
  • 4
  • 74
  • 121