(Quick Reference)

9 Creating Custom UI Sets - Reference Documentation

Authors: Marc Palmer (marc@grailsrocks.com)

Version: 1.0.RC2

9 Creating Custom UI Sets

If you cannot find an existing UI Set that generates markup compatible with your chosen or in-house JS/CSS framework, you can create your own. Search the Grails plugin repository for plugins that provide UI Sets.

Creating a UI Set is significantly more work than creating a theme because you need to supply the structural markup for all the UI tags.

However you can create a UI Set based on the _default provided by Platform UI, or fork a UI Set that already exists in your application i.e. one provided by a plugin you have installed. See the create-ui-set and fork-ui-set script documentation.

9.1 UI Set Resources

UI Sets typically rely on some resources for styling and functionality.

The current UI Set's resources are included in a page using the resources tag - although you do not need to do this if using Themes, as the theme's UI Set resources will be included automatically.

To declare resources for the UI Set you just define a resource module with the name convention "ui.<uisetname>":

modules = {
    // Resources for your custom UI Set
    'ui.Bootstrap' {
        dependsOn 'jquery', 'bootstrap'
        
        resource id:'styling', url:[plugin:'bootstrap-ui', dir:'css', file:'ui-styling.css']
        resource id:'hooks', url:[plugin:'bootstrap-ui', dir:'js', file:'bootstrap-hooks.js']
    }
}

9.2 CSS Classes

Sometimes the structural markup for a UI Set is semantically compatible with what you want to achieve, but an application developer may want to change the CSS class names used for some elements to achieve the styling they want, while still using your UI Set.

They may for example want to use custom CSS and override your UI Set CSS resources and change class names for a table to avoid a clash with some other CSS library they use.

To enable this, UI Tags resolve CSS classes for the common UI elements in such a way that the UI Set developer can provide defaults, and the application developer can override these in Config.

The CSS class name is resolved for you by the UI Tags and is passed into the GSP template used for the UI element. As UI Set developer it is your responsibility to use this class name variable on the correct element.

The CSS Class configuration is performed using Platform Core namespaced config, and here is an example for a plugin that might configure a Bootstrap UI Set for the correct Bootstrap CSS classes:

def doWithConfig = { config ->
    platformUi {
        ui.Bootstrap.actions.cssClass = 'form-actions'
        ui.Bootstrap.button.cssClass = 'btn'
        ui.Bootstrap.tab.cssClass = 'tab-pane'
        ui.Bootstrap.tabs.cssClass = 'nav nav-tabs'
        ui.Bootstrap.field.cssClass = ''
        ui.Bootstrap.input.cssClass = 'input-xlarge'
        ui.Bootstrap.invalid.cssClass = 'invalid'
        ui.Bootstrap.table.cssClass = 'table table-striped'
        ui.Bootstrap.tr.cssClass = ''
        ui.Bootstrap.trOdd.cssClass = ''
        ui.Bootstrap.trEven.cssClass = ''
        ui.Bootstrap.th.cssClass = ''
        ui.Bootstrap.carousel.cssClass = 'carousel'
        ui.Bootstrap.slide.cssClass = 'item'
        ui.Bootstrap.form.cssClass = 'form-horizontal'
    }
}

You can see here for example that the default input field CSS class is "input-xlarge" to create large input fields. The application developer can trivially override this in their application Config to get different sized fields:

plugin.platformUi.ui.Bootstrap.input.cssClass = 'input-large'

9.3 Configuration

UI Sets can change their behaviour based on configuration.

Config paths must be named according to Platform Core namespaced plugin config, within the namespace of the plugin that supplies the UI Set

You can add and declare any configuration options you wish, and for example you could provide more options for CSS Class customisation, or a global switch to change default colours or colour schemes.

In addition there are some Platform UI configuration options that affect UI Sets:

PathTypeDescription
ui.setStringThe default UI Set to use if none is specified on the request or implied by the request Theme. Defaults to "_default" to use the "demo" UI Set that comes shipped in Platform UI

9.4 UI Set GSP Template Reference

The GSP templates used to create a UI Set are detailed in this section. This guide is intended for developers creating their own UI Sets or themes.

Resolution of templates

Templates for UI Set tags are resolved to the following view path within your plugin or application:

