GSP tags plugin

  • Tags: /
  • Latest: 0.5
  • Last Updated: 05 March 2012
  • Grails version: 1.3.7 > *
0 vote
Dependency:
compile ":gsp-taglib:0.5"

 Documentation  Source

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 writing layout tags

Advantages:

  • 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>

This tag can be used like any other tag:

<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>
The box tag could then generate a html table and put the header, left and right parts in the respective td's. You can then even nest boxes inside other boxes etc., without having to duplicate the layout html for every usage.

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 the LayoutWriterStack 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
}
I.s.o. writing to @out@, the header, left and right tags need to write their contents to their respective parts:
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?

Contributors

Special thanks to Johan Geerts and Yannick Houbrix for their contribution.