Using backbone.js:
I have a top level ModelA that contains 2 attributes and 2 nested models, ModelB and ModelC. ModelB and ModelC each have 2 attributes as follows:<
The general answer to the question "Can I" is always "yes, as long as you're willing to write the code." The point behind Backbone is to provide a strong separation of model and view. If B1 has a reference to A1, and A1 has a reference to C1, then you're fully capable of creating methods and setting the rules by which B1 can modify A1 and C1 and so forth.
The views should be set up to receive CRUD events from their respective models. If the user does something with B1view that modifies B1model, and B1model in turn modifies A1model, then A1model should generate an event that A1view receives and causes a re-render of A1view, and so forth. It should happen like magic. (In practice, it takes some time to get the magic right, but I've found Backbone to be really powerful. And BackboneRelational helps with things like what you're describing here.)
To be able to reach attributes on related models, the model must have some kind of knowledge about what models it is related to. Backbone.js does not implicitly deal with relations or nesting, which means you must yourself make sure that the models have knowledge of each other. To answer your questions, one way to go about it is to make sure each child model has a 'parent' attribute. This way you can traverse the nesting first up to the parent and then down to any siblings that you know of.
To be more specific with your questions. When initializing modelA, you are probably creating modelB and modelC, I would suggest setting a link to the parent model when doing this, like this:
ModelA = Backbone.Model.extend({
initialize: function(){
this.modelB = new modelB();
this.modelB.parent = this;
this.modelC = new modelC();
this.modelC.parent = this;
}
}
This way you can reach the parent model in any child model function by calling this.parent.
Regarding your views, when doing nested backbone views, I find it easier to let each view represent one HTML tag by using the tagName option of the view. I would write your views as this:
ViewA = Backbone.View.extend({
tagName: "div",
id: "new",
initialize: function(){
this.viewB = new ViewB();
this.viewB.parentView = this;
$(this.el).append(this.viewB.el);
}
});
ViewB = Backbone.View.extend({
tagName: "h1",
render: function(){
$(this.el).html("Header text"); // or use this.options.headerText or equivalent
},
funcB1: function(){
this.model.parent.doSomethingOnParent();
this.model.parent.modelC.doSomethingOnSibling();
$(this.parentView.el).shakeViolently();
}
});
Then in your application initialization code (eg in your controller), I would initiate ViewA and place its element inside the body element.
You can use some extensions, Backbone-Forms https://github.com/powmedia/backbone-forms for example. To follow your use case define a schema like:
var ModelB = Backbone.Model.extend({
schema: {
attributeB1: 'Text',
attributeB2: 'Text'
}
});
var ModelC = Backbone.Model.extend({
schema: {
attributeC: 'Text',
}
});
var ModelA = Backbone.Model.extend({
schema: {
attributeA1: 'Text',
attributeA2: 'Text',
refToModelB: { type: 'NestedModel', model: ModelB, template: 'templateB' },
refToModelC: { type: 'NestedModel', model: ModelC, template: 'templateC' }
}
});
Look at https://github.com/powmedia/backbone-forms#customising-templates for partial templates.
Important parts here are type: 'NestedModel'
and template: 'templateXXX'
.
This plugin has some limitations but you can look at others at https://github.com/documentcloud/backbone/wiki/Extensions%2C-Plugins%2C-Resources.
The above solution is on the right track but has some problems.
initialize: function(){
this.viewB = new ViewB();
this.viewB.parentView = this;
$(this.el).append(this.viewB.el);
}
Mainly, the model's toJSON() now returns stale data. I've posted a solution to fix this problem in a backbone.js plugin. You're welcome to use it.
There is backbone plugin Backbone-relational.js which provides one-to-one, one-to-many and many-to-one relations between models for Backbone.
I think this js will fulfill your needs. Vist BackboneRelational for more documentation.