/grails-app/views/_ui/<UiSetName>/_<uiTagName>.gsp

Sometimes you may need to work around a problem in a UI Set used by your application, where that UI Set is provided by another plugin or theme. You can do this easily by putting your own GSP in the correct location.

NOT IMPLEMENTED CORRECTLY YET: Themes override the UI set templates by specifying replacement templates in /grails-app/views/_themes/<ThemeName>/_ui/<UiSetName>/_<uiTagName>.gsp. Current implementation is wrong.

Contract for GSP Templates

The sections that follow set out the requirements for each UI tag's GSP template in terms of expected behaviour and the information supplied to the GSP template via the model.

Attributes

All UI tag attributes that are part of the UI Tag contracts are extracted and passed to the GSP template model. Any remaining attributes are passed to the model in the "attrs" variable.

Use of such attributes is typically to pass them through to underlying implementation tags such as g:form, but can be used for specific customisations over and above the UI Set specifications provided the core contract for each UI Tag's attributes is honoured.

9.4.1 The Avatar template

The _avatar.gsp template is used to render the avatar tag.

Contract

The avatar template must render an avatar image for the given user identity.

The template can use any avatar provider. The default implementation uses Gravatar.

The user identity could be any string - but is typically an email address. For example an avatar template implementation that supports local application avatars for account user ids and public internet Gravatars could sense the presence of the email address and switch its behaviour for user ids.

Variables available

NameDescription
avatarClassThe CSS class to use for the avatar container. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
userThe id of the user for whom you wish to render an avatar. Often an email address, but depends on avatar service used by UI Set
defaultSrcThe absolute URL of the default avatar image
sizeThe size in pixels of the avatar to retrieve, i.e. "30"
attrsThe attributes to pass through to the image tag.

Example template implementation

<g:set var="_gravatarURL" value="https://secure.gravatar.com/avatar/"/>
<g:set var="_gravatarArgs" value="${user.encodeAsMD5()}?d=${defaultSrc?.encodeAsURL()}"/>
<img src="${_gravatarURL}${_gravatarArgs}"
     class="${p.joinClasses(values:[avatarClass,classes])}"
     ${ui.attributes(exclude:'src')}/>

9.4.2 The Block template

The _block.gsp template is used to render the block tag.

Contract

The block template should render a container around the contents of the tag body.

It is often the case that such blocks will be separated visually from the surrounding content, for example with a border or similar.

Variables available

NameDescription
blockClassThe CSS class to use for the block container. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe content of the actions section, the markup provided by the caller
attrsThe attributes to pass through to the tag that is the container for the actions

Example template implementation

<div class="${p.joinClasses(values:[blockClass, classes])}">
    ${bodyContent}
</div>
The _carousel.gsp template is used to render the carousel tag.

Contract

A carousel is a UI element typically used on home pages to rotate through a list of content panels, usually by sliding slides on and off or fading in and out.

The style of the animation, whether there are any UI elements to pause or advance the slides are not sepcified in any way.

This GSP template must simply render the correct structural markup and take care of any requirements of the JS code required, and render the various slides contained within the ui:carousel tag appropriately.

Usually frameworks simply use a list of divs and selectively show and hide them, much like tabs.

Slides must be shown in the following order:

  • The slide marked "active" first
  • All other slides, in order they were defined in the slides variable

Variables available

NameDescription
idThe id for the outermost element supplied by the developer or auto-generated
carouselClassThe CSS class to use for the carousel container. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
slidesA list of the slides to include, each with properties id, active, bodyContent
attrsThe attributes to pass through to the container tag.

Example template implementation

<div id="${id}" class="${p.joinClasses(values:[carouselClass, classes])}">
    <div class="carousel-inner">
        <g:each in="${slides}" var="s">${s.bodyContent}</g:each>
    </div>
    <a class="carousel-control left" href="#${id}"
        data-slide="prev">‹</a>
    <a class="carousel-control right" href="#${id}"
        data-slide="next">›</a>
</div>

9.4.4 The Slide template

The _slide.gsp template is used to render the slide tag.

Contract

This template must render the markup required to encapsulate a single slide for a carousel component.

Typically a container div will be required with some kind of indicator class or HTML5-data attribute.

