Reputation: 63405
I have the following backbone application.
It's a generic crud view, with the following template:
<div id="<%= crudId %>">
<div class="header-view"></div>
<div class="table-view"></div>
<div class="form-view"></div>
</div>
You can see the crud live here: http://bbbootstrap.com.ar/index.html#Wine
The view itself has subviews, to be rendered in the table-view and the form-view.
The thing is I want it to be a base crud view, and to be easily entendable, adding new subviews, for example, adding a new panel to issue some bulk operations.
These are the possible solutions I came out with so far
1- inheritance: create a new CrudBulkView inheriting from CrudView, modify the template to have a bulk-view place holder.
pro: inheritance can provide quite an elegant and simple solution cons: it's a bit limiting, I'd like to just be able to compose the BulkView and add it to the CrudView.
2- add a method to crudview like addView(view, place) with place being something like 'beforeForm', 'afterForm', 'beforeTable', etc... (it's much too hardcoded...
cons: too hardcoded
3- pass a function with each subview I want to add, that takes care of creating the dom and attaching to it, right after CrudView has rendered the container. the method could be called setEl and return the newly created el.
pro: really flexible cons: adds some complexity to the process of attaching the subview to the dom
4-modify the crudView template and then attach to it, something like this:
<div id="<%= crudId %>">
<div class="header-view"></div>
<div class="table-view"></div>
<div class="form-view"></div>
<div class="bulk-view"></div
</div>
then bulkView.el would be '.bulk-view'
pro: simple approach cons: have to mess around with strings, instead of dealing with the dom
I think it's not so strange what I'm trying to achieve. I just want to add a view to a container view, being as much decoupled as possible, and being able to establish where it should be rendered.
Upvotes: 0
Views: 246
Reputation: 10241
After reading your response to my previous answer I went through and modified my example to hopefully give you an idea of how you can implement a system with named views that allows you to control the ordering as you desire. Let me know if this helps or if you have any questions about how it works.
var viewCtor = Backbone.View.prototype.constructor;
// Assuming we have a reference to the subviews already
var BaseCrudView = Backbone.View.extend({
// This is null for an important reason, see comment in constructor
subViews: null,
// Override the constructor instead of initialize since this is meant to be a base object, so things that
// inherit don't have to remember to call the parent inialize every time.
constructor: function() {
viewCtor.apply(this, arguments);
// It is important this is initialized when instantiating the view rather than in the prototype.
// Backbone's extend() will "copy" the prototype properties of the parent when extending, which really
// just performs an assignment. If this were initialized above in the prototype then all children
// that inherit from that prototype would share the exact same instance of the array/object. If a child
// adds something to the array, it would be changed for all instances that inherit from the parent.
this.subViews = {
header: new HeaderView(),
table: new TableView
};
this.subViewOrder = [
'header',
'table'
];
},
addBefore: function(subView, name, beforeView) {
this.subViews[name] = subView;
var viewLoc = this.subViewOrder.indexOf(beforeView);
if(viewLoc == -1) {
viewLoc = 0;
}
this.subViewOrder.splice(viewLoc, 0, name);
},
addAfter: function(subView, name, afterView) {
this.subViews[name] = subView;
var viewLoc = this.subViewOrder.indexOf(afterView);
if(viewLoc == -1) {
viewLoc = this.subViewOrder.length - 1;
}
this.subViewOrder.splice(viewLoc + 1, 0, name);
},
moveBefore: function(name, beforeView) {
this.addBefore(this.subViews[name], name, this.subViewOrder.splice(this.subViewOrder.indexOf(name), 1));
},
moveAfter: function(name, afterView) {
this.addAfter(this.subViews[name], name, this.subViewOrder.splice(this.subViewOrder.indexOf(name), 1));
},
render: function() {
var that = this;
_.each(this.subViewOrder, function(viewName) {
// Assumes the render() call on any given view returns 'this' to get 'el'
that.$el.append(this.subViews[viewName].render().el);
});
return this;
}
});
var BulkCrudView = BaseCrudView.extend({
inialize: function() {
// Skipping the last parameter causes it to insert at the end
this.addAfter(new BulkView(), 'bulkView');
}
});
With this you could easily extend the BulkCrudView
and modify its subViews
array in initialize to add/insert whatever you want. Though, it'd work just as well to instantiate a BaseCrudView
and work with the view methods. Just whatever feels cleaner and/or floats your boat.
Upvotes: 1