Appendix C. Using jQuery UI with Backbone

One question I frequently am asked is how jQuery UI works with MVC frameworks like Backbone. This definitely is a topic worth discussing, because jQuery UI compliments Backbone quite nicely. The best way to use the libraries is to let Backbone do what it does best—manage an application’s data and views—and let jQuery UI do what it does best—the UI. Let’s look at how to do that.

Note

This guide is intended for readers who have some familiarly with the Backbone library, although I’ll try to provide enough context so that everyone can follow. To learn more about Backbone, you can refer to its documentation at http://backbonejs.org/, or Addy Osmani’s excellent (and free!) book on writing Backbone applications, available at http://addyosmani.github.io/backbone-fundamentals/.

C.1. Building a Backbone view

To show the integration in action, you’ll build a small sample app to manage a grocery list. Your grocery list will have a single piece of functionality: a button that removes individual groceries from the list. The HTML you’ll use to build this is shown here

<ul id="grocery-list"></ul>
<script type="text/template" id="grocery-template">
    <% _.each( groceries, function( grocery ) { %>
        <li>
            <%= grocery.name %>
            <button data-id="<%= grocery.id %>">Remove</button>
        </li>
    <% }); %>
</script>

and here’s the JavaScript you need:

Note

You can play with this example at http://jsfiddle.net/tj_vantoll/H3fHr/.

If you’re not familiar with Backbone, don’t worry too much about the specific syntax used here. Backbone works by separating the model data (in this case, Grocery and GroceryList) from the view logic (in this case, GroceryView). But because we’re concerned about jQuery UI integration, the main thing to focus on is the render() method. Here, render() takes the data in the View’s model (GroceryList) and uses a template to inject the data into the <ul id="grocery-list"></ul>.

Tip

If you don’t understand what the template is doing here, refer to chapter 11 where we discuss templating in more detail.

Because render() is what updates the HTML, it must be explicitly called every time the view’s data changes. In this example, it’s called twice, once after the initial GroceryList is created and again in the remove() method, which is invoked after the user clicks the Remove buttons in the UI . Now that you have an example in place, let’s see how you can add in jQuery UI widgets.

C.2. Adding jQuery UI to the view

Let’s suppose that you want to change your example’s remove buttons to use a jQuery UI button widget with an icon. You could start by selecting elements and invoking the button widget’s plugin:

$( "button" ).button({
    icons: { primary: "ui-icon-closethick" },
    text: false
});

This works initially, but as soon as you remove a grocery item from the list, the buttons are no longer button widgets. Why? Every time you call render(), the entire view is rerendered from scratch; the buttons you initially created are removed as soon as render() is reinvoked.

Because of this, you must put the widget instantiation in the render() method itself. The following initializes button widgets on each of the remove buttons:

render: function() {
    this.$el.html(
        this.template({ groceries: this.model.toJSON() }));

    this.$el.find( "button" ).button({
        icons: { primary: "ui-icon-closethick" },
        text: false
    });
}
Tip

All Backbone views have el and $el properties. el is a reference to the view element’s DOM node (as an HTMLElement), and $el is that same element wrapped in a jQuery object. Because it is a jQuery object, the $el property gives you direct access to all methods on $.fnshow(), hide(), find(), html(), and so forth.

Now, each time this view is rendered, its HTML is replaced and button widgets are instantiated on each of the newly created <button> elements. This approach works, but it can be a bit verbose to manually instantiate widgets in render()—especially in complex views with a lot of widgets. Let’s see how a library we built in chapter 12 can help out with this.

C.3. Using declarative widgets

In chapter 12 you built the declarative widgets library, a simple means of creating widgets through HTML attributes—rather than explicit JavaScript-based instantiation. Moving the configuration to HTML from JavaScript can be elegant, and in my opinion, it works well in MVC frameworks like Backbone.

To see what I’m talking about, let’s add the declarative widgets library to your example. Currently your template is using the following code to create <button> elements:

<% _.each( groceries, function( grocery ) { %>
    ...
    <button>Remove</button>
    ...
<% }); %>

To switch to using declarative widgets, you have to move the option configuration currently in JavaScript into HTML5 data-* attributes on the <button>:

<% _.each( groceries, function( grocery ) { %>
    ...
    <button data-role="button" data-text="false"
        data-icons='{"primary":"ui-icon-closethick"}'>
        Remove
    </button>
    ...
<% }); %>

Here, the data-role attribute tells declarative widgets which widget the markup should become, and the other data-* attributes correspond to button widget options. So data-role="button" tells declarative widgets this should become a button widget, data-text="false" says to set the button widget’s text option to false, and data-icons='{"primary":"ui-icon-closethick"} says to set the button widget’s icons option to {"primary":"ui-icon-closethick"}.

Notice that for options that are objects—in this case, the icons option—the declarative widgets library requires the corresponding HTML attribute be valid JSON. Both the keys of the object must be enclosed in double quotes. So both data-icons="{'primary':'ui-icon-closethick'}" (single quotes around the key) and data-icons="{primary:'ui-icon-closethick'}" (no quotes around the key) aren’t valid options when using declarative widgets.

Now that you have the HTML attributes in place, you need to use it. The declarative widgets library exposes a single enhance() jQuery plugin method, and all you need to do is call it in render():

render: function() {
    this.$el
        .html(this.template({ groceries: this.model.toJSON() }))
        .enhance();
}
Note

You can view the declarative approach to this example at http://jsfiddle.net/tj_vantoll/Y5BRP/.

Notice that you’re not explicitly instantiating any widgets here. The single call to enhance() finds all elements with a data-role attribute—specifically, itself and each of its children—and initializes the appropriate widgets on those elements. Although I personally find this approach elegant, it’s worth noting that neither the JavaScript-based initialization nor the declarative initialization approaches are correct; it’s a matter of personal preference.

Regardless of how you choose to initialize widgets, Backbone’s render() method is the ideal place to do so, as it’s typically the place that HTML is injected into the DOM. With this approach jQuery UI is a nice compliment to Backbone. You can let Backbone handle your data, routing, and views—and let jQuery UI handle the widgets that you need to build your UI.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.164.34