GSP tags plugin
Dependency :
compile ":gsp-taglib:0.5"
Summary
Grails plugin that makes it possible to define tags in GSP's in the grails-app/taglib
Description
Intro
The gsp-taglib grails plugin makes it possible to declare tags in a GSP under grails-app/taglib. Furthermore, it provides a helper class for writinglayout tagsAdvantages:
- Tags that contain mostly markup, read much better in a gsp. Also, IDE's provide good code completion for markup in gsp's.
- Compared to using templates with the tmpl: tag, tags have a far better performance and better support for code completion.
- GSP tags are defined under grails-app/taglib, where you expect them to be
GSP tag usage
Since this is GSP, you can put anything in a tag that you would use in a normal GSP.Example grails-app/taglib/com/acme/tags/fancyLink.gsp:<%@ page namespace="my" %> <%@ page import="com.acme.*" %> <%@page docs=" Standard tag documentation@attr tooltipCode (required) the i18n message code for the tooltip @attr controller, action, etc " %> <% def tooltipCode = attrs.remove('tooltipCode') %> <div class="box"> <a href="${g.createLink(attrs)}" class="fancy">${body()} <div class="tooltip">${g.message(code: attrs.tooltipCode)}</div> </div>
<my:fancyLink tooltipCode="tool.tip" action="save">click me<my:fancyLink>
Special page directives
- namespace: the optional namespace for the tag
- docs: standard tag documentation that will end up in the generated taglib and can be used by your IDE for code completion purposes.
Required attributes
Adding "gsptaglib.addRequiredAsserts = true" to BuildConfig.groovy, will insert assert statements for attributes that are marked as required in the tag docs. Tag users will then get a clear error message when they don't specify a value for the attribute.Dependency injection
Code blocks that start with "@TagLibCodeBlock" will be put at class level of the generated taglib, meaning that they can be used to define class members that get auto-wired:<% @TagLibCodeBlock
// helloWorldService will be auto-wired
HelloWorldService helloWorldService
%>
<h2>${helloWorldService.hello()}</h2>Code generation
For each GSP under grails-app/taglib, a corresponding _MytagGspTagLib.groovy will be generated if it does not exist yet or if it is older then the GSP. The code generation is triggered by a grails compile or by changing the GSP during a grails run-app.Layout tags
Reusable layouts are achieved in Grails by using Sitemesh. While this is OK for global page layouts, it is not convenient for writing reusable UI components that provide a layout template (with template I mean the design pattern, not a grails GSP template). F.e. if you frequently use boxes with a header and 2 columns, you would write a box tag and use it as follows:<g:box>
<g:header><h1>The header</h1></g:header>
<g:left>complex content...</g:left>
<g:right>other content...</g:right>
</g:box>Writing layout tags
When you want to create a layout tag, you need a way to capture the output of all the parts before the layout tag can render itself. This is where theLayoutWriterStack comes in:
def box={attrs, body->
def parts= LayoutWriterStack.writeParts(body)
out << "<table>"
out << "<tr><td colspan='2'>" << parts.header << "</td></tr>"
out << "<tr><td>" << parts.left << "</td><td>" << parts.right << "</td></tr>"
out << "</table>"
//everything inside the box tag that is not within the header, left or right tag, is still accessible in the 'body' part
out << parts.body
}def header = {attrs, body ->
LayoutWriterStack.currentWriter('header') << "<div class='header>" << body() << "</div">
}TODO
- Currently the generated groovy code is written in grails-app/taglib and will end up in your VCS. The goal is to load gsp tags dynamic with pre-compilation in war-mode (like gsp's).
- The code is generated by a hacked copy of GroovyPageParser.groovy. GroovyPageParser should be refactored to have more protected members that can be accessed and/or overridden.
- The ultimate goal is to get gsp tags into grails core as first class citizens.
- Maybe change the page directives into tag directives?