Reputation: 9883
I need virtual attributes in my backbone model but backbone doesn't appear to support this, how would I go about implementing this myself.
By virtual attributes, I mean attributes which are maintained client side only, and do not save/sync to the server. I would like to use them to store state information which is relevant to the client view rendering only, such as whether a model is selected / checked or not. I will use the model attributes in the template rendering of the view but I do not want these values sending to the server, or being saved in the database.
Upvotes: 1
Views: 1036
Reputation: 1527
If you want to store attributes on the model just on the client-side, why not just use:
model.set({attribute: "client-side only"})
instead of:
model.fetch()
To avoid triggering change events, you can pass in:
model.set({silent:true})
though the docs do not recommend this. If you're feeling more adventurous, you could also override the set method to be truly silent, as discussed in this answer:
Truly change a model attribute silently in Backbone.js?
UPDATE:
After looking around: Backbone.js partial model update and Exclude model properties when syncing (Backbone.js)
It looks like the simplest option you have is:
model.save(model.changedAttributes(), {patch: true});
or if you ever need to create/update these objects in a good restful way, you can override the backbone sync
like so:
Backbone.Model.extend({
// Overwrite save function
save: function(attrs, options) {
options || (options = {});
// 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
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
Credit: Simon Boudrias
Upvotes: 2
Reputation: 9779
A few options:
You could create a generic backbone model and use it to store & listen to properties of the view state.
StatefulView = Backbone.View.extend({
initialize: function(options) {
this.state = new Backbone.Model({
selected: !!options.selected
});
this.listenTo(this.state, 'change:selected', this.renderSelectedState);
},
// ...
});
I would recommend against storing any kind of view-related property in your model, because as your app grows, it is not a scalable solution if you display the same model in multiple selectable lists, for example. But... You could override your model's toJSON
function to remove attributes you don't want to persist:
ModelWithClientAttributes = Backbone.Model.extend({
toJSON: function(withClientAttrs) {
var json = Backbone.Model.prototype.toJSON.call(this);
if (withClientAttrs) return json;
return _(json).omit(this.clientAttrs);
},
// ...
});
// Extend from that base class when defining your model, and define the 'clientAttrs' property
Post = ModelWithClientAttributes.extend({
clientAttrs: ['selected', 'checked'],
// ...
});
// Then, in your view:
render: function() {
this.$el.html(
this.template(
this.model.toJSON( true )
)
);
}
// Backbone.sync will call toJSON without arguments, so they will receive the filtered json representation
var post = new Post({
selected: true,
title: 'May Snow Storm',
body: '...'
});
post.save();
console.log(post.toJSON()); // {"title": "May Snow Storm", "body": "..."}
console.log(post.toJSON(true)); // {"selected": true, "title": "May Snow Storm", "body": "..."}
Probably the safest and best solution is to simply whitelist the attributes that you want to persist in your server-side code.
Upvotes: 1