Monday, December 27, 2010

Grails and dynamically generated JavaScript


I can already hear you guys explaining in thousands of words why such a thing is inherently bad. I understand that, I even encourage others to do static JavaScript instead of dynamically generated because it is simply better this way. There are however times when having such a thing comes incredibly handy. When?

- whenever you need to pass on some dynamically-generated variables (like URLs for example) and you only want to do it once
- whenever the code depends on some configuration option

There are more examples but the general rule in my opinion is: as long as you keep your JavaScript generation marginal you're on the safe side.

Let's see how we can implement such a thing.

No doubt there's more than one option to do that. The way I've chosen is in my opinion best because it separates the actual program logic from the generation of JavaScript. So what I've done was I've created a separate controller, called ScriptsController, that has some actions on it (usually actions that provide configuration options for the static part of the JavaScript code)
class ScriptsController {
def home = { }
In this case there are no variables passed on from controller to the view but as you can imagine there are possibilities to do that.

I'm using the outcome of this view as
src="${g.createLink(controller: 'script', action: 'home')}.js">
and instead of rendering plain HTML code from my GSP I'm rendering JavaScript from /views/scripts/home.js.gsp:
<%@ page contentType="text/javascript"%>
var HomeLink = "${g.createLink(controller: 'home', action: 'index')}";

Because I am a very lazy man and the situation where I need the same set of dynamically-generated content in different views for one controller I've decided to create a tag for it:
class ExampleTagLib {
static namespace = 'g'

def script = { attrs ->
def link = g.createLink(
controller: 'scripts',
action: ?: controllerName)
out << """<script type="text/javascript" src="${link}.js"></script>"""
So here I ask for the current controller's name (which is btw nicely preprocessed by Grails) and use that instead of passing in the same parameter value every time I need it even if the dynamic portion is named the same as the controller that called it. A convention, so to speak.

So here's the simples usage example:
<g:script />
What this will do it will look-up the current controller name and render a script tag that will access the outcome of an action with the same name as the current controller as the src. This is exactly the same as the first version if called from HomeController only shorter.

If however you need to get the script breaking this convention (for whatever reason like common scripts maybe) then the name parameter comes in handy
<g:script name="common" />
This way the common script will be requested.

As usual here's a ready-to-use example.

I hope this will help you out to write less dynamically-generated JavaScript but when you'll have to do it your code will be better structured.

No comments: