Last updated by 5 years ago

Page: Contribute a Tag, Version:13

Contribute a Tag!

This page contains user submissions of custom tags that may or may not be included in the Grails core.

For general information on using and creating custom tags see http://grails.org/Dynamic+Tag+Libraries.

Add your tag below and please update this list, too:

Tags

dateFormat Tag

Description

Allows formatting of data objects.

Example


<g:dateFormat value="${new Date()}" format="dd-MM-yyyy" />








The Code!


def dateFormat = { attrs ->
   out << new java.text.SimpleDateFormat(attrs.format)
                           .format(attrs.value)
}








esc Tag

Description

Escapes HTML entities within its body to ensure no cross-site scripting (XSS) attacks can work.

Example


<g:esc>${someobj.name}</g:esc>









The Code!


private static final String AMP = "&amp;"
    private static final String LT = "&lt;"
    private static final String GT = "&gt;"
    private static final String QUOTE = "&quot;"

/** * Escape HTML entities within the body. */ def esc = { attrs, body -> def text = ''

if(body instanceof Closure) { text = TagLibUtil.outToString(body, attrs) } else if(body instanceof String) { text = body } else if(attrs instanceof String) { text = attrs }

out << escapeEntities(text); }

/** * Return the given string with all HTML entities escaped into their * HTML equivalent. * * @param text String containing unsafe characters. * @return <var>text</var> with characters turned into HTML entities. */ public static String escapeEntities(String text) { if (text == null) text = "" String trim = text.trim() char[] c = trim.toCharArray()

StringBuffer buffer = new StringBuffer() def i = -1; while (++i < c.length) { if (c[i]=='&') buffer.append(AMP) else if (c[i]=='<') buffer.append(LT) else if(c[i]=='>') buffer.append(GT) else if(c[i]=='"') buffer.append(QUOTE) else buffer.append(c[i]) } return buffer.toString() }









format

Description

The core formatDate tag now allows formats specified in the message bundle (since 1.0-RC2).
Extend FormatTag to format dates and numbers using format specifications from the message bundle.

Example

test.gsp

<g:format value="${new Date()}" formatName="format.date"/>
<g:format value="${new Date()}" formatName="format.time"/>
<g:format value="${new Integer(100)}" formatName="format.currency"/>
messages.properties
format.date=MM/dd/yyyy
format.time=HH:mm:ss
format.currency=$#,##0.00

The Code!&nbsp;


import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import org.codehaus.groovy.grails.plugins.web.taglib.FormatTagLib;

class ExtendedFormatTagLib extends FormatTagLib { def format = { attrs -> def value = attrs.get('value') def formatName = attrs.get('formatName')

if (formatName) { def locale = RCU.getLocale(request) def messageSource = grailsAttributes.getApplicationContext().getBean('messageSource') attrs.format = messageSource.getMessage(formatName, null, null, locale) }

if (value instanceof Date) { formatDate.call(attrs + [date:value]) } else if (value instanceof Number) { formatNumber.call(attrs + [number:value]) } else { out << value } } }









jsdatePicker (DHTML JavaScript Date Picker) Tag

Description

Grails Calendar (Date Picker) TagLib using , dynarch.com's The DHTML / JavaScript Calendar. ~Sorry, it's not using YahooUI~

The following preparation is needed before using.
  1. Download jscalendar-1.0.zip from http://www.dynarch.com/projects/calendar/
  2. Extract the archive into {your-grails-app}/web-app/js/
  3. rename directory name jscalendar-1.0 to calendar.

Ex: {your-grails-app}/web-app/js/calendar/







Example

Set to <header> area.

<g:jscalender></g:jscalender>
Set this to where you want to use Date Picker.
<g:jsdatePicker name="myDate" value="${new Date()}" />








The Code!


import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import java.text.SimpleDateFormat;

/** * A date picker by jscalendar-1.0 * * eg. <g:jsdatePicker name="myDate" value="${new Date()}" /> */ def jsdatePicker = { attrs -> def value = (attrs['value'] ? attrs['value'] : new Date()) def name = attrs['name'] //def locale = RCU.getLocale(request) def c = null if(value instanceof Calendar) { c = value } else { c = new GregorianCalendar(); c.setTime(value) } def day = c.get(GregorianCalendar.DAY_OF_MONTH) def month = c.get(GregorianCalendar.MONTH)+1 def year = c.get(GregorianCalendar.YEAR) def hour = c.get(GregorianCalendar.HOUR_OF_DAY) def minute = c.get(GregorianCalendar.MINUTE)

// def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource") def button_label = "Select..";//messageSource.getMessage("calendar.select",null,locale)

out << """ <input type='hidden' name='${name}' value='struct' /> <input type='text' id='${name}_year' name='${name}_year' value='${year}' style='width:35px;text-align:center;'> /<input type='text' id='${name}_month' name='${name}_month' value='${month}' style='width:20px;text-align:center;'> /<input type='text' id='${name}_day' name='${name}_day' value='${day}' style='width:20px;text-align:center;'>

<input type='text' id='${name}_hour' name='${name}_hour' value='${hour}' style='width:20px;text-align:center;'> : <input type='text' id='${name}_minute' name='${name}_minute' value='${minute}' style='width:20px;text-align:center;'> <input type='button' value='${button_label}' id='${name}_trigger'> <script language='javascript'> Calendar.setup( {inputField:'${name}', button:'${name}_trigger', onSelect:${name}_dateChanged ,showsTime:true} ); function ${name}_dateChanged(calendar) { document.getElementById('${name}_year').value=calendar.date.getFullYear(); document.getElementById('${name}_month').value=calendar.date.getMonth()+1; document.getElementById('${name}_day').value=calendar.date.getDate(); document.getElementById('${name}_hour').value=calendar.date.getHours(); document.getElementById('${name}_minute').value=calendar.date.getMinutes(); } </script> """ } /** * Set this tag inside <head> tag when using js datepicker * <g:jscalender></g:jscalender> */ def jscalender = { attrs -> def applicationUri = grailsAttributes.getApplicationUri(request) out << """ <link rel="stylesheet" type="text/css" media="all" href="${applicationUri}/js/calendar/skins/aqua/theme.css" title="Aqua" /> <script type='text/javascript' src='${applicationUri}/js/calendar/calendar.js'></script> <script type='text/javascript' src='${applicationUri}/js/calendar/calendar-setup.js'></script> <script type='text/javascript' src='${applicationUri}/js/calendar/lang/calendar-en.js'></script> """ }









Using with another languages(locales)

