Thursday 28 February 2008

Grails: Hacking paginate tag to make Ajax way

I one thing I miss in Grails is a remotePaginate Taglib (you can vote in Jira), today I'm going to hack paginate tag to make an Ajax paginate.

For this I have various options, I can modify the actual paginated taglib or the option I've chosen, hack with prototype observe the links generated by paginate tag.

To make easy, I've used observe tag, to capture clicks events, let's go with the example of list.gsp:

You have to wrap your list with div for this example 'ajax_wrap' to refresh with Ajax calls.
<div id="ajax_wrap">

<div class="list">
<g:render template="archive" collection="${archiveList}" var="archive"/>
</div>

<div class="paginateButtons">
<g:paginate action="list" total="${Archive.count()}" />
<g:observe classes="${['step','prevLink','nextLink']}" event="click" function="clickPaginate"/>
</div>

</div>
I've used g:observe to capture click events in generated links by g:paginate, and define a function handler clickPaginate that performs the Ajax calls.

Here function handler defnition:
function clickPaginate(event){
event.stop();
var link = event.element();
if(link.href == null){
return;
}

new Ajax.Updater(
{ success: $('ajax_wrap') },
link.href,
{
evalScripts: true
});
}
The Ajax call update the element 'ajax_wrap'.

With this you have an elegant way to make your list paginated effortlessly

Monday 18 February 2008

Grails: Tip for scaffolding required fields

Scaffolding is one of the most useful features of grails and customizing scaffolding it's basic for increase your productivity.

One thing I've customized is render a '*' for required fields, for this you have to install scaffolding templates with grails install-templates.

This is a fragment of the original templates/scaffolding/create.gsp,

props.each { p ->
if(p.type != Set.class) {
cp = domainClass.constrainedProperties[p.name]
display = (cp ? cp.display : true)
if(display) { %>
<tr class="prop">
<td valign="top" class="name">
<label for="${p.name}">${p.naturalName}:</label>
</td>
<td valign="top" class="value \${hasErrors(bean:${domainClass.propertyName},field:'${p.name}','errors')}">
${renderEditor(p)}
</td>
</tr>
<% } } } %>


I'm going to introduce a line, to check if the field has a constraint blank:false, and in this case render as a required field.

if(!cp?.blank) { %><span class="req">*</span><% } %>

The updated version complete:
   props.each { p ->
if(p.type != Set.class) {
cp = domainClass.constrainedProperties[p.name]
display = (cp ? cp.display : true)
if(display) { %>
<tr class="prop">
<td valign="top" class="name">
<% if(!cp?.blank) { %><span class="req">*</span><% } %>
<label for="${p.name}">${p.naturalName}:</label>
</td>
<td valign="top" class="value \${hasErrors(bean:${domainClass.propertyName},field:'${p.name}','errors')}">
${renderEditor(p)}
</td>
</tr>
<% } } } %>

That's all folks.

Monday 11 February 2008

My first Grails plugin

The grails plugin architecture it's fantastic, I've spent one afternoon doing a static content plugin, yeah there is a oficial static plugin, but I use jetty for development and production, and the static content it's uploaded by the users.
With Jetty servlet to server static content is enought for me, and with some configuration parameters, if required can be disabled.

Another advantage is that this way you can protect your static content with the Acegi plugin.

The first step it's create plugin:
grails create-plugin jettystatic
The project looks like a normal grails project, excepts that you have a plugin descriptor file.
In the JettystaticGrailsPlugin.groovy I've updated the web.xml generated:

def doWithWebDescriptor = { webXml ->
log.info "Jetty Static Initializing servlet"
def dir = application.config?.jettystatic.dir
if(!dir){
dir = './static'
}

if(application.config?.jettystatic.ignore){
log.info "Jetty Static: No jetty static servlet inicialized"
return
}

log.info "Jetty Static dir '${dir}' and mapping '${mapping}'"
def mapping = '/resources/*'
def servlets = webXml.'servlet'

servlets[servlets.size()-1] + {
'servlet' {
log.info 'Jetty Static generating <servlet> for static content'

'servlet-name'("jettystatic")
'servlet-class'("org.mortbay.jetty.servlet.DefaultServlet")

'init-param' {
'param-name'("resourceBase")
'param-value'(dir)
}

'init-param' {
'param-name'("dirAllowed")
'param-value'("false")
}

'init-param' {
'param-name'("gzip")
'param-value'("true")
}

'load-on-startup'("1")
}
}

def mappings = webXml.'servlet-mapping'
mappings[mappings.size()-1] + {
'servlet-mapping' {
'servlet-name'("jettystatic")
'url-pattern'(mapping)
}
}
}
The other thing I need, is a taglib to make links to static content, and I'd like that the tag transform from absolute path of static content to a relative web path, this make easy upload content, and server the content uploaded.

def resource = {attrs ->
def url = grailsApplication.config?.jettystatic.absolute.url
def dir = grailsApplication.config?.jettystatic.basepath
def file = attrs.file
def baseUrl = ""

if(dir){
if(!dir.endsWith('/')){
dir = dir + '/'
}
file = file.replaceFirst(dir,"")
}
if(url){
baseUrl = url
}else{
baseUrl = g.createLinkTo(dir:'resources')
}

def env = GrailsUtil.environment
if(env && env == "development"){
baseUrl = g.createLinkTo(dir:'resources')
}


if(!baseUrl.endsWith('/')){
baseUrl = baseUrl + '/'
}
out<< "${baseUrl}${file}"
}
At this point the only thing I have to do is package my plugin (grails package-plugin) to use in any project.

I can't imagine doing something like this in java and spend this time.