The output of the slide template is collected together and passed to the template for the carousel tag.

Variables available

NameDescription
idUnique slide id - will be passed in by the user or auto generated for you
slideClassThe CSS class to apply for slides, from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe body of the slide
attrsThe attributes to pass through to the outermost container element.

Example template implementation

<div id="slide_${id}" class="${p.joinClasses(
    values:[slideClass, active ? ' active' : '', classes])}"${ui.attributes()}>
    ${bodyContent}
</div>

9.4.5 The Form template

The _form.gsp template is used to render the form tag.

Contract

This template must render an HTML form using the standard attributes supported by the Grails g:form tag.

It can apply any structural HTML or decoration that it desires. It may for example apply special CSS classes to toggle between vertical or horizontal label and field layout, and it could even use Config to toggle between these so that the application developer can change the look and feel across their app in one place.

Variables available

NameDescription
formClassThe CSS class to use for the primary container of the form HTML, obtained from UI Set config.
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe body of the form to be rendered
attrsThe attributes to pass through to the g:form tag. The form is expected to support the standard g:form attributes but need not call g:form as long as it is compatible with the standard g:form attributes.

Example template implementation

<p:callTag tag="g:form" class="${p.joinClasses(values:[formClass,classes])}"
    attrs="${attrs}" bodyContent="${bodyContent}"/>

This example simply delegates to g:form.

9.4.6 The Actions template

The _actions.gsp template is used to render the actions tag.

Contract

The actions template must render the block of form actions supplied in the body of the tag.

The caller uses the ui:actions tag to demarcate the part of a ui:form that contains the actions the user can perform - such as "Save or cancel" button and links, or perhaps other data navigation buttons etc.

Sometimes this is separated visually from the rest of the form, and sometimes it is repeated at the top and bottom of forms.

This is entirely up to the UI Set implementation however. ui:actions is to be called only once or not at all in each ui:form.

Variables available

NameDescription
actionsClassThe CSS class to use for the actions container. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe content of the actions section, the markup provided by the caller
attrsThe attributes to pass through to the tag that is the container for the actions

Example template implementation

<div class="${p.joinClasses(values:[actionsClass, classes])}">
    ${bodyContent}
</div>

9.4.7 The Field template

The _field.gsp template is used to render the field tag.

Contract

This GSP template must render all the structural markup required to render a label, input widget and optionally field errors and hints.

The rendering of fields is rather complex. There are various approaches to rendering the markup of input fields, such as Bean Fields and the preferred Grails Fields plugin. This template however is only responsible for the structural layout of the various field elements, not the input itself. For the details of how inputs are actually rendered see the input template reference.

There are several contractual considerations in this template.

Your implementation must support:

  • The ability for developers to customize the various elements using ui:fieldLabel, ui:fieldHint, ui:fieldErrors and ui:fieldInput. These nested tags that can be used within ui:field simply capture the relevant body markup which is passed in to this tempalte as customXXXX variables
  • Support for rendering multiple errors inline if the UI Set is rendering field errors inline

The ui:field tag handles most of the complexity around this for you. Each inner element of the field (label/errors/input/hint) that is not provided via a nested tag will be resolved as appropriate. For example label, hint and error text is resolved using standard Platform UI i18n rules.

Variables available

NameDescription
idThe HTML id to apply to the input, and for the label to reference
attrsAny extra attributes to pass through to be applied to the outermost container
fieldClassThe CSS class to use for the primary container of the field HTML, obtained from UI Set config.
invalidClassThe CSS class to use for the primary container of the form HTML, in the event of a value being invalid, obtained from UI Set config - in addition to the fieldClass value
classesA string containing the list of CSS classes to apply to the outer container
labelThe label text for the label, already resolved against i18n.
hintAn optional hint string to render with the field, already resolved against i18n.
invalidIf value is Groovy true, indicates that the field isn't valid.
requiredIf value is Groovy true, indicates that the field is required.
nameThe name of the field and/or name of the bean property of the field.
beanObjectThe original root bean supplying the value, if any.
valueThe value of the field to use if a the bean property value is differentx
typeThe explicit type override for the field (see input tag) if any
errorsThe list of field error messages, already resolved against i18n. Groovy false if there are no errors.
inputThe markup to use for the input field, if customInput has no value
customLabelThe custom label markup to use. If defined, must be used as the full markup for the <label>, but still contained within the structural markup required by the UI Set
customHintThe custom hint markup to use. If defined, must be used as the full markup for the hint, but still contained within the structural markup required by the UI Set
customInputThe custom input markup to use. If defined, must be used as the full markup for the input widget, but still contained within the structural markup required by the UI Set
customErrorsThe custom error markup to use. If defined, must be used as the full markup for the errors, but still contained within the structural markup required by the UI Set

