Presenter: Dave Klein
Custom Tags are Valuable
- build reusable components
- keep code out of pages
- make pages more readable
- even putting presentation/html code into custom tags can make the page more readable
- encapsulate domain knowledge
- easy for page designers to use without knowing much about the domain model, etc.
JSP Custom Tags are Painful
- create handler class for each tag
- implement one of several interfaces
- implement interface’s methods
- <pure_evil>define a TLD</pure_evil>
- add a page directive for each TLD
- great once they exist, but because they’re a pain to create people avoid them and don’t get the benefit
- JSP custom tag for hello wolrd is 2+ pages of code, equivalent GSP tag is about 6 lines
The Power of JSP Without the Pain
- convention over configuration
- no tld, no xml
- no interfaces to implement
- a TagLibrary is a single groovy class
- can contain multiple tags within this class
- each tag is a closure
- don’t need to declare within the page
- if it’s in the project, it’s available on every page
GSP: A Quick-Start Guide
- from project directory run grails create-tag-lib TagName
- open the new taglib at grails-app/taglib/GQuickTagLib.groovy
- add a closure for each tag
- add some tests to test/unit/GQuickTagLibTests.groovy
- use the tag(s) in your GSP pages
- blog about how awesome GSP tags are
What you can do in a GSP tag
- accept and use attributes
- accept and conditionally use a body
- access your domain model including all the GORM methods
- call service classes
- call other tags
- other tags are called as methods from within a tag
- access the session, request, and response
Implicit Objects in a GSP Tag
- session (GrailsHttpSession)
- request (HttpRequest)
- response (GrailsContentBufferngResponse)
- out (GSPResponseWriter)
Example Tag — Output Groovy Group List
- def groupLinks = {
def groups = GroovyGroup.list()
out << “<br/><ul>”
groups.each { group ->
out << “<li><a href='”
out << createLink(action:’show’, id:group.id)
out << “‘>${group}”
out << “</a></li>”
}
out << “</ul>
} - call tag as <g:groupLinks />
Example Tag With a Body
- def ifLoggedIn = { attrs, body ->
def user = session.use
if (user)
out << body()
} - <g:ifLoggedIn>
info for logged in users goes here
</g:ifLoggedIn> - note that in the tag code, the first parameter will be treated as a map of attrs so even if you’re not using attrs, but you’re using the body, still need to have a dummy parameter for attrs in order for things to work correctly
Using Custom Tags Instead of <g:if>
- def buttonBar = {
def user = session.user
out << “<div class=’buttons’>”
if (user) {
// output buttons for logged in users here
if (user.isAdmin()) {
// output admin buttons here
}
} else {
out << “<input type=’button’ value=’Login’ />”
}
out << “</div>”
} - call tag as <g:buttonBar />
Testing Tags
- very easy
- tags when called as methods return a string
- TagLibUnitTestCase makes it even easier
- includes mocks for
- session
- request
- response
- includes mocks for
- includes other mocks from GrailsUnitTestCase
A Sample Test Class
- import grails.test.*
class DemoTagLibTests extends TagLibUnitTestCase { public void setUp() {
super.setUp()
tagLib.metaClass.createLink = { params ->
“/groovyGroups/show/${params.id}”
} // Had to add this because otherwise createLink wouldn’t be available in the test
// This would also be true of custom tags that aren’t in the same tag library
} void testButtonBarWithAdmin() {
mockSession.user = [isAdmin:{-> true}] // map is serving as mock object
def output = tagLib.buttonBar() // tag itself is a closure, so can call as a method here
assert output.toString().contains(‘Create Stuff’)
} void testIfLoggedIn() {
mockSession.user = “Anything can go here” // doesn’t matter what goes here since we’re just checking session.user
def output = tagLib.ifLoggedIn([:]) {
‘User is logged in’
}
assertEquals output.toString(), ‘User is logged in’
} void testGroupLinks() {
mockDomain(GroovyGroup, // new GroovyGroup objects here …)
def output = tagLib.groupLinks()
assert output = tagLib.groupLinks()
assert output.toString().contains(‘<a href=’)
assert output.toString().contains(‘Group2’)
}
} - remember that unit tests run much faster but don’t have everything available, so if you find yourself mocking a ton of stuff in unit tests, that’s a good indication you need integration tests
Namespaces
- if you don’t declare a namespace, uses g
- put static namespace = ‘demo’ at the top of your taglib class to declare a namespace
- use the namespace as a prefix when calling the tag as a method, e.g. namespace.tag()
Distributing TagLibs With Plugins
- create a plugin project
- create or copy a TagLib to the /taglib directory
- package your plugin
- Install your plugin in another application
- your custom tags are now available in the application
Demo
- showing demo of simple custom tag to replace a lot of the redundant stuff in the grails scaffolding
- one limitation of gsp tags is you can’t have nested tags
- you can fake this out a bit by leveraging the request scope
- great use of gsp tags is to bundle up html, css, and javascript that gets output to the page
- FieldData plugin available that does a lot of these sorts of things
- check out the custom tags available in the grails core for good examples of how to do things