Dynamic Tag Libraries
Grails has a wide range of custom tags built in for both JSP and GSP (see the
Tag Library Reference here), however Grails also allows the creation of simple, logical, and iterative custom tags through its simple dynamic tag library mechanism.
Got a tag that you would like to share with the rest of the Grails developers? Contribute a tag!
The benefit of Grails' tags is that they require no additional configuration, no updating of TLD descriptors, and can be auto-reloaded at runtime without a server restart. This makes developing tags feel as if you were just developing another part of the view and increases their usefulness tenfold.
Grails Tag libraries require a little extra work to integrate into JSP and are more seamlessly integrated into GSP because of its dynamic nature. See the last section for how to use grails tags from JSP
Simple tags
To create new tags, run "grails create-tag-lib" or manually create a new class in "grails-app/taglib/" with a name ending in "TagLib". (If you use "ApplicationTagLib.groovy", you will no longer be able to use the standard grails tags.) To create a simple tag add a new closure property that takes 1 argument which are the attributes of the tag:
def includeJs = { attrs ->
out << "<script src='scripts/${attrs['script']}.js' />"
}
To call your tag your from a GSP page use the "g" prefix followed by the tag property name:
<g:includeJs script="myscript" />
Tag namespaces
By default, tags are added to the default Grails namespace and are used with the "g" prefix in GSP pages. However, you can specify a different namespace by adding a static property to your TagLib file:
class MyTagLib {
static namespace = 'my' def someTag = { attrs ->
…
}
}
The tags in this tag lib must then be referenced from GSP pages like this:
<my:someTag name="..." />
where the prefix is the same as the value of the static {{namespace}} property. Namespaces are particularly useful for plugins.
The following is subject to change
Since Grails 1.0 tags within namespaces can be invoked as methods. In versions prior to Grails 1.0 if you placed a tag library within a namespace you could not invoke the tags as methods from GSP, controllers or other tag libraries.
To do this in Grails 1.0 you use the namespace as a prefix to the method call:
out << my.someTag(name:"foo")
This works from GSP, controllers or tag libraries
Logical tags
You can also create logical tags by using a closure syntax that takes 2 arguments, the attributes of the tag and the body of the tag as a closure:
def isAdmin = { attrs, body ->
def user = attrs['user']
if(user != null && checkUserPrivs(user)) {
out << body()
}
}
The tag above checks if the user is an administrator and invokes the body of the tag if he/she is:
<g:isAdmin user="${myUser}">
// some restricted content
</g:isAdmin>
You can pass an argument to body() when you invoke it. This argument will be available as "it" in your GSP. Currently (Grails .5.6), you can't change the name of "it" and you can't pass "it" or any of its properties to another dynamic tag. For now, you can work around this using the request object:
def userForUsername = { attrs, body ->
def username = attrs['username']
def user = // look up User, perhaps from database
if(user != null) {
request.setAttribute('curUser', user)
out << body()
}
}
And in the GSP:
<g:userForUsername username="${myUsername}">
<g:link action="show" controller="user" id="${request.getAttribute('curUser').id}">
Show User
</g:link>
</g:userForUsername>Iterative tags
And of course you can create iterative tags:
def repeat = { attrs, body ->
def i = Integer.valueOf( attrs["times"] )
def current = 0
i.times {
// pass the current iteration as the groovy default arg "it"
// then pass the result to "out" to send it to the view
out << body( ++current )
}
}
To call your iterative tag do the following:
<g:repeat times="3">
<p>Repeat this 3 times! Current repeat = ${it}</p>
</g:repeat>Markup building in tags
Grails provides a special method that allows building of markup (a common usecase in tags). To do so you invoke the 'mkp' method passing a closure with the markup you want rendered:
def dialog = { attrs, body ->
def mkp = new groovy.xml.MarkupBuilder(out) //this line will be unnecessary in versions of Grails after version 1.2
mkp {
div('class':'dialog') {
body()
}
}
}see also
Dynamically add 'mkp' instance to TagLib if it is really realised in version 1.2
Tags as method calls in GSP
GSP tags can also be used in Groovy expressions in the GSP page. For example, the hasErrors tag can be used normally as a tag like this:
<g:hasErrors bean="${book}" field="title">
<span class='label error'>There were errors on the book title</span>
</g:hasErrors>
Or as a method call, like this:
<span id="title" class="label ${hasErrors(bean:book,field:'title','errors')}">Title</span>
The last argument of the method is taken as the body of the tag. Or you can pass a closure that returns a string:
<%=
hasErrors(bean:book,field:'title') {
'errors'
} %>
Note that, although they look like method calls, dynamic tags cannot return values for use in the GSP page. They are intended to write directly to the output.
Using Grails tag libs from JSP
To use a Grails taglib definition in JSP you can use the JSP "invokeTag" tag which will call a tag defined in the Grail tag library:
<g:invokeTag name="includeJs" script="myscript" />
<g:invokeTag name="isAdmin" user="${myUser}">
// some restricted content
</g:invokeTag >
<g:invokeTag name="repeat" times="3">
<p>Repeat this 3 times! Current repeat = <c:out value="${it}" /></p>
</g:invokeTag>
If you want your tags to appear like normal JSP tags, here's what you need to do:
1) Create a new java class that sub classes the Grails org.codehaus.groovy.grails.web.taglib.jsp.JspInvokeGrailsTagLibTag class and calls the "setName()" method in the constructore of your new class passing the name of your tag:
package com.mycompany.taglib;
public class IncludeJsTag extends JspInvokeGrailsTagLibTag {
public static final String TAG_NAME = "includeJs";
public IncludeJsTag() {
super.setName(TAG_NAME);
}
}
2) JSP requires declarative tag library definition files (TLD) for each tag, to do this modify the "web-app/WEB-INF/tld/grails.tld" file and add the necessary entries that point to your class:
<tag>
<name>includeJs</name>
<tag-class>com.mycompany.taglib.IncludeJsTag</tag-class>
<body-content>JSP</body-content>
<variable>
<name-given>it</name-given>
<variable-class>java.lang.Object</variable-class>
<declare>true</declare>
<scope>AT_BEGIN</scope>
</variable>
<dynamic-attributes>true</dynamic-attributes>
</tag>
3) You can then call your tag from JSP like a normal JSP tag:
<g:includeJs script="myscript" />