Example template implementation

<div class="${p.joinClasses(values:[fieldClass, classes])}">
    <g:if test="${customLabel}">
        ${customLabel}
    </g:if>
    <g:else>
        <label for="${id.encodeAsHTML()}">${label.encodeAsHTML()}</label>
    </g:else>

    <g:if test="${customInput}">
        ${customInput}
    </g:if>
    <g:else>
        ${input}
    </g:else>

    <g:if test="${customHint}">
        ${customHint}
    </g:if>
    <g:elseif test="${hint}">
        <span class="${hintClass}">${hint.encodeAsHTML()}</span>
    </g:elseif>

    <g:if test="${customErrors}">
        ${customErrors}
    </g:if>
    <g:elseif test="${errors}">
        <g:each in="${errors}" var="err">
            <span class="${errorClass}">${err.encodeAsHTML()}</span>
        </g:each>
    </g:elseif>
</div>

9.4.8 The input template

The _input.gsp template is used to render the input tag.

Contract

This GSP template must render just the input widget for a single field.

This template must be smart: when rendering a field for a bean property it must attempt to automatically work out the correct field type to use, and to apply relevant constraints from the command or domain class.

These tasks are normally performed using a plugin such as Bean Fields or the preferred Grails Fields.

This template should, where possible, delegate to such a library to avoid reinventing the wheel. However this is not a requirement and you can provide any alternative implementation as long as this contract is honoured.

This is probably the most complicated UI Set template to implement. If possible use an existing implementation.

Your implementation should support:

  • Rendering arbitrary fields
  • Auto-sensing an appropriate field type for the current field value
  • Manual field type overrides
  • Rendering fields for an arbitrary property path starting at a root bean
  • Relevant user input constraints based on the constraints of the bean (if any)
  • Giving a visual indication of whether or not a field is required
  • Giving a visual indication of whether there is an error on the field

The ui:field tag handles most of the complexity around this for you.

Variables available

NameDescription
idThe HTML id to apply to the input, and for the label to reference
attrsAny extra attributes to pass through to be applied to the outermost container
inputClassThe CSS class to use for the input element, obtained from UI Set config.
invalidClassThe CSS class to use for the primary container of the form HTML, in the event of a value being invalid, obtained from UI Set config - in addition to the fieldClass value
classesA string containing the list of CSS classes to apply to the outer container
invalidIf value is Groovy true, indicates that the field isn't valid.
requiredIf value is Groovy true, indicates that the field is required.
nameThe name of the field and/or name of the bean property of the field.
beanObjectThe original root bean supplying the value, if any.
valueThe value of the field to use if a the bean property value is differentx
typeThe explicit type override for the field (see input tag) if any

Example template implementation

<g:if test="${beanObject}">
    <g:set var="value" value="${beanObject[name]}"/>
</g:if>
<g:if test="$">
    <input id="${id}" name="${name.encodeAsHTML()}" 
        class="${p.joinClasses(values:[inputClass, classes])}"
        type="${type}" value="${value?.encodeAsHTML()}"/>
</g:if>
<g:elseif test="${type == 'select'}">
    <g:select id="${id}" name="${name}" value="${value}"
        noSelection="[null:'No value']" from="${[ [value:value] ]}"/>
</g:elseif>
<g:elseif test="${type == 'textarea'}">
    <textarea id="${id}" name="${name}">${value?.encodeAsHTML()}</textarea>
</g:elseif>
<g:elseif test="${type == 'datepicker'}">
    <g:datePicker id="${id}" name="${name}" value="${value}"/>
</g:elseif>

This naïve example does not attempt to support bean properties, and does nothing with required or invalid indications. Here is an example using Bean Fields:

