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 foo
when 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.
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.
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:
tbone.createModel('counter', function () {
return {
intervalId: 0,
value: 0
};
}).singleton();
Our counter
model contains two attributes, intervalId
and value
.
We will be using the setInterval method to increment the counter, so intervalId
will store the interval id and value
will simply store the counter value.
Next, we will create a view for controlling (Start, Stop, Reset) the counter:
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:
<div class="container">
<h1>TBone Counter Example!</h1>
<hr>
<div tbone="inline counter">
<h3>Counter - <%=counter.value%></h3>
</div>
<div tbone="view counterControl">
<button class="btn" id="start">Start</button>
<button class="btn" id="stop">Stop</button>
<button class="btn" id="reset">Reset</button>
</div>
</div>
So what's happening? When you click the Start button, the value
attribute in the
counter
model 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:
tbone.createModel('timer', tbone.models.base, {
depends: {
'*': ['counter']
},
calc: function(state) {
var count = state.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 timer
, we tell it TBone that this model depends on the counter
model. Thus, anytime the counter
model changes, the calc
method will fire,
and recalculate the attributes (seconds & minutes) for the timer
model. Pretty
neat, huh? Well, let's see it in action!
Just update your template to add the timer:
<div tbone="inline timer">
<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.