You want to include a link to one of your Grails actions (say an AJAX target link) in one of your JavaScript files. What to do?
Well you could just inline your JavaScript in your GSP file, right? Then you can just access all the GSP tags you like and the problem just goes away, right?
<r:script>
console.log("Now hacking on ${assessment.id}");
$.ajax({
type: 'GET',
url: "${g.createLink(action: 'addThreat')}",
data: { assessmentId : "${assessment.id}" },
/* more JS magic here */
});
</r:script>
Well, yes, but then you have other problems (like wanting to take advantage of JavaScript compression and caching, and not to mention the general mental clutter of having all this magic happening in the same file).
I’m not sure how other people handle this situation, but I figure I’d document one pattern for tackling this problem in case you find yourself running up against it.
In my scenario, I have a workspace.gsp file for all the Grails-ey stuff and a workspace.js file which hosts all my jQuery code.
The general pattern I use is to create a namespaced JavaScript object in my GSP file which hosts all the links that I want to consume. This part I actually *do* inline, but there’s not a lot that could be compressed/cached, so I don’t stress about it. So my workspace.gsp file looks like this:
.. Normal GSP stuff here ..
<!-- We inline the values (ids, action links, resource links) we want to use in our JS file -->
<r:script>
var GMARC = {
assessmentId : '${assessment.id}',
threatUrl: '${g.createLink(action: "getThreats")}',
addThreatUrl: '${g.createLink(action: "addThreat")}',
categoryIcon: '${g.resource(dir: 'images/icons', file: 'folder.png')}',
assetIcon: '${g.resource(dir: 'images/icons', file: 'television.png')}'
}
</r:script>
<!-- Next we pull in our JS file, and can reference GMARC.assessmentId, GMARC.assetIcon, etc -->
<g:javascript src="workspace.js"/>
With our GSP file in place, we can now move on to our workspace.js file where we can take advantage of all of those Grails values that we squirrelled away in our JavaScript hash. Here is an extract from my workspace.js file:
$.ajax({
type: 'GET',
url: GMARC.addThreatUrl,
data: { assessmentId : GMARC.assessmentId },
dataType: 'json',
success: function(json, textStatus) {
/* Do JS Goodness here */
},
error: function(xhr, textStatus, errorThrown) {
alert('An error occurred! ' + ( errorThrown ? errorThrown : xhr.status ));
}
});
In the JS above, I can take advantage of the GMARC.assessmentId (line 3) and GMARC.addThreatUrl (line 4) variables I declared in my GSP file, while keeping the JavaScript testable, cacheable and as clean as a whistle.
Well, that’s at least one pattern for cleanly using Grails variables in your JavaScript. Keen to know how other people approach this problem!
This is exactly the reason I wrote the GSParse plugin ( see http://nerderg.com/GSParse ) with the added goodness of inserting links and letting you use variables in CSS as well.
I do like the added testability here for the JS tho.
I’d use HTML5 data attribute (data-*) to store a link and pull the value from HTML in my JS file.
This is what I am doing as well. Working well.
Thanks for posting this. I was looking for something like, as otherwise I sometimes used to make them so many javascript global variables in my gsp.
Links specifically may well be the href attribute of a link you’re Ajax-enabling or the action attribute of a form, etc. In those cases the best thing would be to use the same URL for Ajax & regular behaviour & use request.xhr on the server side if you need to handle them differently. Then your script can simply read the URL from the markup attribute.
As mentioned above the data-* attribute convention in html5 provides a way to do that for more arbitrary data.
Excellent. The html5 approach, sounds very promising. What about legacy browser support? (which in my case is absolutely essential as my enterprise clients are running older versions of Internet Explorer as part of their standard operating environment)
Are people just adopting the html5 data-* conventions and breaking the older html dtds? Or using some kind of Modernizr sensing first up?
The other trick I use is to put the link in an element like the form element, and read the link from that element in JS
I use and a global js var for i18n messages.
Feels ugly to have the code in two places.
I meant a link element (markup mangled in previous post).
And I meant code in three places (the js module, these vars in the head section, the dom)… It’s difficult to enjoy coding in JS…
A similar old blog here http://www.intelligrape.com/blog/2011/01/16/grails-taglib-and-javascript-file/
I use Peter’s plugin (GSParse). It’s working very well and very easy to setup.
You only need to define a URLMapping like “/resource/$path**”(controller: ‘script’, action: ‘parse’) and add your layout/view page necessary js import like . Then you’re able to use gsp tags in js files.
I am trying to do drag and drop with ajax , but can not get posted to the controller the variable