<bean:inputTemplate>
${field}
</bean:inputTemplate>
<bean:selectTemplate>
${field}
</bean:selectTemplate>
<bean:checkBoxTemplate>
${field}
</bean:checkBoxTemplate>
<bean:radioTemplate>
${field}
</bean:radioTemplate>
<bean:textAreaTemplate>
${field}
</bean:textAreaTemplate>
<g:if test="${beanObject}">        
        <g:if test="${type == 'password'}">
            <bean:field type="password" beanName="dummy"
                bean="${beanObject}" property="${name}" noLabel="${true}"/>
        </g:if>
        <g:else>
            <bean:field beanName="dummy" bean="${beanObject}"
                property="${name}" noLabel="${true}"/>
        </g:else>
</g:if>
<g:else>
    <input id="${id}" class="
        ${p.joinClasses(values:[classes, invalid ? invalidClass : ''])}"
        type="${type}" name="${name}" value="${value}"/>
</g:else>

This is again somewhat naïve as for non-bean fields it assumes <input> will cover all the cases, which it obviously will not.

9.4.9 The Field Group template

The _fieldGroup.gsp template is used to render the fieldGroup tag.

Contract

The fieldGroup template must render a container around the contents of the tag body.

Sometimes groups of fields will be separated visually from the surrounding content, for example with a border or similar. Usually this is achieved with a <fieldset> tag but this is not always the case, so the template has the flexibility to use any markup it requires.

Variables available

NameDescription
fieldGroupClassThe CSS class to use for the field group container. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe content of the actions section, the markup provided by the caller
attrsThe attributes to pass through to the tag that is the container for the actions

Example template implementation

<fieldset class="${p.joinClasses(values:[fieldGroupClass,classes])}"
  ${ui.attributes(attrs:attrs)}>
    ${bodyContent}
</fieldset>

9.4.10 The Image template

The _image.gsp template is used to render the image tag.

Contract

This template needs to render an inline image, such as a photo or illustration on the site. Images such as these may support zooming via a lightbox or similar UI, or other decoration such as a frame that would not be applied to other <img> tags.

The template must render the image using the same attribute contract as the Resources r:img tag - i.e. the attributes will contain either a uri or a dir/file pair.

Variables available

NameDescription
titleTitle for the image (if any), resolved from an i18n message code if available. The template might render this with special formatting
imageClassThe CSS class to use for the primary container of the image HTML, obtained from UI Set config.
classesA string containing the list of CSS classes to apply to the outer container
attrsThe attributes to pass through to the g:form tag. The form is expected to support the standard g:form attributes but need not call g:form as long as it is compatible.

Example template implementation

<div class="${p.joinClasses(values:[imageClass,classes])}">
    <p:callTag tag="r:img"
        attrs="${attrs}" title="${title.encodeAsHTML()}"/>
</div>

This example simply delegates to r:img and passes through any classes, after applying a container.

The _logo.gsp template is used by the logo tag.

Contract

This template must render the application logo as an inline image.

The logo should include a link to the site's primary URL.

Themes that use pure CSS for logos using e.g. background-image should follow the same conventions for the logo file name and location as per the logo tag definition.

Variables available

NameDescription
logoUriURI of the logo to use
classesString of CSS classes supplied
logoClassClass to apply to the logo element
widthWidth of the logo or empty
heightHeight of the logo or empty

Example template implementation

<a href="${p.siteURL().encodeAsHTML()}">
  <r:img uri="${logoUri.encodeAsHTML()}" 
    class="${p.joinClasses(values:[classes, logoClass])}"
    alt="${p.siteName()}" 
    width="${width}" height="${height}"/></a>

9.4.12 The Message template

The _message.gsp template is used to render the message tag.

Contract

This template must render out a UI message which is essentially a block of unstyled text resolved via i18n, with a message type associated with it.

Variables available

NameDescription
messageClassA string containing the CSS class to apply to the outermost container element
classesA string containing the list of CSS classes to apply to the outer container
typeThe kind of message to use. See messagefor the
bodyContentThe body of the message, without formatting
attrsThe attributes to pass through to the outermost containing element.

Example template implementation

<div class="${p.joinClasses(values:[messageClass, classes])}"${ui.attributes()}>
    <p>${bodyContent}</p>
</div>

9.4.13 The Paginate template

