Declarative Views for Backbone.

Go from this:

<div>
  <%= locales.translate('welcome') %>
</div>
<% if (model.get('visible')) { %>
<div>
  <img src="<%= model.photo_url %>"/>
  <a href="<%= model.get('url') %>">
    <span><%= escape(model.get('first_name')) %></span>
    <span><%= escape(model.get('last_name')) %></span>
  </a>
  <p><%= truncate('bio') %></p>
</div>
<% }; %>
        

To this:

<div data-translate="your_profile"/>
<div data-show="visible">
  <img data-src="photo_url"/>
  <a data-href="url">
    <span data-escape="last_name"/>
    <span data-escape="first_name"/>
  </a>
  <p data-html="bio"/>
</div>
          
Overview

At only 949 bytes (minified), Quilt is a lightweight library offering a simple approach to building views and behavior in Backbone applications. It leaves HTML pure, attaching complex behavior in distinct modules behind the scenes. As much a pattern as a library, Quilt will clean up your view templates and business logic, making your application more maintainable and better composed.

Using Quilt, you build small pieces of functionality that get attached to DOM elements declaratively within your HTML. These Quilt "patches" are usually associated with a model or collection, and changes are synced automatically to the DOM. Because Quilt isolates UI behavior and view logic, writing and maintaining tests becomes a lot easier.

Out of the box Quilt comes with a small library of core patches for common problems. Included are patches for showing and hiding elements in various situations, updating DOM text and attributes safely, and a few others. More will be added with time. You are encouraged to share your own patches.

data-ref

The safety and convenience of element IDs, but scoped to the current view. Using class selectors to find view elements conflates style with business logic. IDs have to be globally unique. data-ref provides a convenient way to declare what elements are relavent to your view and UI logic.


<div data-ref="handle">Find me in your view as @$handle.</div>
          
data-show

Show or hide an element when an attribute is true of false.


<div data-show="visible">Show me when my model's visible attribute is truthy.</div>
          

<div data-hide="visible">Hide me when my model's visible attribute is truthy.</div>
          
data-html

Render an attribute as HTML. If @model.get("description") contained HTML:


<div data-html="description"><b>I'm html.</b></div>
          
data-escape

Escape any HTML and render an attribute as text. If @model.get("description") contained HTML it will be stripped:


<div data-escape="description">I'm html.</div>
          
data-attrs

Update an elements attribute when a model attribute changes.


<img data-attr='{"src": "image_url"}'/>
          
Source

The source for Quilt is hosted on github. Please give it a read before trying anything serious as it will greatly improve the utility you gain from the library. The annotated source is also provided for easier reading. Supermodel is available for use under the MIT software license.

Loose Coupling and Modularity

In order to promote reusability, Quilt plugins are instantiated through html data attributes. This allows developers to create entirely new functionality without writing any javascript. For example, the following snippet renders an attribute from the model as html and updates it on change.


var Html = Quilt.View.extend({

  initialize: function(options) {
    this.attr = options.attr;
    this.model.on('change:' + this.attr, this.render, this);
  },

  render: function() {
    this.$el.html(this.model.get(this.attr));
    return this;
  }

});
          

To expose this functionality to templates in Quilt views, a property is attached to Quilt.attributes.


Quilt.attributes.html = function(el, options) {
  return new Html({el: el, attr: options});
};
          

The attribute can now be used in a view, provided that it inherits from Quilt.View.


var View = Quilt.View.extend({
  template: function() {
    return '<p data-html="body"></p>';
  }
});
          
Quilt.attributes

The extension point for declarative attributes. All handlers should be attached as functions here. Each handler is provided with the element to extend (where the data attribute was found), the value of the attribute (parsed as JSON via jQuery.fn.data), and is called with the parent view as context.


// Set a reference to a specific element on the parent view.
//
//    <p data-ref='body'></p>
//
Quilt.attributes.ref = function(el, options) {
  this['$' + options] = $(el);
};
          

Note: Dashed data attributes will be changed into their camel-cased counterpart before use. For instance, data-foo-bar will use Quilt.attributes.fooBar as a handler.

Quilt.View

Custom view class that handles extension by data attributes.

model/collection

Quilt views follow the convention that models and collections will be passed in via the model and collection properties. Any other models or collections created by the view are its own responsibility and should be cleaned up manually during dispose.

template

A function that takes a data object as an argument and returns rendered html. Defaults to null.


var View = Quilt.View.extend({
  template: _.template(text)
});
          
views

An array of child views, to be cleaned up on dispose.


var View = Quilt.View.extend({
  render: function() {
    Quilt.View.prototype.render.apply(this, arguments);

    this.views.push(new Child().render());

    return this;
  }
});
          
render

After cleaning up old views, use template to generate html and then replace the element's contents with it. Then, search for appropriate data attributes and call their respective handlers. If a handler returns an instance of Quilt.View, it will be rendered and stored in views.

dispose

When you no longer have any use for a view, you should call dispose. It will clean up any event handlers on model and collection and dispose of each of its child views (stored in views). It is assumed that all handlers will be attached with the view as context.


// Will be cleaned up by quilt.
this.model.on('event', this.handler, this);
this.collection.on('event', this.handler, this);

// Will *not* be cleaned up by quilt.
this.model.on('event', this.handler);
this.collection.on('event', this.handler);
this.customProperty.on('event', this.handler, this);