Question

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.

Was it helpful?

Solution 2

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.

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top