The _paginate.gsp template is used to render the paginate tag.

Contract

This template is responsible for rendering the multi-page navigation controls used for reports, search results and so on.

The paginate tag implementation takes care of the hard parts related to calculating links, what options should be available to the user etc, and this template must simply render the correct markup using the information provided.

Variables available

NameDescription
paginateClassA string containing the CSS class to apply to the outer container
classesA string containing the list of CSS classes to apply to the outer container
nextThe information for the "next" link
prevThe information for the "previous" link
earlierThe information for the "earlier" link, to the list of pages before the current start page number
laterThe information for the "later" link, to the list of pages after the last page number listed
itemsThe list of page items to show UI for, each with properties "link", "active" and "text"
attrsAny extra attributes the use passed in. Usage of this is undefined across UI Sets

Example template implementation

<ul class="${p.joinClasses(values:[paginateClass, classes])}">
    <g:if test="${prev}">
        <li><a href="${prev.link}" class="prev">${prev.text}</a></li>
    </g:if>
    <g:if test="${earlier}">
        <li><a href="${earlier.link}" class="earlier">${earlier.text}</a></li>
    </g:if>
    <g:each in="${items}" var="i">
        <li><g:if test="${i.active}">
                ${i.text}
            </g:if>
            <g:else>
                <a href="${i.link}">${i.text}</a>
            </g:else>
        </li>
    </g:each>
    <g:if test="${later}">
        <li><a href="${later.link}" class="later">${later.text}</a></li>
    </g:if>
    <g:if test="${next}">
        <li><a href="${next.link}" class="next">${next.text}</a></li>
    </g:if>
</ul>

9.4.14 The Primary Navigation template

The _primaryNavigation.gsp template is used to render the primaryNavigation tag.

Contract

This template must render the primary navigation items for the application.

The primary navigation is the top-level site navigation, which typically includes highlighting the current active top level item.

Only the top level items of the default site navigation must be rendered.

The primaryNavigation tag is typically only called from Theme layouts to render the site navigation menu.

See navigation template documentation for details of adherence to Navigation API.

Variables available

NameDescription
primaryNavigationClassA string containing the CSS class to apply to the outermost container element
classesA string containing the list of CSS classes to apply to the outer container
attrsThe attributes to pass through to the outermost containing element.

Example template implementation

<p:callTag tag="nav:primary" attrs="${attrs + 
    [class:p.joinClasses(values:[primaryNavigationClass, classes])]}"/>

9.4.15 The Secondary Navigation template

The _secondaryNavigation.gsp template is used to render the secondaryNavigation tag.

Contract

This template must render the secondary navigation items for the application.

The secondary navigation is the set of navigation items that apply to the currently active primary navigation item.

Only the top level secondary items must be rendered.

The secondaryNavigation tag is typically only called from Theme layouts to render the site navigation menu.

See navigation template documentation for details of adherence to Navigation API.

Variables available

NameDescription
secondaryNavigationClassA string containing the CSS class to apply to the outermost container element
classesA string containing the list of CSS classes to apply to the outer container
attrsThe attributes to pass through to the outermost containing element.

Example template implementation

<p:callTag tag="nav:secondary" attrs="${attrs + 
    [class:p.joinClasses(values:[secondaryNavigationClass, classes])]}"/>

The _navigation.gsp template is used to render the navigation tag.

Contract

This template must render the navigation items from the supplied scope, or default the default application navigation scope if no scope is supplied.

Only the top level items at the scope supplied must be rendered, nested item rendering is not permitted.

An example use case: a plugin may expose a page that lists a set of administration options that are below secondary navigation level, such as:

<theme:zone name="body">
    <ui:block title="Email Confirmation Administration">
        <p>Choose from the following options:</p>
        <ui:navigation scope="plugin/emailconfirmation.admin"/>
    </ui:block>
</theme:zone>

Variables available

NameDescription
navigationClassA string containing the CSS class to apply to the outermost container element
scopeA string identifying the navigation scope to render
classesA string containing the list of CSS classes to apply to the outer container
attrsThe attributes to pass through to the outermost containing element.

Example template implementation

<p:callTag tag="nav:menu" attrs="${attrs + 
    [class:p.joinClasses(values:[navigationClass, classes]), scope:scope]}"/>

