Ivy - Bound JavaScript
I recently was working on a side project, and grew frustrated with the hoops required to get simple things done. As a result, I thought I would share with you a little about Ivy, a small library I wrote to make my life easier.
Unclear code is tedious. It hurts the eyes, brain, and heart. Unclear code leads to bugs, frustration, and keyboard pounding wrath. Ivy introduces just enough functionality to disentangle the DOM from your code so that your HTML focuses on presentation, while your JavaScript focuses on business logic.
Clarity
Clear code focuses on the problem at hand. Relying on CSS selectors in your JavaScript forces you to depend on logic and structure elsewhere. What is the role of button
below?
<a class='button orange'>Buy Now!</a>
Change the HTML, and it may break the JavaScript if some piece of code relies on the button
class. Perhaps the code relies on the element being an anchor tag. The intention behind this is unclear.
Ivy attempts to eliminate this confusion by making the interaction between HTML and JavaScript explicit.
<a class='button orange' data-bind='on: click buy'>Buy Now!</a>
Now the CSS classes are explicitly for styling. The JavaScript no longer needs to rely on them for finding where to bind handling. This may look verbose, but consider that your JavaScript no longer needs to query for this element, and your HTML no longer needs classes and IDs to support JavaScript behavior.
Satisfaction
Binding events is tedious. Updating HTML when data changes is tedious. Parsing form values is tedious. Tedium is a drains the satisfaction from programming. Ivy will not make writing an order form fun, but it will take some of the tedium out of the process. Consider the following example where we will let people buy some shirts, and see how much they will cost.
<form data-bind='on: submit sendOrder' id='example-html'>
<div>
<label>
Shirt Count: <input data-bind='value: quantity'>
</label>
</div>
<div>
<label>
<input type='checkbox' data-bind='checked: nextDay'/>
Next Day Shipping
</label>
</div>
<hr/>
<dl>
<dt>Sub Total</dt><dd data-bind='text: subTotal'/>
<dt>Tax </dt><dd data-bind='text: tax'/>
<dt>Total </dt><dd data-bind='text: total'/>
</dl>
<input type='submit' value='Buy Now'/>
</form>
We need to make sure that the quantity is a number, that totals update whenever the quantity or shipping changes, and that the order gets sent when the form is submitted. How does Ivy help us? It will take care of all the data binding for us, observe:
function OrderForm(shirtCost, nextDayCost, taxRate){
this.quantity = Ivy.attr(1, positiveInteger);
this.nextDay = Ivy.attr(false);
// Computed Sub Total:
this.subTotal = Ivy.fnWith(this, function(quantity, nextDay){
if (this.quantity == 0) return 0;
var shipping = (nextDay.get() ? nextDayCost : 0);
return shirtCost * quantity + shipping;
});
// Computed Tax:
this.tax = Ivy.fnWith(this, function(subTotal){
return subTotal * taxRate;
});
// Computed Total:
this.total = Ivy.fnWith(this, function(subTotal, tax){
return subTotal + tax;
});
// Input Format:
function positiveInteger(string){
var integer = parseInt(string, 10);
return integer < 0 ? 0 : integer;
}
}
OrderForm.prototype.sendOrder = function(){
var orderData = {quantity: this.quantity, nextDay: this.nextDay};
var json = JSON.stringify(orderData);
alert("Sending order data: "+ json);
};
// Bind the form assuming shirts cost $12.00,
// next day shipping is $5.00,
// and the tax rate is 4%
Ivy.bindDom(document.body, new OrderForm(12, 5, 0.04));
The OrderForm is a plain JavaScript object with no notion of the HTML it is bound to. Ivy’s attributes and bound functions keep track of the data’s dependencies, and update them when needed.
And Yet
Ivy is quite young, it has progressed to the point where it solves my problem, but there is plenty to do still. Swing by the Ivy site for examples, documentation, and tests. Feel free to poke around the source on Github if you are curious.
Of course Ivy isn’t for everyone, so by all means take a look at Backbone, Ember, Knockout, or Angular, they are all great projects with different trade offs and approaches.