Is there a standard way to deal with non-saveable values in Backbone.
e.g.
MyModel = Backbone.extend(Backbone.Model, {
initialize: function () {
this.set({'inches': this.get('mm') / 25});
}
})
If I call save() on this model it will throw an error as there is no corresponding database field for inches
. I can think of a few ways to fix this, but am wondering if there's a tried and tested approach generally best used for this?
At the moment my preferred solution is to extend Backbone's toJSON
method and to allow passing of a boolean parameter dontCleanup
to allow for it to still return all the model's values (including the non saveable ones) when it's needed e.g. for passing to a template.
I like Peter Lyon's idea. I've thought about that a few times, but never actually put it in place. For all the ways that I have handled this, though, here are my two favorites:
This one is simple: don't store the values you need in the model's standard attributes. Instead, attach it directly to the object:
myModel.someValue = "some value";
The big problem here is that you don't get all of the events associated with calling set
on the model. So I tend to wrap this up in a method that does everything for me. For example, a common method I put on models is select
to say that this model has been selected:
MyModel = Backbone.Model.extend({
select: function(){
if (!this.selected){
this.selected = true;
this.trigger("change:selected", this, this.selected);
}
}
});
In your case, I'm not sure this would be a good approach. You have data that needs to be calculated based on the values that are in your attributes already.
For that, I tend to use view models.
The basic idea is that you create a backbone model that is persist-able, as you normally would. But the you come along and create another model that inherits from your original one and adds all the data that you need.
There are a very large number of ways that you can do this. Here's what might be a very simple version:
MyModel = Backbone.Model.Extend({ ... });
MyViewModel = function(model){
var viewModel = Object.create(model);
viewModel.toJSON = function(){
var json = model.toJSON();
json.inches = json.mm / 25;
return json;
};
return viewModel;
});
The big benefit of wrapping this with Object.create
is that you now have a prototypal inheritance situation, so all of your standard functionality from the model is still in place. We've just overridden the toJSON
method on the view model, so that it returns the JSON object with the inches
attribute.
Then in a view that needs this, you would wrap your model in the initialize function:
MyView = Backbone.View.extend({
initialize: function(){
this.model = MyViewModel(this.model);
},
render: function(){
var data = this.model.toJSON(); // returns with inches
}
});
You could call new MyViewModel(this.model)
if you want, but that's not going to do anything different, in the end, because we're explicitly returning an object instance from the MyViewModel
function.
When your view's render method calls toJSON
, you'll get the inches
attribute with it.
Of course, there are some potential memory concerns and performance concerns with this implementation, but those can be solved easily with some better code for the view model. This quick and dirty example should get you down the path, though.
I think this should do it. Define your Model
defaults
as your valid schema and then return only the subset of this.attributes
that is valid during toJSON
.
var Model = Backbone.Model.extend({
defaults: {
foo: 42,
bar: "bar"
},
toJSON: function () {
var schemaKeys = _.keys(this.defaults);
var allowedAttributes = {};
_.each(this.attributes, function (value, key) {
if (_.include(schemaKeys, key)) {
allowedAttributes[key] = value;
}
return allowedAttributes;
}
});
Note that _.pick
would make the code a bit shorter once you have underscore 1.3.3 available. I haven't seen a "tried and tested" convention in my travels through the backbone community, and since backbone leaves so many options open, sometimes conventions don't emerge, but we'll see what this stackoverflow question yields.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With