dynarch.com's The DHTML / JavaScript Calendar can change language set by changing "en" of "/calendar/lang/calendar-en.js" to another if your language supported. Ex. "/calendar/lang/calendar-de.js"

It is possible to change automatically using the locale,Here's sample:

  • Make sure that locale you want use is supported.

Check {your-grails-app}/web-app/js/calendar/lang/ directory.
  • replace part of the jscalender closure like below
def jscalender = { attrs ->
    	def locale = RCU.getLocale(request)
    	def applicationUri = grailsAttributes.getApplicationUri(request)
		out << """
			<link rel="stylesheet" type="text/css" media="all"
			 href="${applicationUri}/js/calendar/skins/aqua/theme.css" title="Aqua" />
			<script type='text/javascript' src='${applicationUri}/js/calendar/calendar.js'></script>
			<script type='text/javascript' src='${applicationUri}/js/calendar/calendar-setup.js'></script>
			<script type='text/javascript' src='${applicationUri}/js/calendar/lang/calendar-${locale}.js'></script>
		  """
    }


jQuery Tags

Description

This set of tags allows you to include the JQuery JavaScript libary and register a "toggle" effect on an arbitrary HTML element referenced via id. Toggle means that the element you reference is magically disappearing and then shows up again if you hit the toggle link again. You first need to download jQuery which is only 15kB in size. Place the jquery.js file in a new web-app/js/jquery directory. To use the tags, you first need to reference the jquery.js from your HTML header, as shown below:

Example


<script type="text/javascript" src="${createLinkTo(dir:'js/jquery', file:'jquery.js')}"></script>

<g:jquery> <g:toggleelement elementId="notecontent" linkId="showhidelink" event="click" speed="slow"/> </g:jquery>









How it might look

Below are two example screenshots out of my app. The orange box is a div, referenced by id and styled via CSS. Note that the link is in a separate div box, resulting in the link beeing visible all the time. &nbsp; Once you click the link, the box slides down and opens.&nbsp; &nbsp; &nbsp; To have the boxed immediately closed after the page loaded, add this after the toggleelement tag of the example above: $("#notecontent").hide("slow");&nbsp; &nbsp;

toggleelement description

  • elementId - references the id of the element that you want to toggle, e.g. disappear and appear
  • linkId - the link that will trigger the action, e.g. a <a href="http://docs.codehaus.org/pages/editpage.action#" id="showhidelink">link</a> ...
  • event - the event that is listened to: click means you have to click the link, can also be: dblclick, mouseover, etc. see the visual jQuery guide for a full list (go to events/mouse)
  • speed - can be either slow, normal or fast

The Code!


import org.springframework.validation.Errors;
import org.springframework.context.NoSuchMessageException;
import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU;

class JQueryTagLib {

def jquery = { attrs, body -> out << '<script type="text/javascript"> $(document).ready(function(){'

out << body()

out << '}); </script>' }

def toggleelement = { attrs -> out << /$("#${attrs['linkId']}").${attrs['event']}(function(){ $("#${attrs['elementId']}").toggle("${attrs['speed']}"); return false; });/ }

}

Note that this only implements a very small subset of the jQuery tag library. Feel free to extend this and post your code here :-) Enjoy.


textField enhanced&nbsp;

Description

Creates an input of type "text" specifically tailored to display and update a property on an object.

Parameters

object (required) - the object the property of which is accessed field(required) - the name of the property to access id(optional) - the id of the resulting input element, defaults to the name of the property

Example


<g:textField object="${book}" field="author.lastName" />
which gives:
<input type="text" id="author.lastName" name="author.lastName" value="Auster" />








Code


import org.springframework.web.util.HtmlUtils as HU;
def textField = { attrs ->
       attrs.type = "text"
       attrs.name = attrs.remove('field')
       attrs.id = (attrs.id == null) ? attrs.name : attrs.id
       def val = attrs.remove('object')
       attrs.name.split('\\.').each { val = val[it] }
       attrs.value = (val == null) ? "" : HU.htmlEscape(val.toString())
       field(attrs)
}








Yahoo Calendar&nbsp;

Description

3 tags that allow the Yahoo calendar control to be used to enter date values in a form. Note that this calendar tag is still a little rough, I placed it here because someone on the forum needed one and I figured that it was better than starting from scratch :-)

Parameters

  • yuiCalendarUtils
    • No Parameters
  • yuiCalendarHeader
    • name - the name of the calendar instance (mandatory)
    • value - the date value to set the calendar to (optional)
  • yuiCalendarBody
    • name - the name of the calendar instance
    • bean - the bean containing the date parameter to set the display and hidden fields to

Dependencies

The calendar tags need the following resources:

Example


<html>
    <head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
		<meta name="layout" content="main" />
		<title>Create Action</title>  		<!-- YUI files -->
		<script type="text/javascript" src="${createLinkTo(dir:'js/yahoo',file:'calendar-min.js')}"></script>
		<!-- Calendar Utils -->
		<script type="text/javascript" src="/appname/js/toolbox/date.js"/>

<link rel="stylesheet" type="text/css" href="${createLinkTo(dir:'js/yahoo/assets',file:'calendar.css')}"/> <g:yuiCalendarUtils/> <g:yuiCalendarHeader name="dueDate" value="${action.dueDate}"/> </head> <body> <div id="yui-main"> <div class="yui-b"> <g:form action="save" method="post" > <div class="data_entry"> <label for='dueDateValue'>Due Date:</label> <g:yuiCalendarBody name="dueDate" bean="${action}"/> </div>

<div class="buttons"> <span class="formButton"> <input type="submit" value="Create"></input> </span> </div> </g:form> </div> </div> <div class="yui-b"> This text is in the sidebar </div> </body> </html>









Code


import java.text.SimpleDateFormat

/** * A tag lib that provides tags for working with Yahoo calendar controls inside forms * * @author Peter Kelley * @since 15-September-2006 */

