Binding to Views

By james

I’ve landed on something of a simplified View object that can be used for javascript widgetry. The notion really is that the views are just some template
html string that gets plopped onto a page and bound to a model and a controller. This implementation takes that pretty literally. The View expects a model, controller, peer element (where it should ‘paint’ its template), Template, and Binders. Subclasses should just pass the right thing to the View constructor. The TaskView became quite simple when it switched to using the base View:

var TaskView = function(_task, _viewPort){
	View.call(this, {
             model:_task,
             controller:this,
             peer:_viewPort,
             template:TaskView.Template,
             binders:TaskView.Binders
        });
}
TaskView.extend(View);
TaskView.Template = '<input type="checkbox"/><span></span>';
TaskView.Binders = [
	new ModelBinder(new TagNameLocator('input',0), 'checked', '_complete'),
	new ModelBinder(new TagNameLocator('span', 0), 'innerHTML', '_description'),
	new EventBinder(new TagNameLocator('input', 0), 'click', '_handleCheckboxClicked')
];
TaskView.methods({
	_handleCheckboxClicked: function(event){
		if(event.target.checked){
			this.getModel().complete();
		}
	}
});

What’s going on here? The TaskView constructor just calls the super class constructor with the params it wants: model, controller, peer element, template, and binders. Thats a lot of args, and we may be able to help that with some good conventions. But what are all the args for?

  • The model is the data that the view will display.
  • The controller is meant to handle the behavior of the view. User gestures (mouse clicks and such) are ‘bound’ to methods on the controller via EventBinders.
  • The peer is the html element the view should “paint” into.
  • The Template is..well…the “template” or “mock” markup for the view.
  • The binders are what bind behavior and data to the view. ModelBinders are for binding bits of the model to bits of the view. EventBinders are for binding user gestures (html events) to methods on the controller.

OK, but how does it get put together? The View constructor will set the innerHTML of the peer element to the value of Template and then iterate over the binders telling them to ‘bind’ to the peer. Binders use Locators to find the html elements to bind to. The TagNameLocator will find elements with a particular tagName. Optionally, it will take an index which would make the locator only return the N-th element with that tagName where N is the index provided.

So, in the TaskView, there are 2 ModelBinders and 1 EventBinder. The first ModelBinder binds the ‘checked’ attribute of the first ‘input’ element to the ‘_complete’ property of the model. The second ModelBinder binds the ‘innerHTML’ attribute to the ‘_description’ property of the model. In both cases, the Binder creates a ModelBinding object which becomes an observer of the model and will keep the model and html element in sync. The EventBinder binds a ‘click’ event on the first input to the “_handleCheckBoxClicked” method on the TaskView (which is serving as the controller here).

Not too bad I’d say. Needs some work still though. My first thought is to
take the template and binders out of the call to the super constructor and make them smart defaults. If the super class constructor doesn’t have them in the call, it can look on the subclass for a ’static’ Template and Binders fields. Another thing I’d like to do is have the ModelBinder support methods as well as properties, so it could bind the return value of a method (that way we can bind to ‘getDescription’ instead of ‘_description’).

Maybe tomorrow.