Closed giggio closed 11 years ago
Yeah, this is a tricky problem – that being creating a hard link between the view and an unknown model schema. The documented approach is to call applyBindings
on your view after making schema changes, or else pre-define your schema using model defaults.
I've got some long-term thoughts for a dynamic bridge feature, although I'm not convinced that it would solve more problems than it creates.
To make schema changes I would have to manually copy each value from the input field to the model. Only then I could call applyBindings. This would not make sense, as this information is already present in the bindings configuration. It makes more sense to declare the defaults and let the bindings do their work. You could, somehow, make the bindings do not depend on what is currently on the model, and add the attribute to the model if it is not already there. Right now the schema of the bindings is the schema of the models, the bindings are enforcing that, and that should not be coupled on such a dynamic language such as JS.
I'd have to respectfully disagree on the dynamic nature of JavaScript being a pass for building an application around view configuration. What JavaScript is or is not capable of is inconsequential to the fundamental discussion of software design, where Model/View separation is a key concept. Abiding the principle that a view should be a disposable layer built on top of a structured application model – then relying on the view to define model schema is putting the cart before the horse.
For what it's worth, I solved this by adding the following changes to my base model class:
var BaseModel = Backbone.Model.extend({
schema: [],
initialize: function() {
// Init model attributes from schema
var attributesToInit = {};
_.each(this.schema, function(attributeName) {
if (typeof this.get(attributeName) === "undefined")
attributesToInit[attributeName] = null;
}, this);
this.set(attributesToInit, { silent: true })
}
}
Schema attributes can then be defined on model classes like so:
var Dog = BaseModel.extend({
schema: [
"id",
"breed",
"age",
"name",
"color"
]
}
There is a backbone plugin that implements backbone model schemas, and provides additional common wants like type enforcement. I haven't tested whether or not it automatically makes sure all schema fields are present in the attributes
object, but if not, a similar check on initialize
could presumably be implemented.
When I am editing an existing model, binding works perfectly, because all the model attributes are set. When I am creating a new model, it does not yet have all the attributes, and binding fails. To solve that I am having to declare undefined defaults, like so: defaults: { _id: undefined, slug: undefined, name: undefined } Then binding works. I checked the code, it seems that when the parserFunct is created: https://github.com/gmac/backbone.epoxy/blob/cae1429ea51a40bea8db9d8c3e6d71e73e136aae/backbone.epoxy.js#L1193 It is not allowing for an attribute that is not yet defined. This is really anoying, as I would not have to define those attributes otherwise. After the attribute is defined, binding should happen normally.