Wednesday, January 21, 2009

Update to Growl4Rails

Growl4Rails is now production ready. I've added the ability to show multiple growls at once, as well as improved support for IE. Here's a screen shot of the multiple Growls:



Unfortunately, the multiple growls thing was an entire rewrite of the code, so the usage is quite different.

Now, when you setup the includes, you specify default duration and max number of growls to show. These arguments are optional and will default to 3000 milliseconds and 3, respectively.


<%= growl4rails_includes(3000, 5) %>

Also, when showing the growls, the args are now a single hash, with named keys:

<script type="text/javascript" language="javascript">
Growl4Rails.showGrowl({
image_path:"/images/download.png",
title:"Foo Bar.pdf",
message:"File is ready for download."
});
</script>


When you want to handle the click event, you can wire up the event like so:

var growl_id = Growl4Rails.showGrowl({
image_path:"/images/download.png",
title:"Foo Bar.pdf",
message:"File is ready for download."
});
document.observe(growl_id + ':clicked', function(event) {
console.log('Growl %s was clicked.', Event.findElement(event).id);
});

Again, it's open source, so if you want to contribute, or if you have any feedback, you can check out the project on GitHub.

Saturday, January 17, 2009

Javascript Templating

Writing HTML with Javascript has always been a necessary evil, and for me a painful thing to look at in code. Usually we have to do this when we've got some set of data in a collection on the client, and we need to loop through the collection and build HTML.

Back in the day, before I was privy to Javascript templating, I'd do this:


var myArray = ['John', 'Steve', 'Bill'];
var myHTML = '';
for(i=0;i<myArray.length;i++) {
myHTML += '<div>Hello, my name is ' + myArray[i] + '.</div>';
}
var container = document.getElementById('myContainer');
container.innerHTML = myHTML;

There's a few things wrong with this. It's everything bad about the close intermingling of two different languages, or having one language write the other. It's also extremely inefficient. The concatenation of the string results in 3 additional strings allocated during each iteration. And finally, what if my data structure gets more complicated (which in this case, there's no doubt it will), and the array becomes multi-dimensional, then this code gets even more gnarly.

Today, with the arrival of Javascript frameworks like Prototype, jQuery and Dojo, doing this work becomes not only trivial, but much better performing. I'm going to use Prototype for my examples here, but you'll find the other frameworks only differ slightly in semantics.

//use a more descriptive structure
var myData = [{name: 'John'}, {name: 'Steve'}, {name: 'Bill'}];

//Template is a Prototype class
var template = new Template('<div>Hello, my name is #{name}.</div>');

//each is a handy Prototype array extension
myData.each(function(item) {
//the $('foo') syntax is Prototype shorthand for getElementById.
$('myContainer').insert(template.evaluate(item));
});

Definitely a much cleaner approach here. With the old way of doing things, as the complexity of what you have to display grows, the complexity of the code is exponential. However, with the template approach, complexity is closer to linear.