class YahooCalendarTagLib {

/** * Tag to output the correct javascript utility functions to support the calendar tags. * This tag takes no parameters. This tag should be placed in the page header once if * the yuiCalendarHeader and yuiCalendarBody tags are to be used. Only a single instance * of this tag is ever required. */ def yuiCalendarUtils = {attrs, body -> out << "<script language='javascript'>n" out << "YAHOO.namespace('example.calendar');n" out << "function showYUICalendar(calendarName, calendarInstance) {n" out << " var linkName = calendarName + 'Link';n" out << " var link = document.getElementById(linkName);n" out << " var pos = YAHOO.util.Dom.getXY(link);n" out << " calendarInstance.oDomContainer.style.display='block';n" out << " YAHOO.util.Dom.setXY(calendarInstance.oDomContainer, [pos[0]+link.offsetWidth+5,pos[1]] );n" out << "}n" out << "function setCalendarDateValue(calendarName, calendarInstance, displayFieldName) {n" out << " var selected = calendarInstance.getSelectedDates()[0];n" out << " calendarInstance.oDomContainer.style.display='none';n" out << " var submitField = document.getElementById(calendarName + '_day');n" out << " submitField.value = selected.format('dd');n" out << " submitField = document.getElementById(calendarName + '_month');n" out << " submitField.value = selected.format('MM');n" out << " submitField = document.getElementById(calendarName + '_year');n" out << " submitField.value = selected.format('yyyy');n" out << " submitField = document.getElementById(calendarName + '_hour');n" out << " submitField.value = selected.format('HH');n" out << " submitField = document.getElementById(calendarName + '_minute');n" out << " submitField.value = selected.format('mm');n" out << " var displayField = document.getElementById(displayFieldName);n" out << " displayField.innerHTML = selected.format('E d MMM yyyy');n" out << "}n" out << "</script>n" }

/** * Tag to provide initialization code for a single Yahoo calendar instance. This tag * should be used once for each calendar instance used on the page. This tag * should be placed in the page header and paired with a yuiCalendarBody tag in the body with * the same value for the calendarName parameter. * @param name the name of the calendar instance * @param value the date value to set the calendar to */ def yuiCalendarHeader = {attrs, body -> def calendarName = attrs.name if (calendarName == null) { throw new IllegalArgumentException("name parameter must be specified") } Date dateValue = attrs.value def monthYearFormatter = new SimpleDateFormat("MM/yyyy") def monthDayFormatter = new SimpleDateFormat("MM/dd") out << "<script language='javascript'>n" out << 'YAHOO.namespace("example.calendar");n' out << "function init${calendarName}() {n" out << " YAHOO.example.calendar.${calendarName} = new YAHOO.widget.Calendar('YAHOO.example.calendar.${calendarName}', '${calendarName}Container'" if (dateValue != null) { out << ", '${monthYearFormatter.format(dateValue)}', '${monthDayFormatter.format(dateValue)}'" } out << ");n" out << " YAHOO.example.calendar.${calendarName}.title = 'Select ${calendarName}';n" out << " YAHOO.example.calendar.${calendarName}.onSelect = set${calendarName};n" out << " YAHOO.example.calendar.${calendarName}.render();n" out << "}n" out << "function set${calendarName}() {n" out << " var calendarInstance = YAHOO.example.calendar.${calendarName};n" out << " setCalendarDateValue('${calendarName}', calendarInstance, '${calendarName}Value');n" out << "}n" out << "function show${calendarName}() {n" out << " var calendarInstance = YAHOO.example.calendar.${calendarName};n" out << " showYUICalendar("${calendarName}", calendarInstance);n" out << "}n" out << "YAHOO.util.Event.addListener(window, 'load', init${calendarName});n" out << "</script>n" }

/** * Tag to provide field code for a single Yahoo calendar instance. This tag * should be used once for each calendar instance used on the page. This tag * should be placed in the page body and paired with a yuiCalendarHeader tag in the header with * the same value for the calendarName parameter. * @param name the name of the calendar instance * @param bean the bean containing the date parameter to set the display and hidden fields to */ def yuiCalendarBody = { attrs, body -> def calendarName = attrs.name def bean = attrs.bean if (calendarName == null) { throw new IllegalArgumentException("name parameter must be specified") } if (bean == null) { throw new IllegalArgumentException("bean parameter must be specified") }

def day = "" def month = "" def year = "" def hour = "" def minute = ""

def dateDisplay = ""

if (bean[calendarName] != null) { //Set the various date values to use in the output def calendar = new GregorianCalendar() calendar.setTime(bean[calendarName])

day = calendar.get(Calendar.DATE) month = calendar.get(Calendar.MONTH) + 1 // Stupid Java months start at 0 year = calendar.get(Calendar.YEAR) hour = calendar.get(Calendar.HOUR_OF_DAY) // 24 hour clock minute = calendar.get(Calendar.MINUTE)

def formatter = new SimpleDateFormat("E d MMMM yyyy") dateDisplay = formatter.format(bean[calendarName]) } out << "<span class='value ${hasErrors('bean':bean,field:calendarName,'errors')}'>n" out << "<span id='${calendarName}Value'>${dateDisplay}</span>n" out << "<a id='choose${calendarName}' onclick='show${calendarName}()' href='javascript:void(null)' >n" out << " <img border='0' id='${calendarName}Link' style='margin: 5px; vertical-align: middle' src='" createLinkTo(dir:'images',file:'office-calendar.png') out << "' />n" out << "</a></span>n" out << "<div id='${calendarName}Container' class='form_calendar'></div>n" out << "<input id='${calendarName}' type='hidden' name='${calendarName}' value='struct'/>n" out << "<input id='${calendarName}_day' type='hidden' name='${calendarName}_day' value='${day}'/>n" out << "<input id='${calendarName}_month' type='hidden' name='${calendarName}_month' value='${month}'/>n" out << "<input id='${calendarName}_year' type='hidden' name='${calendarName}_year' value='${year}'/>n" out << "<input id='${calendarName}_hour' type='hidden' name='${calendarName}_hour' value='${hour}'/>n" out << "<input id='${calendarName}_minute' type='hidden' name='${calendarName}_minute' value='${minute}'/>n" }

}









JavaScript escape Tag

Allows you to embed HTML in a JavaScript string by escape special JavaScript characters.

Example usage


var content = '<g:escapeBody javaScriptEscape="true"><g:render template="/books/book" /></g:escapeBody>';
Code


import org.springframework.web.util.JavaScriptUtils

