Tuesday, 21 April 2009

Using JavaScript with Ruby on Rails

Okay, this is a blog about Ruby and Rails, however, there are occasions when you want to do some processing at the client end, and JavaScript is the usual language to do that with. It is pretty straightforward to add JavaScript to your Rails web application. The strategy I employ is to write a static web page with the script on it, and get that working, and then transfer that to the Rails view.

One imortant time to use JavaScript is for user-defined code. You can save the code to your database, for use later. But rather than invoke it in Ruby on your server (which is going to be a big security risk), use JavaScript on the client machine instead. Here are a couple of examples that I have used.

Moving the focus
This script will more the focus to the first text field or text area form element on the page (after checking that there is a form). I have this in my application-wide layout, so the focus gets set for every form in my application.
<script>
// Sets the form focus to the first element found in forms[0] that
// is a textfield or text area
function setFocus() {
// Bail if no form on page
if (document.forms[0] == null) return;

// Iterate though elements
for (var i = 0; i < document.forms[0].elements.length; i++) {
e = document.forms[0].elements[i];
if ((e.type == "text") || (e.type == "textarea")) {
e.focus();
break;
}
}
}

</script>

<body onload="setFocus()">


Two buttons to add default values
First some background: This was for a table of laboratory samples. Occasionally, analysis is not required, and I wanted a quick way to note that in the comments text area. This helper method adds a link; click on it and the text is added to the text area.
def not_required_button
js = "sample_comments.value += \'Not required.\'"
"<a onclick=\"#{js}\" class=\"not-button\">[n/r]"
end

The method builds a little string of JavaScript, and then uses that in some HTML. I set up the CSS class like this:
a.not-button {
color: silver;
font-size: 10px;
cursor: pointer;
}

The next example is a bit more complicated. Again this was for the table of laboratory samples, and there are several different types of samples requiring different fields. To handle that, I created an array of hashes; each hash held the information for one field (for example, how to display it). The edit and show actions iterate through the array, and display the fields based on the values in the hash. Several fields usually require the next number in a sequence, so what I wanted was a button the user could click to add that number. This helper method does just that:
def count_button field
return '' if !field[:count]
old_value = Sample.maximum field[:column_name]
js = "#{sample_#{field[:column_name]}}.value = \'#{old_value.nil? ? 1 : old_value + 1}\'"
"<input type=\"button\" onclick=\"#{js}\" value=\"\#\"}>"
end

The method accepts the hash for the field as a parameter. If the value of count is not set to true, then no button is wanted, and the method returns. Otherwise, it searches the Sample table for the highest value already there, and adds one to that (or uses one as the value if there are none there yet). Then it builds the JavaScript, in the string js. The tricky bit is working out what JavaScript will expect the element to be called; the convention is class_column, all in lowercase and underscored (you can confirm that by looking at the HTML source of your output page, of course). The JavaScript code is in turn is put into the HTML code for the button.

Struggling with Ruby: Contents Page