Simon Boudrias
Simon Boudrias

Reputation: 44629

Exclude model properties when syncing (Backbone.js)

Is there a way to exclude certain property from my model when I sync?

For example, I keep in my model information about some view state. Let's say I have a picker module and this module just toggle a selected attributes on my model. Later, when I call .save() on my collection, I'd want to ignore the value of selected and exclude it from the sync to the server.

Is there a clean way of doing so?

(Let me know if you'd like more details)

Upvotes: 43

Views: 20433

Answers (11)

Simon Boudrias
Simon Boudrias

Reputation: 44629

This seems like the best solution (based on @nikoshr referenced question)

Backbone.Model.extend({

    // Overwrite save function
    save: function(attrs, options) {
        options || (options = {});
        attrs || (attrs = _.clone(this.attributes));

        // Filter the data to send to the server
        delete attrs.selected;
        delete attrs.dontSync;

        options.data = JSON.stringify(attrs);

        // Proxy the call to the original save function
        return Backbone.Model.prototype.save.call(this, attrs, options);
    }
});

So we overwrite save function on the model instance, but we just filter out the data we don't need, and then we proxy that to the parent prototype function.

Upvotes: 46

Nacho
Nacho

Reputation: 81

I found some problems with the accepted solution, as options.data modifies the way Backbone makes the calls. Better using options.attrs as this:

Backbone.Model.extend({
    save: function (attrs, options) {
        options = options || {};
        attrs = _.extend({}, _.clone(this.attributes), attrs);

        // Filter the data to send to the server
        delete attrs.selected;
        options.attrs = attrs;
        // Proxy the call to the original save function
        return Backbone.Model.prototype.save.call(this, attrs, options);
    }
});

Upvotes: 3

Gabriel Lupu
Gabriel Lupu

Reputation: 1829

Having this same issue, I decided to create a small module that can help : https://github.com/lupugabriel1/backbone-model-save

This is how you can use it in your models:

var myModel = new Backbone.ModelSave.extend({
    notSync: ['name', 'age']
});

Upvotes: 0

Pavel Sedek
Pavel Sedek

Reputation: 529

To set only desired values, use HTTP PATCH insead of HTTP POST. On the backbone side, just add a patch attribute to the save method:

entity.save(data,{patch:true})

Using save with this attribute, only fields passed as data are sent to the server.

Upvotes: 0

Pascal
Pascal

Reputation: 2405

In fact there is a much simpler way of achieving this without messing with backbone save or sync function since you would no be expecting this behaviour to be permanent

if you look at backbone.js line 1145 you will see that

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

Which means that you may override the data part of the xhr by putting data in your options

Since backbone save requires model.save([attributes], [options])

But remember that attributes like id might be essential to proper saving

Example

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

So you should be doing something like this

var data = { id : model.id , otherAttributes : 'value' }  ;  
model.save( {}, { data : JSON.stringify(data) } );

This do the trick quite well for me and could be used with any backbone with xhr such as fetch, save, delete, ...

Upvotes: 6

John Xiao
John Xiao

Reputation: 1838

Set options.attrs will allow you customise api param:

var model = new Backbone.Model();
model.save(null, {
  wait: true,
  success: function() {
  },
  attrs: _.omit(model.attributes, 'selected')
});

Upvotes: 2

Ross Allen
Ross Allen

Reputation: 44900

Based on several of the answers, this accounts for cases of null objects and a conditional in Backbone that doesn't sent the contentType if options.data is already set:

EDITABLE_ATTRIBUTES = ["name", "birthdate", "favoriteFood"];

...

save: function(attrs, options) {
  // `options` is an optional argument but is always needed here
  options || (options = {});

  var allAttrs = _.extend({}, this.attributes, attrs);
  var allowedAttrs = _.pick(allAttrs, EDITABLE_ATTRIBUTES);

  // If `options.data` is set, Backbone does not attempt to infer the content
  // type and leaves it null. Set it explicitly as `application/json`.
  options.contentType = "application/json";
  options.data = JSON.stringify(allowedAttrs);

  return Backbone.Model.prototype.save.call(
    this, allowedAttrs, options);
},

Upvotes: 3

oak
oak

Reputation: 3028

my solution combine all the above. just use white list instead of black one .. this is good rule in general

define

          attrWhiteList:['id','biography','status'],

and then overwrite the save

  save: function(attrs, options) {
    options || (options = {});

 //here is whitelist or all
    if (this.attrWhiteList != null )
          // Filter the data to send to the server
             whitelisted =  _.pick(this.attributes, this.attrWhiteList);
    else  
        whitelisted =this.attributes;
    /* it seems that if you override save you lose some headers and the ajax call changes*/
    // get data
    options.data = JSON.stringify(whitelisted);

    if ((this.get('id') == 0) || (this.get('id') == null)) 
        options.type = "POST"
    else
        options.type = "PUT";


    options.contentType = "application/json";
     //        options.headers =  { 
     //            'Accept': 'application/json',
     //            'Content-Type': 'application/json' 
     //        },

    // Proxy the call to the original save function
   return  Backbone.Model.prototype.save.call(this, attrs, options);
},

Upvotes: 11

byoungb
byoungb

Reputation: 1801

In Underscore 1.3.3 they added pick and in 1.4.0 they added omit which can be used very simply to override your model's toJSON function to whitelist attributes with _.pick or blacklist attributes with _.omit.

And since toJSON is used by the sync command for passing the data to the server I think this is a good solution as long as you do not want these fields wherever else you use toJSON.

Backbone.Model.extend({
    blacklist: ['selected',],
    toJSON: function(options) {
        return _.omit(this.attributes, this.blacklist);
    },
});

Upvotes: 35

Tallmaris
Tallmaris

Reputation: 7590

If it is a one-off occasion, you could use mode.unset('selected', { silent:true }) (silent is set only to avoid firing the change event), to remove the attribute... This has the not so nice counter-effect of having to re-set it after saving though.

This said, I totally endorse one of the solutions above. Moreover if this is something you need on a more regular basis.

Upvotes: 0

rinat.io
rinat.io

Reputation: 3198

Since save uses toJSON we override it:

    toJSON: function(options) {
        var attr = _.clone(this.attributes);
        delete attr.selected;
        return attr;
    },

But it may not work if you're using toJSON and need selected in views for example. Otherwise you probably need to override save method.

Upvotes: 2

Related Questions