class MyTagLib {

/** * Captures the given body's text and returns it as a String * * @param body the body Closure as provided to a tag */ static captureBody(body) { def sw = new StringWriter() def saveOut = body.delegate.out try { body.delegate.out = new PrintWriter(sw) body() } finally { body.delegate.out = saveOut } return sw.toString() }

/** * Escape the body text for use in a Javascript string context * * Useful for Ajax stuff */ def escapeBody = { attrs, body -> // TODO maybe support more formats assert attrs.javaScriptEscape, "'javaScriptEscape' attribute was not provided, but it's the only supported escaping mechanism right now!?" out << JavaScriptUtils.javaScriptEscape(captureBody(body)) } }


























image tag&nbsp;


/*
	   src is the path to the image within the images directory
	 	<g:image src="grails_logo.jpg" alt="foo" title="bar" />
	 	If you pass in the code attribute it will use that to create
	 	an alt and title attribute pulled from your i18n messages.
	 	This will supercede any alt or title attributes that you
	 	have manually passed in.
	 	The code attribute is the only case where it will escape quotes
	 	in your attribute's contents.
	 */
	 def image = {attrs ->
	 	def src = attrs.remove('src');
	 	def imageString = "<img src="${grailsAttributes.getApplicationUri(request)}/images/${src}"";
	 	def newAttrs = [:];
	 	for (key in attrs.keySet()){
	 		if (key != 'code'){
	 			newAttrs[key] = attrs[key];
	 		}
	 	}
	 	if(attrs['code']) {
	 		def messageSource = grailsAttributes
				.getApplicationContext()
				.getBean("messageSource");
			def locale = RCU.getLocale(request);
			def code = attrs['code']
			def message = messageSource.getMessage( code, null, null, locale );
			if(message) {
				newAttrs['alt'] = message.replaceAll(""", "\\\"");
				newAttrs['title'] = message.replaceAll(""", "\\\"");
			} else {
				newAttrs['alt'] = code;
				newAttrs['title'] = code;
			}
	 	}

for (key in newAttrs.keySet()){ imageString += " ${key}="${newAttrs[key]}""; }

imageString += " />"; out << imageString; }









DOM ids&nbsp;

With all this Ajaxy goodness you need some easy consistent way to reference DOM elements. Until something like this is integrated into the core domain objects here's a simple tag to generate dom ids for you.

/** takes two attributes object (that you want the dom_id of)
			and optionally a prefix which will be prepended with an underscore
			before the object's class and id.
		<g:domId object="${aStudentObject}" prefix="foo" />
		Assuming the Student object's id was 4 this outputs:
			foo_student_4
	*/
	def domId = { attrs ->
		if (attrs['prefix']){
			out << attrs['prefix'];
			out << '_';
		}
	 	out << attrs['object'].getClass().getName().toLowerCase();
	 	out << '_';
	 	out << attrs['object'].id
	}








Enable or disable checkbox

This version of the g:checkbox is identical to the original one, except that you can set the "disabled=" property to true or false.

def managedCheckBox = { attrs ->
          def value = attrs.remove('value')
          def name = attrs.remove('name')
          def disabled = attrs.remove('disabled')
          if(!value) value = false
          out << '<input type="hidden" '
          out << "name="_${name}" />"
          out << '<input type="checkbox" '
          out << "name="${name}" "
          if(value) {
                out << 'checked="checked" '
          }
          if(disabled != null && disabled == 'true')
          {
                out << 'disabled="disabled" '
          }
          out << "value="true" "
        // process remaining attributes
        outputAttributes(attrs)

// close the tag, with no body out << ' />' }

Usage:
<g:managedCheckBox disabled="true" name="myCheckbox" value="not_editable_value" />
<g:managedCheckBox disabled="false" name="myCheckbox" value="editable_value" />








Model TagLib

This tag lib refines Bean TagLib to modularize the code and provide support for many more field types, and provides template-driven control of the rendering of fields, their labels, errors and required indicator.

The code is currently running on 2 live sites, sadly without unit tests (TagLib testing was broken in head when I wrote this). modelRadioXXX is not implemented yet.

There is a modelCountry tag which is dependent on another taglib called I18NTaglib.

For the lengthy code see Draft_ModelTagLib.

I18N TagLib

Embryonic support for country selection drop-downs, and localized date display. Country names in English only currently but I have them in 4 or so languages once I work out how to do this.

In future will support restricting range of countries listed, and setting the order (default = server-locale sort order of country name).

For the code see Draft_I18NTagLib.

renderRandom Tag

render a configurable number of random items out of a collection, uses default g:render

Example usage

<g:renderRandom template="/shared/product" max="4" var="item" collection="${Product.findAllByActive(true)}"/>
&nbsp; Code:
def renderRandom = { attrs, body ->
        if(!attrs.collection)
            throwTagError("Tag [renderRandom] is missing required attribute [collection]")
        def max = (attrs.max == null)?5:attrs.max.toInteger()
        def collection = attrs.collection
        if(collection.size() > max){
            Random rnd = new Random()
            def newcollection = []
            for(i in 0..<max){
                def nextRandom = rnd.nextInt(collection.size() - 1)
                newcollection.add(collection.remove(nextRandom))
            }
            attrs.collection = newcollection
        }
        render(attrs,body)
     }








transform Tag

Description

Applies an XSL stylesheet to a source XML document and writes the output to the response. The transformed XML will appear wherever the tag is used in a GSP.

Parameters

  • source - The source XML to transform. This can be one of: a DOM node, an _InputStream_, a _Reader_, or a file path.
  • transformer (optional) - A javax.xml.transform.Transformer instance that will be used to transform the source XML.
  • stylesheet (optional) - The name of the XSL stylesheet to apply. The stylesheet will be loaded from a stylesheets directory in the root of the web application (i.e. _<app>/web-app/stylesheets_). For example, if this parameter has the value "verbatim", then the stylesheet will be loaded from _<app>/web-app/stylesheets/verbatim.xsl_. Note Either transform or stylesheet must be specified, but not both.
  • factory (optional) - This parameter can be used with stylesheet to specify a particular javax.xml.transform.TransformerFactory to use. It is the fully-qualified class name of the TransformerFactory you want to use.

Examples

The following example uses a JAXP transformer that has already been configured in the controller. The controller simply puts the transformer object in the model under the key 'transformer' and the source DOM document under the key 'doc'.

<g:transform transformer="${transformer}" source="${doc}" />








The Code!


import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamSource
import javax.xml.transform.stream.StreamResult

class XmlTagLib { /** * Applies an XSL stylesheet to a source DOM document or file and * writes the result to the output stream. */ def transform = { attrs -> // Check that some XML to transform has been provided. def input = attrs['source'] if (!input) { throwTagError('Tag [transform] missing required attribute [source].') }

// Check the various attributes. // // We must have one of 'transformer' or 'stylesheet'. def transformer = attrs['transformer'] if (!transformer) { // No transformer specified, so use a stylesheet instead. def stylesheet = attrs['stylesheet']

// If there's no stylesheet either, then we're stuck. if (!stylesheet) { throwTagError('Tag [transform] missing required attribute [transformer] or [stylesheet].') }

// See whether a factory class has been specified. If so, // we use that one to create a transformer. Otherwise, we // just use the default factory. def factory if (attrs['factory']) { factory = Class.forName(attrs['factory']).newInstance() } else { factory = TransformerFactory.newInstance() }

// Load up the stylesheet into a transformer instance. def xslStream = servletContext.getResourceAsStream("/stylesheets/${stylesheet}.xsl") transformer = factory.newTransformer(new StreamSource(xslStream)) } else if (attrs['stylesheet']) { throwTagError('Tag [transform]: [stylesheet] attribute can not be used with [transformer].') }

// We have the transformer set up, so now prepare the source // XML and wrap the output writer in a Result. def output = new StreamResult(out) if (input instanceof Node) { input = new DOMSource(input) } else if (input instanceof InputStream || input instanceof Reader) { // Create a StreamSource for the given file. input = new StreamSource(input) } else { input = new StreamSource(new File(input)) }

// Perform the transformation! transformer.transform(input, output) } }









ScaffoldTags

Moved to its own page under the plugins section, along with a walkthrough of what it provides. Also checked in on the Grails Plugin SVN.

Features

  • Display, list, or edit domain objects on your GSP pages using a single tag call.
  • Modify how your domain objects are displayed without requiring significant rewrite (either for a specific domain class or in general).
  • Modify how specific data types, or even specific domain properties are displayed without requiring significant rewrite.
  • Modify how specific types of relationships are displayed.
  • Either inherit from a shared template or use a controller-specific template
  • Extendable to support other types of views, and to reuse templates between view types

&nbsp;

radiogroup

Description

Similar functionality to the FormTagLib <g:select> but produces a radio group instead of a select box.

Why

The main utility of this would be displaying a radio group based an a data base table. Radio groups are sometimes preferred to select lists for small numbers of items.

Parameters

Same as <g:select>:&nbsp; from keys value noSelection

Name change from <g:select>:

&nbsp;&nbsp;&nbsp; optionKey to radioKey

&nbsp;&nbsp;&nbsp; optionValue to radioValue

New &nbsp;&nbsp;&nbsp;&nbsp; horizontal: if horizontal="true" buttons are displayed horizontally

&nbsp;&nbsp;&nbsp; name: name of radiogroup

&nbsp;&nbsp;&nbsp; noSelectionChecked:&nbsp;&nbsp;&nbsp; Unsurprisingly, checks the noSelection radio button&nbsp;

&nbsp;&nbsp;&nbsp; &nbsp;

Example


<g:radiogroup>	radioKey="id"	from=$"$books.list()}"	noSelection="['':'']"	noSelectionChecked="true"	name="books"	</g:radiogroup>
Code
/** author: Larry Headlund **/def renderRadio = { name, value, label, checked, remainingAttributes  ->
		out << '<INPUT TYPE=radio '
		out << "NAME="${name}" "
		out << "VALUE="${value.toString().encodeAsHTML()}" "
		if(checked) {
			out << 'CHECKED '
			}
		outputAttributes(remainingAttributes)
        out << " ></INPUT>"
		out << "${label}"
	}

/** * A helper tag for creating HTML radiogroups following the select helper tag * * Examples: * <g:radiogroup name="user.age" from="${18..65}" value="${age}" /> * <g:radiogroup name="user.company.id" from="${Company.list()}" value="${user?.company.id}" optionKey="id" /> * * It has the additional attribute 'horizontal' ( which * determines if the layour is horizontal or vertical */ def radiogroup = { attrs -> def name = attrs.remove('name') def from = attrs.remove('from') def keys = attrs.remove('keys') def radioKey = attrs.remove('radioKey') def radioValue = attrs.remove('radioValue') def value = attrs.remove('value') def horizontal = attrs.remove('horizontal') def noSelection = attrs.remove('noSelection') def noSelectionChecked = (attrs.remove('noSelectionChecked') ? true : false)

// create radio buttons from list if(from) { from.eachWithIndex { el,i -> def val = ""; def label = ""; def checked = false;

if(keys) { val = keys[i] if(keys[i] == value) { checked = true; } } else if(radioKey) { def keyValue = null if(radioKey instanceof Closure) { keyValue = radioKey(el) val = keyValue } else if(el !=null && radioKey == 'id' && grailsApplication.getGrailsDomainClass(el.getClass().name)) { keyValue = el.ident() val = keyValue } else { keyValue = el.properties[radioKey] val = keyValue }

if(keyValue == value) { checked = true } } else { val = el if(el == value) { checked = true } } if(radioValue) { if(radioValue instanceof Closure) { label = radioValue(el).toString().encodeAsHTML() } else { label = el.properties[radioValue].toString().encodeAsHTML() } } else { def s = el.toString() if(s) { label = s.encodeAsHTML() } } renderRadio(name, val, label, checked,attrs) if (horizontal != "true") { out << "<br>" } out << "n" } }

if (noSelection != null) { noSelection = noSelection.entrySet().iterator().next() renderRadio(name, noSelection.key, noSelection.value, noSelectionChecked,attrs) } }

&nbsp;

multiple select tag

Description&nbsp;

The current (as of 1.0 RC3) GSP select tag does not deal with a 'value' of anything but a single string.&nbsp; This tag allows you to have a list as the 'value' parameter, so that select boxes with multiple options can display multiple values pre-selected.

Example

<g:select from="${roles.list()}" values="${user.roles}" name="user.roles" multiple />

How to use&nbsp;

&nbsp;Copy the code below in to a file named "MultiselectTagLib.groovy" and place in the grails-app/taglib directory of your application.

Code&nbsp;


/**
 * multiselect taglib
 * adapted from select functionality in Grails' FormTagLib.groovy file
 *
 * Intended as a stopgap until multiselect functionality is added
 * to Grails permanently
 *
 * Additional functionality for multiple select behaviour
 * provided by Michael Kimsal
 *
 * Original FormTagLib code
 * Copyright 2004-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 */

import org.springframework.web.servlet.support.RequestContextUtils as RCU;

class MultiselectTagLib {

/** * A helper tag for creating HTML selects * * Examples: * <g:select name="user.age" from="${18..65}" value="${age}" /> * <g:select name="user.company.id" from="${Company.list()}" value="${user?.company.id}" optionKey="id" /> */ def multiselect = { attrs -> def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource") def locale = RCU.getLocale(request) def writer = out attrs.id = attrs.id ? attrs.id : attrs.name def from = attrs.remove('from') def keys = attrs.remove('keys') def optionKey = attrs.remove('optionKey') def optionValue = attrs.remove('optionValue') def value = attrs.remove('value') def valueMessagePrefix = attrs.remove('valueMessagePrefix') def noSelection = attrs.remove('noSelection') if (noSelection != null) { noSelection = noSelection.entrySet().iterator().next() }

writer << "<select name="${attrs.remove('name')}" " // process remaining attributes outputAttributes(attrs)

writer << '>' writer.println()

if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, value) writer.println() }

// create options from list if(from) { from.eachWithIndex { el,i -> def keyValue = null writer << '<option ' if(keys) { keyValue = keys[i] writeValueAndCheckIfSelected(keyValue, value, writer) } else if(optionKey) { if(optionKey instanceof Closure) { keyValue = optionKey(el) } else if(el !=null && optionKey == 'id' && grailsApplication.getArtefact(DomainClassArtefactHandler.TYPE, el.getClass().name)) { keyValue = el.ident() } else { keyValue = el[optionKey] } writeValueAndCheckIfSelected(keyValue, value, writer) } else { keyValue = el writeValueAndCheckIfSelected(keyValue, value, writer) } writer << '>' if(optionValue) { if(optionValue instanceof Closure) { writer << optionValue(el).toString().encodeAsHTML() } else { writer << el[optionValue].toString().encodeAsHTML() } } else if(valueMessagePrefix) { def message = messageSource.getMessage("${valueMessagePrefix}.${keyValue}", null, null, locale) if(message != null) { writer << message.encodeAsHTML() } else if (keyValue) { writer << keyValue.encodeAsHTML() } else { def s = el.toString() if(s) writer << s.encodeAsHTML() } } else { def s = el.toString() if(s) writer << s.encodeAsHTML() } writer << '</option>' writer.println() } } // close tag writer << '</select>' }

private writeValueAndCheckIfSelected(keyValue, value, writer){ writer << "value="${keyValue}" " if(keyValue?.toString() == value?.toString()) { writer << 'selected="selected" ' } if(value?.contains(keyValue?.toString())) { writer << 'selected="selected" ' } }

/** * Dump out attributes in HTML compliant fashion */ void outputAttributes(attrs) { attrs.remove( 'tagName') // Just in case one is left attrs.each { k,v -> out << k << "="" << v.encodeAsHTML() << "" " } }

}



localeSelecter Tag

Description

Creates a select to select from a list of available locales

I know that there is a localeSelect, but this one only show languages for which there is a message_XX.properties file.

And the language name is show in its own language, i.e. 'Deutch' instead of 'German'&nbsp;

Example


<g:localeSelecter />

The Code!

import org.springframework.web.servlet.support.RequestContextUtils

def localeSelecter = {attrs -> List locales = [] new File('./grails-app/i18n').eachFile { def arr = it.name.split("[_.]") locales << (arr.length > 3 ? new Locale(arr[1], arr[2]) : arr.length > 2 ? new Locale(arr[1]) : new Locale("")) } attrs['from'] = locales attrs['name'] = "lang" attrs['value'] = RequestContextUtils.getLocale(request) attrs['optionValue'] = {"${it.getDisplayName(it)}"} out << select(attrs) }

Sorting each Tag

Description

An each tag that can sort the items by it's alphanumeric string value, not lexical string value (Test2 is before Test100)

This uses AlphNumSorter.groovy from http://www.davekoelle.com/files/alphanum.groovy. Put AlphNumSorter.groovy in grails_app_home/src/groovy/. It support the var and status attributes of the standard g:each tag. It will sort if the sort attribute is set to "true".

Example


<g:each sort="true" var="t" in="${test.testActionMixes}">
     <li><g:link controller="testActionMix" action="show" id="${t.id}">${t}</g:link></li>
</g:each>

The Code!

/* Author: Drew Gulino */
def each =
    {
        attrs, body ->
        def var = attrs.var ? attrs.var : "num"
        def status = attrs.status ? attrs.status : "status"
        def unsorted = attrs.in
        def output
        def index = 0

if (attrs?.sort == "true") { output = new TreeSet(new AlphNumSorter()) output.addAll(unsorted) } else { output = unsorted }

output.each() { num -> out << body((var):num,(status):index) index += 1 } }

stateDropDown Tag

Description

Creates a state drop down list (including DC) and allows for passing in of preselected state value. Does NOT require the "from" attribute of the g:select tag so you don't have to pass in a list of states.

Example


<g:stateDropDown name="state" id="state" value="${someObj.state}" />

The Code!

/* Author Ryan Headley */
def stateDropDown = { attrs ->
		def stateList = [
		AL:"Alabama",
		AK:"Alaska",
		AZ:"Arizona",
		AR:"Arkansas",
		CA:"California",
		CO:"Colorado",
		CT:"Connecticut",
		DC:"District of Columbia",
		DE:"Delaware",
		FL:"Florida",
		GA:"Georgia",
		HI:"Hawaii",
		ID:"Idaho",
		IL:"Illinois",
		IN:"Indiana",
		IA:"Iowa",
		KS:"Kansas",
		KY:"Kentucky",
		LA:"Louisiana",
		ME:"Maine",
		MD:"Maryland",
		MA:"Massachusetts",
		MI:"Michigan",
		MN:"Minnesota",
		MS:"Mississippi",
		MO:"Missouri",
		MT:"Montana",
		NE:"Nebraska",
		NV:"Nevada",
		NH:"New Hampshire",
		NJ:"New Jersey",
		NM:"New Mexico",
		NY:"New York",
		NC:"North Carolina",
		ND:"North Dakota",
		OH:"Ohio",
		OK:"Oklahoma",
		OR:"Oregon",
		PA:"Pennsylvania",
		RI:"Rhode Island",
		SC:"South Carolina",
		SD:"South Dakota",
		TN:"Tennessee",
		TX:"Texas",
		UT:"Utah",
		VT:"Vermont",
		VA:"Virginia",
		WA:"Washington",
		WI:"Wisconsin",
		WV:"West Virginia",
		WY:"Wyoming"]

out << "<select name='${attrs.name}' id='${attrs.id}'>" out << "<option value=''>Select...</option>" stateList.each { out << "<option value='${it.key}'" if(attrs.selectedValue == it.key) { out << " selected='selected'" } out << ">${it.value}</option>" } out << "</select>" }


Check Box List

Description

In a has-many relationship, Grials default implementation generates a list of links, and an add link where one can add a new item.

The Check Box List allows selection of existing items by presenting them as an array of checkboxes. In other words, it allows the user to select a subset amongst a superset of all items - as a more user-friendly alternative to a multi-select listbox (where a single click without the modifier key de-selects everything!). The CSS rendering provides the list within a scrollable DIV element to keep it independent of the total number of items in the list.

Tag parameters:

name: Name of the checkboxlist - this will also be the name of the element&nbsp;

from: A collection of the master set of elements (containing all possible items)

value: A collection (subset) of the currently selected items.

optionKey: same as the conventional option key

Example


<g:checkBoxList name="usersIds" from="${User.list()}" value="${claim?.users?.collect{it.id}}" optionKey="id"/>





Should render a checkbox list that looks like this&nbsp;

Code

A bit of CSS is needed to be included somewhere, either inline or in a CSS file. Needless to say, there's plenty of scope for further customization here:

<style type="text/css">
.CheckBoxList {
    height: 100px; overflow: auto; overflow-x: hidden; width: 200px; border: 0px solid #000;
    list-style-type: none; margin: 0; padding:0px
}

.CheckBoxList li { padding:2px } </style>

And here's the main code from the tag library…
//  Checkbox list that can be used as a more user-friendly alternative to
    // a multiselect list box
    def checkBoxList = {attrs, body ->

def from = attrs.from def value = attrs.value def cname = attrs.name def isChecked, ht, wd, style, html

// sets the style to override height and/or width if either of them // is specified, else the default from the CSS is taken style = "style='" if(attrs.height) style += "height:${attrs.height};" if(attrs.width) style += "width:${attrs.width};"

if (style.length() == "style='".length()) style = "" else style += "'" // closing single quote

html = "<ul class='CheckBoxList' " + style + ">"

out << html

from.each { obj ->

// if we wanted to select the checkbox using a click anywhere on the label (also hover effect) // but grails does not recognize index suffix in the name as an array: // cname = "${attrs.name}[${idx++}]" // and put this inside the li: <label for='$cname'>...</label>

isChecked = (value?.contains(obj."${attrs.optionKey}"))? true: false

out << "<li>" << checkBox(name:cname, value:obj."${attrs.optionKey}", checked: isChecked) << "${obj}" << "</li>" }

out << "</ul>"

}

To map the selected ids to corresponding domain objects, an additional set property required in the containing domain class &nbsp;&nbsp;&nbsp;

//    this is a has-many defined as usual
    static hasMany = [users:User]

// this additional setter is used in the multiselect list to convert // the ids selected in the checkbox list to the corresponding domain objects public void setUserIds(List ids) { this.users = ids.collect { User.get(it) } }


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

Label List

Description

Renders a delimiter-seperated list of values from an array. Could be used in conjunction with a CheckBoxList - placing it just above it to show the selected items in a label.

Usage

Selected: <g:labelList value="${claim?.users}" delimiter="/ "/>
or
<g:labelList value="${[1,2,3,4]}" delimiter="/"/>
The latter example produces the output:

1/2/3/4&nbsp;

Code

def labelList = {attrs, body ->

def value = attrs.value def delim = attrs.delimiter

if (!delim) delim = ""

if (value instanceof Collection && value.size() > 0) {

StringBuilder html = new StringBuilder()

value.each { obj -> html.append("$obj$delim") }

out << html.substring(0, html.length() - delim.length()) }

}


&nbsp;&nbsp;

last.fm Recent List Tag

Description

recent: Retrieves the most recent playlist from a last.fm account and displays as a list using a similar style to last.fm Song titles link to the last.fm page for that song.

weekly: Fetches the most popular artists for the current week for a user account and displays an ordered list.

Usage

basic lists:

<lastfm:recent username="someUserAccount"/>
<lastfm:weekly username="someUserAccount" limit="15"/>

to order the recent list:

<lastfm:recent username="someUserAccount" ordered="true"/>

to use a previously downloaded XML file (ie by a cron or quartz job):

<lastfm:recent src="file:///home/me/myRecentList.xml"/>

The Code

/**
 * LastFmTagLib provides two targets to retrieve user data from last.fm 
 * (http://www.lastfm.com).  
 *
 * 1. The "recent" tag pulls the 10 most recently listened to tracks for the 
 *    supplied username:
 *    <lastfm:recent username="someuser"/>	 
 *
 * 2. The "weekly" tag pulls the most listened to artists for a supplied 
 *    username, sorted by most tracks played:
 *    <lastfm:weekly username="someuser" limit="20"/>
 *
 * Each target can optionally take a "src" attribute to read the XML from
 * instead of hitting the last.fm site each time it's executed. This is
 * useful for a busy site or page that can grab the XML via a cron job or
 * quartz job, storing the data locally for some minutes at a time.
 *
 * @author Darren Davison (darren [at] davisononline (dot) org)
 */
class LastFmTagLib {

static namespace = 'lastfm'

def ws_prefix = "http://ws.audioscrobbler.com/1.0/user/" def ws_recent = "/recenttracks.xml" def ws_weekly = "/weeklyartistchart.xml"

/* * supply a 'username' attribute to get the most recent * last.fm tracks for that user as a list. Optionally * supply a 'listClass' css attribute for the list. * * This target also takes an optional "ordered" attribute to * decide whether to display an ordered (<ol/>) or unordered * (<ul/>) list. */ def recent = { attrs, body ->

def src = (attrs.src ? attrs.src : "${ws_prefix}${attrs.username}${ws_recent}") def xml = new URL(src).text def recent = new XmlSlurper().parseText(xml) def tracks = recent.track def listType = (attrs?.ordered) ? "ol" : "ul" def now = System.currentTimeMillis() / 1000

// render as a list with links out << "<${listType} id="lastfmRecentList" class="${attrs?.listClass}">n" tracks.each { def d = Long.valueOf(it.date.@uts.text()) def diff = now - d def showdiff if (diff < 180) showdiff = "just listened" else if (diff < 3600) showdiff = "${Math.round(diff/60)} minutes ago" else if (diff < 86400) showdiff = "${Math.round(diff/3600)} hours ago" else showdiff = "${Math.round(diff/86400)} days ago"

out << " <li class="lastfmRecentListItem"><a href="${it.url}" title="listen at last.fm">${it.name}</a> - ${it.artist} (${showdiff})</li>n" } out << "</${listType}>"

}

/* * supply a 'username' attribute to get the most recent * last.fm tracks for that user as a list. Optionally * supply a 'listClass' css attribute for the list. * * This target also takes an optional "limit" attribute to * limit the number of entries shown from the 250 available. * Default is 10. */ def weekly = { attrs, body ->

def src = (attrs.src ? attrs.src : "${ws_prefix}${attrs.username}${ws_weekly}") def xml = new URL(src).text def weekly = new XmlSlurper().parseText(xml) def artists = weekly.artist

// render chart list with links to artists out << "<ol id="lastfmWeeklyList" class="${attrs?.listClass}"/>n" def limit = (attrs.limit ? Integer.valueOf(attrs.limit) : 10) (0..limit - 1).each { def a = artists[it] out << " <li class="lastfmWeeklyListItem"><a href="${a.url}" title="${a.name}">${a.name}</a> - ${a.playcount} plays </li>n" } out << "</ol>" } }

ICQStatus

Description

Displays a little icon showing your ICQ status. You have to specify the UIN (ICQ's user number) and optional a style for the image. I think image style 21 is the best one. That is the default if you omit the 'style' attribute.

Example

<g:icq uin="12345" style="21"/>

The Code

class IcqTagLib {
  def ICQStatus = { attrs ->
    def uin = attrs.uin?.toInteger()
    if( uin ) {
      def style = attrs.style? attrs.style.toInteger() : 21
      out << '<img src="http://status.icq.com/online.gif?icq='+uin+'&img='+style+'"/>'
    }
  }
}

&nbsp;&nbsp;

Remote Paginate

Description

Copy of the standard paginate that calls ajax instead.

Code

import org.springframework.web.servlet.support.RequestContextUtils as RCU;

class RemotePaginateTagLib { /** * Creates next/previous links to support pagination for the current controller * * <g:paginate total="$ { Account.count() } " /> */ def remotePaginate = {attrs -> def writer = out if (attrs.total == null) throwTagError("Tag [remotePaginate] is missing required attribute [total]")

if (attrs.update == null) throwTagError("Tag [remotePaginate] is missing required attribute [update]")

def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource") def locale = RCU.getLocale(request)

def total = attrs.total.toInteger() def update = attrs.update def action = (attrs.action ? attrs.action : (params.action ? params.action : "list")) def offset = params.offset?.toInteger() def max = params.max?.toInteger() def maxsteps = (attrs.maxsteps ? attrs.maxsteps.toInteger() : 10)

if (!offset) offset = (attrs.offset ? attrs.offset.toInteger() : 0) if (!max) max = (attrs.max ? attrs.max.toInteger() : 10)

def linkParams = [offset: offset - max, max: max] if (params.sort) linkParams.sort = params.sort if (params.order) linkParams.order = params.order if (attrs.params) linkParams.putAll(attrs.params)

def linkTagAttrs = [url: "#"] if (attrs.controller) { linkTagAttrs.controller = attrs.controller } if (attrs.id != null) { linkTagAttrs.id = attrs.id }

// determine paging variables def steps = maxsteps > 0 int currentstep = (offset / max) + 1 int firststep = 1 int laststep = Math.round(Math.ceil(total / max))

// display previous link when not on firststep if (currentstep > firststep) { linkTagAttrs.class = 'prevLink' writer << link(linkTagAttrs.clone()) { (attrs.prev ? attrs.prev : messageSource.getMessage('paginate.prev', null, messageSource.getMessage('default.paginate.prev', null, 'Previous', locale), locale)) } }

// display steps when steps are enabled and laststep is not firststep if (steps && laststep > firststep) { linkTagAttrs.class = 'step'

// determine begin and endstep paging variables int beginstep = currentstep - Math.round(maxsteps / 2) + (maxsteps % 2) int endstep = currentstep + Math.round(maxsteps / 2) - 1

if (beginstep < firststep) { beginstep = firststep endstep = maxsteps } if (endstep > laststep) { beginstep = laststep - maxsteps + 1 if (beginstep < firststep) { beginstep = firststep } endstep = laststep }

// display firststep link when beginstep is not firststep if (beginstep > firststep) { linkParams.offset = 0 linkTagAttrs.onclick = getOnClick(update, linkParams.offset, params) writer << link(linkTagAttrs.clone()) {firststep.toString()} writer << '<span class="step">..</span>' }

// display paginate steps (beginstep..endstep).each {i -> if (currentstep == i) { writer << "<span class="currentStep">${i}</span>" } else { linkParams.offset = (i - 1) * max linkTagAttrs.onclick = getOnClick(update, linkParams.offset, params) writer << link(linkTagAttrs.clone()) {i.toString()} } }

// display laststep link when endstep is not laststep if (endstep < laststep) { writer << '<span class="step">..</span>' linkParams.offset = (laststep - 1) * max linkTagAttrs.onclick = getOnClick(update, linkParams.offset, params) writer << link(linkTagAttrs.clone()) { laststep.toString() } } }

// display next link when not on laststep if (currentstep < laststep) { linkTagAttrs.class = 'nextLink' linkParams.offset = offset + max linkTagAttrs.onclick = getOnClick(update, linkParams.offset, params) writer << link(linkTagAttrs.clone()) { (attrs.next ? attrs.next : messageSource.getMessage('paginate.next', null, messageSource.getMessage('default.paginate.next', null, 'Next', locale), locale))

} } }

/* make more object oriented? */

private String getOnClick(update, offset, params) { def contextPath = servletContext.getContextPath()

def path = contextPath + "/" + params.controller + "/" + params.action def paramString = "?offset=" + offset.toString() params.each { if (it.key != "controller" && it.key != "action" && it.key != "offset" ) { paramString += "&amp;" + it } }

def onclick = "new Ajax.Updater('" + update + "','" + path onclick += paramString + "'," onclick += "{asynchronous:true, evalScripts:true,on404:function(e){alert('not found');}});return false;" return onclick }

}

h3 button

description

Have you ever need a button that will direct you to another controller / action without actually submitting the form ? this button is influenced by seam's s:button but grails style

h4 how to use try using it as you use g:link, it is meant to be the same way as g:link but renders as button

h4 the code

def button = { attrs, body ->
        def name = attrs.remove('name')
        def id = attrs.remove('id')
        def value = attrs.remove('value')
        def params = attrs.remove('params')
        def controller = attrs.remove('controller')
        def action = attrs.remove('action')
        def url = [:]
        def temp = attrs.remove('url')
        if (temp != null) {
            url = temp
        } else {
            if (controller != null)
                url.put("controller", controller)
            if (action != null)
                url.put("action", action)
            if (id != null)
                url.put("id", id)
        }
        def link = createLink(url:url, params:params)
        out << "<input type="button" onclick="location.href='$link'""
        if (value != null)
            out << "value="$value""
        out << ">${body()}</input>"
    }

the code

Select Tag

description

This is an enhanced version of <g:select> tag.

h4 how to use Like the <g:select> tag +

<select:select
   optionKey="id"
   from="${UserGroup.list()}"
   name="userGroup.id"
   value="${userGroupPermission?.userGroup?.id}"
   optionValue="name"                                
   valueMessagePrefix="label.constant.defaultUserGroup"/>
Now, the i18n message will be found by optionValue. If there is no optionValue, then the optionKey will be used.

h4 the code

private String optionValueToString(def el, def optionValue) {
        if (optionValue instanceof Closure) {
            return optionValue(el).toString().encodeAsHTML()
        }

el[optionValue].toString().encodeAsHTML() }