Building reuseable interfaces with Backbone.js
For the last few months I’ve been working on Projector, my first large scale Backbone project. I have to admit: I’m in love with Backbone.View. I’m able to create a view class, add in some business logic, effortlessly attach event handlers to elements within that view, and attach an instance of that view to an element on the page.
I wanted something like an iPad popover for the “Add Story” interface. After failing to find what I was looking for as a jQuery plugin, I figured I’d just write my own. But then I thought about what I was about to do: Define a new function that would take an element, setup some click handlers, position the popover where I need it, and show or hide the popover. The same foundational work I’m doing all the time in my Backbone views! I also knew that this wouldn’t be the only place in the app that I’d want a popover.
Going the Backbone.View route would make manually managing my popovers after they were created a lot easier. jQuery plugins typically have you call the function you need modified again against your elements, and supply a string argument signifying what you want changed. For instance, if I wanted to disable a jQuery Draggable element after creating it, I’d need to call: $( “.selector” ).draggable( “option”, “disabled”, true ). I’d much prefer just calling disable() on a draggable’s view instance.
Defining the base class
The base class had to do the following:
- Initialize with a reference element (where to position the popover)
- Render a wrapping element that creates the floating element and the arrow pointing to the reference element
- Render the template of inherited view inside the wrapping element
- Respond to toggle(), show() and hide()
- Directly position the popover and the arrow under the reference element
Backbone views are initialized with a JSON object that usually contains an element to attach to (el) and a model instance. The popover doesn’t care about either of these. It creates an element for you and doesn’t depend on any model. Your inherited classes, however, might want a model reference.
The popover’s initialize function remembers the reference element that was supplied and calls an internal _render() function, which renders the wrapping element and then calls render(), which the subclass should define. The internal render method sets this.content to be the content area of the view’s element - I don’t want subclasses to touch the wrapping elements.
To use the popover, a parent view instantiates the popover during its initialization, and then calls toggle() whenever the click event on the reference element is fired.