opensas
opensas

Reputation: 63405

advice on how to design an extendable backbone view

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

Answers (1)

Justin Warkentin
Justin Warkentin

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

Related Questions