Exclude model properties when syncing (Backbone.js)
Solution 1
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.
Solution 2
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);
},
});
Solution 3
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);
},
Solution 4
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, ...
Solution 5
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);
}
});
Related videos on Youtube
Simon Boudrias
Originally from Montréal, but explored the world through different long term work opportunities: San Francisco 🚆Beijing 🚆Vancouver. Main contributor on Yeoman project and original author of Inquirer. I also manage open sourced many other semi-popular projects. Checkout https://github.com/SBoudrias to find out more.
Updated on July 09, 2022Comments
-
Simon Boudrias almost 2 years
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 ofselected
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)
-
nikoshr over 11 yearsSomething like this stackoverflow.com/questions/11522286/… ?
-
Simon Boudrias over 11 yearsHey thanks @nikoshr that's a pretty badass answer.
-
-
Azder almost 11 yearsthe problem with toJSON approach is that it filters out data you need for the view as well as, not just for saving to server
-
Azder almost 11 years
_.pick
and_.omit
are great, I use them, but insave
, nottoJSON
. The problem withtoJSON
approach is that it filters out data you need for the view as well as, not just for saving to server. -
David Tinker almost 11 yearsattrs might be null so do JSON.stringify(attrs || this.attributes) instead .. or JSON.stringify(_.pick(attrs || this.attributes, "f1", "f2", ...)) for a whitelist
-
Will over 10 yearsPretty sure this example isn't quite right. You're stringifying attrs and then trying to delete variables from it like it's an object... but it's a string.
-
msanjay about 10 yearsyeah whitelist feels better. And I'd pass it as an option in case I need different things to be saved in different situations.
-
msanjay about 10 yearsBtw what about the attrs parameter? It should be included in the toJson
-
byoungb about 10 yearsThat is true, but I would also say that for me I do not use the toJSON for view/template rendering already because it is not very forgiving on undefined variables, so I instead pass the entire model to the template, and then I can user model.has("field") in case it is not set yet. (for instance the ID on a newly created field)
-
Azder about 10 years"not forgiving" - I haven't had that problem with my templates, toJSON returned all I need: all I did was set the field defaults: { id: null }, and voila, id is set :)
-
oak about 10 yearshey @msanjay sorry for the delay but what do you mean by "included in the toJson"?
-
msanjay about 10 yearsOops by toJson I meant stringify, I don't remember why exactly now, but for some reason I got it working with: options.data = JSON.stringify(_.extend(whitelisted, attrs));
-
oak about 10 yearsbasicly you don't need to _.extend whitelisted as long as you have attrWhiteList in your object. why? because the _.pick will get you the right attr. if you want to use the passed attr, you can do
_.pick(attrs,this.attrWhiteList)
instead ofthis.attributes
-
Anton Abilov almost 10 yearsThis is great, however, I have a problem that the non-synced attributes are overwritten on a collection fetch. Is it possible to preserve them?
-
byoungb almost 10 yearsYeah you could just override your model's
parse
function and strip out the attributes that you do not what overwritten (with_.omit
!), but this would cause it to not be set it initially. But you could code around that. -
Simon Boudrias almost 10 yearsGood solution if you need custom per action attributes. But it won't scale if you want to always ignore certain properties when saving.
-
John Xiao almost 10 years@SimonBoudrias If that, you can consider this solution: gist.github.com/bammoo/d8b09252e4cfa081d0e6
-
Simon Boudrias over 9 yearsThat works well for a case by case need. Not to filter out each time display related property. But it is a valid solution and way easier than a lot proposed here for "case by case" solution.
-
Simon Boudrias over 9 yearsUpdated - although I'm unsure in which case the attrs would be passed empty.
-
Evan Hobbs over 9 yearsI think this solution will cause strange errors. If you pass in null for attrs then it will use the model's attributes and delete a couple (attrs.selected and attrs.dontSync) so everytime you call save you'll be losing attributes from your model. Unless I'm reading that wrong?
-
Simon Boudrias over 9 years@EvanHobbs Looks correct, we should clone the object.
-
Cymen almost 9 years
save
should also return the result of calling theBackbone.Model.prototype.save
as it returns the XHR request object. -
Cymen almost 9 yearsI edited that in -- commenting in case I missed a reason not to do that.
-
mikebridge almost 9 yearsI think that this is now the correct solution---it requires no modification or complex assumptions about
Backbone.sync
orModel.save
, and it allows you to pass whatever attributes you want via XHR, without stepping on any of the MANY confusing things thatModel.save()
does. -
byoungb over 8 years@azder to clarify what I meant by "not forgiving". If you don't have defaults for everything and assume that is available in the _.template it will fail. Also I like to use custom model methods inside my templates from time to time so I find it just easier to pass the entire model to the template.
<%= model.get('selected') %>
or<%= model.custom_method() %>
-
Emile Bergeron almost 8 yearsNote that you can pass options to
toJSON
making it possible to bypass your modification when needing it for a view. -
Annarfych over 7 yearsAlso mind that this method won't properly
set
blacklisted properties if they're passed as parameter to thesave
. -
Shyam Habarakada almost 5 yearsThis works as advertised and I agree looks simpler than the current accepted answer.