Relieve Event Binding Aches in Backbone.js
by March 6, 2013

Filed under: Performance Monitoring

AppNeta no longer blogs on DevOps topics like this one.

Feel free to enjoy it, and check out what we can do for monitoring end user experience of the apps you use to drive your business at www.appneta.com.

Dan Riti

Dan Riti

Hey! I’m Dan Riti, the latest addition to the TraceView team! I recently joined as a full stack engineer in the Providence office, and it’s been an exciting past couple of weeks learning about the world of tracing distributed web applications.  I’ve been working primarily on improving our Trace Details page, which provides an in-depth look into the performance of individual requests. The major challenge in our front end web application is managing data dependencies (i.e. update foowhen bar changes) while also maintaining a rich user experience. We deal with a lot of data, from numerous sources, and all with unique dependencies. Thus, we have developed TBone, an open-source extension to Backbone.js to help us out.

Enter TBone, a Backbone.js extension

TBone removes the complexity of manually managing data dependencies in Backbone, enabling “live” templates as well as functions that automatically re-execute when the data they reference changes.

It’s designed to build on top of existing backbone-powered apps and scale with the application, enabling simple re-use of data throughout your application without the need to tell the page what to update when that data changes.

At AppNeta, we’ve used TBone to eliminate a set of custom page events corresponding to interactions like “refresh data” and “add filter.” With a large application, it becomes difficult to manage what exactly needs to be refreshed when something changes. While Backbone is a critical step toward reducing this complexity, TBone enables us to do so without even thinking about event binding – every view and model stays in sync by design and without unnecessary work.

Digging into TBone

Let’s implement a sample application that demonstrates some of the “automagic” of TBone. For this example, we will build a simple counter that increments every second, implement some simple controls (Start/Stop/Reset) and finally demonstrate data dependency by introducing a model that depends on the counter.

  • Try out this example on JS Bin!
  • Or view the code on Github.
  • Or clone the repo: git clone git://github.com/danriti/tbone-counter-example.git

Now that you’re looking at the code, let’s get started!

First, we will create a model to represent the counter using TBone:

1
tbone.createModel('counter').singleton();

Our counter model needs two attributes, intervalId and value. We will be using the setInterval method to increment the counter, so intervalId will store the interval id andvalue will simply store the counter value. So let’s set them:

1
2
3
4
tbone.set('counter', {
    intervalId: 0,
    value: 0
});

Next, we will create a view for controlling (Start, Stop, Reset) the counter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
tbone.createView('counterControl', function() {
    var self = this;
    var startBtn = self.$('button#start');
    var stopBtn = self.$('button#stop');
    var resetBtn = self.$('button#reset');
    // Initially disable the stop button.
    stopBtn.attr("disabled", true);
    // Event handler for the start button click.
    startBtn.click(function() {
        // Set button states.
        startBtn.attr('disabled', true);
        stopBtn.removeAttr('disabled');
        // Increment the counter every second.
        var intervalId = setInterval(function() {
            // Lookup the counter model value.
            var i = tbone.lookup('counter.value');
            // Increment the counter model value.
            tbone.set('counter.value', i+1);
        }, 1000);
        tbone.set('counter.intervalId', intervalId);
    });
    // Event handler for the stop button click.
    stopBtn.click(function() {
        // Set button states.
        stopBtn.attr('disabled', true);
        startBtn.removeAttr('disabled');
        // Fetch the interval id and stop incrementing the counter.
        var intervalId = tbone.lookup('counter.intervalId');
        clearInterval(intervalId);
    });
    // Event handler for the reset button click.
    resetBtn.click(function() {
        // Reset the counter value to 0.
        tbone.set('counter.value', 0);
    });
});

Finally, we will bind our model and views to our template:

1
2
3
4
5
6
7
8
9
10
11
12
<div class="container">
<h1>TBone Counter Example!</h1>
<hr />
<div>
<h3>Counter - <%=counter.value%></h3>
</div>
<div><button id="start" class="btn">Start</button>
 <button id="stop" class="btn">Stop</button>
 <button id="reset" class="btn">Reset</button></div>
</div>

So what’s happening? When you click the Start button, the value attribute in the countermodel is being incremented every second. Everytime the model changes, the template is forced to update to display the latest data. As you play with the other controls, you will notice that template responds accordingly. This is an example of “live” templating in TBone.

So now let’s make things interesting. Let’s introduce a new model called timer, however let’s assume it is depends on the counter model to keep track of time. In TBone, this is pretty simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tbone.createModel('timer', function() {
    var count = tbone.lookup('counter.value') || 0;
    var rval = {};
    // Calculate seconds and minutes.
    var seconds = count % 60;
    var minutes = Math.floor(count / 60);
    // Pad the left side (i.e. 09 instead of 9) of the seconds
    // and minutes.
    rval.seconds = _.string.pad(seconds, 2, '0');
    rval.minutes = _.string.pad(minutes, 2, '0');
    return rval;
}).singleton();

When creating the timer model, we tell TBone that this model depends on the countermodel by performing a tbone.lookup within the anonymous function we pass in. TBone now knows a dependency exists between the two models and handles all of the heavy lifting for us. Thus, anytime the counter model changes, the anonymous function will execute and the timer model attributes (seconds & minutes) will be recalculated. Pretty neat, huh? Well, let’s see it in action!

Just update your template to add the timer:

1
2
3
<div>
<h3>Timer (MM:SS) <%=timer.minutes%>:<%=timer.seconds%></h3>
</div>

Now the newly created timer model will update seemlessly as the counter model increments away!

Isn’t TBone delicious? If you’d like seconds, be sure to check it out on Github.