9.4.17 The Table template

The _table.gsp template is used to render the table tag.

Contract

This template is used to render tables. Typically this would use HTML <table> but this is not a concrete requirement - just that it is represented to the user as a columnar table.

The nested ui:tr and ui:th calls allow this template to apply extra logic.

Variables available

NameDescription
tableClassThe CSS class to use for the primary container of the table HTML, obtained from UI Set config.
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe body of the table
attrsThe attributes to pass through to the outermost container element.

Example template implementation

<table class="${p.joinClasses(values:[tableClass, classes])}"${ui.attributes()}>
    ${bodyContent}
</table>

9.4.18 The Table Row template

The _tr.gsp template is used to render the tr tag.

Contract

This template is used to render table rows for the tr tag.

This can be used to render extra decoration.

Variables available

NameDescription
trClassCSS class to use for the element, from UI Set config
oddEvenClassThe CSS class to use for the row, to indicate if it is even/odd. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
rowThe current row index in the table (zero based)
bodyContentThe body of the row
attrsThe attributes to pass through to the outermost element.

Example template implementation

<tr class="${p.joinClasses(values:[trClass, oddEvenClass, classes])}"${ui.attributes()}>
    ${bodyContent}
</tr>

9.4.19 The Table Heading template

The _th.gsp template is used to render the th tag.

Contract

This template is used to render table header cells for the th tag.

This can be used to render out metadata or extra elements for e.g. sortable columns, and uses Platform UI conventions to resolve i18n heading text.

Variables available

NameDescription
thClassCSS class to use for the element, from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe body of the heading
attrsThe attributes to pass through to the outermost element

Example template implementation

<th class="${p.joinClasses(values:[thClass, classes])}"${ui.attributes()}>
    ${bodyContent}
</th>

9.4.20 The Tabs template

The _tabs.gsp template is used to render the tabs tag.

Contract

This template renders the markup requires to define a set of tabs.

Tabs have a title for each tab, and can either link to a new URL or hide and display different tabs of content embedded in the page.

The tabs are provided in a model variable, having been collected from the tab template used to implement the tab tag.

Variables available

NameDescription
idThe id of the outermost element supplied by application developer or auto-generated
tabsClassThe CSS class to use for the tabs container. Value comes from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
tabsThe list of tabs, each with active, id and bodyContent properties
attrsThe attributes to pass through to the outermost container element.

Example template implementation

<ul class="${p.joinClasses(values:[tabsClass, classes])}" ${ui.attributes()}>
<g:each in="${tabs}" var="t">
    <li ${t == active ? 'class="active"' : ''}>
        <a href="${t.link ?: ('#'+t.id).encodeAsHTML()}">${t.title.encodeAsHTML()}</a>
    </li>
</g:each>
</ul>
<div class="tabBodies">
    <g:each in="${tabs}" var="t">
        ${t.bodyContent}
    </g:each>
</div>

This example renders a <ul> list containing the tabs and links, and then a set of <div> containing the tab contents.

9.4.21 The Tab template

The _tab.gsp template is used to render the tab tag.

Contract

This template must render the markup required to define a tab within the context of a tabs tag.

The tabs template collects all the outputs of each tab invocation rendered by this template, and the tabs template then iterates over them to render the necessary markup.

Variables available

NameDescription
idUnique id of the tab in this page. Supplied by user or auto generated
tabClassCSS class to apply to the outermost container element, from UI Set config
classesA string containing the list of CSS classes to apply to the outer container
bodyContentThe tab content
activeTrue if the tab is currently the active tab
titleTitle text for the tab, resolved from i18n
linkTarget link for the tab if it is a link to another URL, may be null
attrsThe attributes to pass through to the outermost container element.

Example template implementation

<g:if test="$">
    <div id="tab_${id}" class="${p.joinClasses(
        values:[active ? ' active' : '', tabClass, classes])}"
        ${ui.attributes()}>
        ${bodyContent}
    </div>
</g:if>

9.5 Utility Tags

There are a set of utility tags and functions provided to make it easier to implement UI Set tag templates.

Documentation will be improved in future, but for now see:

attributes tag

cssClass tag

listSets tag

Also see the Platform Core TagLibUtils class javadocs for a set of useful methods.