(Quick Reference)

4 Customizing Field Rendering

Version: 7.0.0-SNAPSHOT

4 Customizing Field Rendering

The plugin resolves the GSP template used for each property according to conventions. You can override the rendering based on the class and property name or the property type. The f:field tag looks for a template called _wrapper.gsp, the f:widget tag looks for a template called _widget.gsp, the f:display tag looks for a template called _displayWrapper.gsp.

Breaking changes in version 1.5

In version 1.5 a new template was introduced _displayWidget.gsp. This is the corollary of _widget.gsp for fields that are read-only, i.e. it is responsible for rendering just the markup for the field itself. Furthermore, the default names of all the templates were changed in this version, in the interest of clarity and consistency. The changes to the template names are summarized below:

Table 1. Template Name Changes
Old Template Name (before v.1.5) New Template Name (v.1.5 onwards)

_field.gsp

_wrapper.gsp

_display.gsp

_displayWrapper.gsp

N/A

_displayWidget.gsp

Users upgrading to 1.5 from a previous version should either rename their templates (recommended) or add the following to grails-app/conf/application.yml to change the default templates names to the old names

grails:
    plugin:
        fields:
            wrapper: field
            displayWrapper: display
            widget: input
            displayWidget: displayWidget

1. Locating Field Templates by Convention

The template for a field is chosen by a convention using the names of the controller, action, bean class, bean property, theme, etc. All the tags will look for templates in the following directories in decreasing order of preference:

  • grails-app/views/controllerNamespace/controllerName/actionName/propertyName/_themes/themeName/

  • grails-app/views/controllerNamespace/controllerName/actionName/_themes/themeName/propertyType/

  • grails-app/views/controllerNamespace/controllerName/actionName/_themes/themeName/

  • grails-app/views/controllerNamespace/controllerName/propertyName/_themes/themeName/

  • grails-app/views/controllerNamespace/controllerName/_themes/themeName/propertyType/

  • grails-app/views/controllerNamespace/controllerName/_themes/themeName/

  • grails-app/views/controllerName/actionName/propertyName/_themes/themeName/

  • grails-app/views/controllerName/actionName/_themes/themeName/propertyType/

  • grails-app/views/controllerName/actionName/_themes/themeName/

  • grails-app/views/controllerName/propertyName/_themes/themeName/

  • grails-app/views/controllerName/_themes/themeName/propertyType/

  • grails-app/views/controllerName/_themes/themeName/

  • grails-app/views/_fields/_themes/themeName/class/propertyName/

  • grails-app/views/_fields/_themes/themeName/superclass/propertyName/

  • grails-app/views/_fields/_themes/themeName/associationType/

  • grails-app/views/_fields/_themes/themeName/propertyType/

  • grails-app/views/_fields/_themes/themeName/propertySuperclass/

  • grails-app/views/_fields/_themes/themeName/default/

  • grails-app/views/controllerNamespace/controllerName/actionName/propertyName/

  • grails-app/views/controllerNamespace/controllerName/actionName/propertyType/

  • grails-app/views/controllerNamespace/controllerName/actionName/

  • grails-app/views/controllerNamespace/controllerName/propertyName/

  • grails-app/views/controllerNamespace/controllerName/propertyType/

  • grails-app/views/controllerNamespace/controllerName/

  • grails-app/views/controllerName/actionName/propertyName/

  • grails-app/views/controllerName/actionName/propertyType/

  • grails-app/views/controllerName/actionName/

  • grails-app/views/controllerName/propertyName/

  • grails-app/views/controllerName/propertyType/

  • grails-app/views/controllerName/

  • grails-app/views/_fields/class/propertyName/

  • grails-app/views/_fields/superclass/propertyName/

  • grails-app/views/_fields/associationType/

  • grails-app/views/_fields/propertyType/

  • grails-app/views/_fields/propertySuperclass/

  • grails-app/views/_fields/default/

The variables referenced in these paths are:

Table 2. Referenced Variables
Name Description

controllerName

The name of the current controller (if any).

actionName

The name of the current action (if any).

themeName

Theme name specified as value of theme attribute (Optional).

class

The bean class. For simple properties this is the class of the object passed to the bean attribute of the f:field or f:widget tag but when the property attribute was nested this is the class at the end of the chain. For example, if the property path was employees[0].address.street this will be the class of address.

superclass

Any superclass or interface of class excluding Object, GroovyObject, Serializable, Comparable and Cloneable and those from GORM.

propertyName

The property name at the end of the chain passed to the property attribute of the f:field or f:widget tag. For example, if the property path was employees[0].address.street then this will be street.

propertyType

The type of the property at the end of the chain passed to the property attribute of the f:field or f:widget tag. For example, for a java.lang.String property this would be string.

propertySuperclass

Any superclass or interface of propertyType excluding Object, GroovyObject, Serializable, Comparable and Cloneable.

associationType

One of 'oneToOne', 'oneToMany', 'manyToMany' or 'manyToOne'. Only relevant if the property is a domain class association.

All class names are camel-cased simple forms. For example java.lang.String becomes string, and com.project.HomeAddress becomes homeAddress.

Templates are resolved in this order so that you can override in the more specific circumstance and fall back to successively more general defaults. For example, you can define a field template for all java.lang.String properties but override a specific property of a particular class to use more specialized rendering.

Templates in plugins are resolved as well. This means plugins such as Joda Time can provide default rendering for special property types. A template in your application will take precedence over a template in a plugin at the same 'level'. For example if a plugin provides a grails-app/views/_fields/string/_widget.gsp the same template in your application will override it but if the plugin provides grails-app/views/_fields/person/name/_widget.gsp it would be used in preference to the more general template in your application.

For most properties the out-of-the-box defaults should provide a good starting point.

2. Locating Templates Conventionally Example

Imagine an object of class Employee that extends the class Person and has a String name property.

You can override the template f:field uses with any of these:

  • grails-app/views/controllerName/actionName/name/_themes/themeName/_wrapper.gsp

  • grails-app/views/controllerName/actionName/name/_wrapper.gsp

  • grails-app/views/controllerName/actionName/string/_wrapper.gsp

  • grails-app/views/controllerName/actionName/_wrapper.gsp

  • grails-app/views/controllerName/name/_wrapper.gsp

  • grails-app/views/controllerName/string/_wrapper.gsp

  • grails-app/views/controllerName/_wrapper.gsp

  • grails-app/views/_fields/employee/name/_wrapper.gsp

  • grails-app/views/_fields/person/name/_wrapper.gsp

  • grails-app/views/_fields/string/_wrapper.gsp

  • grails-app/views/_fields/default/_wrapper.gsp

override the template f:widget uses with any of these:

  • grails-app/views/controllerName/actionName/name/_themes/themeName/_widget.gsp

  • grails-app/views/controllerName/actionName/name/_widget.gsp

  • grails-app/views/controllerName/actionName/string/_widget.gsp

  • grails-app/views/controllerName/actionName/_widget.gsp

  • grails-app/views/controllerName/name/_widget.gsp

  • grails-app/views/controllerName/string/_widget.gsp

  • grails-app/views/controllerName/_widget.gsp

  • grails-app/views/_fields/employee/name/_widget.gsp

  • grails-app/views/_fields/person/name/_widget.gsp

  • grails-app/views/_fields/string/_widget.gsp

  • grails-app/views/_fields/default/_widget.gsp

And override the template f:display uses with any of these:

  • grails-app/views/controllerName/actionName/name/_themes/themeName/_displayWrapper.gsp

  • grails-app/views/controllerName/actionName/name/_displayWrapper.gsp

  • grails-app/views/controllerName/actionName/string/_displayWrapper.gsp

  • grails-app/views/controllerName/actionName/_displayWrapper.gsp

  • grails-app/views/controllerName/name/_displayWrapper.gsp

  • grails-app/views/controllerName/string/_displayWrapper.gsp

  • grails-app/views/controllerName/_displayWrapper.gsp

  • grails-app/views/_fields/employee/name/_displayWrapper.gsp

  • grails-app/views/_fields/person/name/_displayWrapper.gsp

  • grails-app/views/_fields/string/_displayWrapper.gsp

  • grails-app/views/_fields/default/_displayWrapper.gsp

During template development it is usually recommended to disable template caching in order to allow the plugin to recognize new/renamed/moved templates without restarting the application. See the "Performance" section of the guide for the exact settings.

3. See Template Seach Path

The plugin logs which locations it is checking for templates as debug log. You can enable this by defining a logger in logback.groovy

logger('grails.plugin.formfields.FormFieldsTemplateService', DEBUG,['STDOUT'])

The can disable the caching in application.yml using:

grails:
    plugin:
        fields:
            disableLookupCache: true

4. Default Behaviour - Using Grails Widget Tags

If no template override is found the plugin will use the standard grails input tags (e.g. g:select, g:checkbox, g:field) for rendering input controls. Using f:field you can pass extra arguments (e.g. optionKey, optionValue) through to these tags by prefixing them with widget-, e.g.

<f:field bean="person" property="gender" widget-optionValue="name"/>

5. Template parameters

The f:field and f:widget tags will pass the following parameters to your templates or to the body of f:field if you use one:

Table 3. Template Parameters
Name Type Description

bean

Object

The bean attribute as passed to the f:field or f:widget tag.

property

String

The property attribute as passed to the f:field or f:widget tag. This would generally be useful for the name attribute of a form input.

type

Class

The property type.

label

String

The field label text. This is based on the label attribute passed to the f:field or f:widget tag. If no label attribute was used the label is resolved by convention - see below.

value

Object

the property value. This can also be overridden or defaulted if the value or default attribute was passed to f:field or f:widget.

constraints

ConstrainedProperty

The constraints for the property if the bean is a domain or command object.

persistentProperty

DomainProperty

The persistent property object if the bean is a domain object.

errors

List<String>

The error messages for any field errors present on the property. If there are no errors this will be an empty List.

required

boolean

true if the field is required, i.e. has a nullable: false or blank: false constraint.

invalid

boolean

true if the property has any field errors.

prefix

String

A string (including the trailing period) that should be appended before the input name such as name="${prefix}propertyName". The label is also modified.

In addition f:field passes the following parameters:

Table 4. Parameter Names from f:field

Name

Type

Description

widget

String

The output of f:widget for the current bean and property if f:field was used without a tag body, otherwise the output of the tag body.

If the bean attribute was not supplied to f:field then bean, type, value and persistentProperty will all be null.

6. Field labels

If the label attribute is not supplied to the f:field tag then the label string passed to the field template is resolved by convention. The plugin uses the following order of preference for the label:

  • An i18n message using the key 'beanClass.path.label'. For example when using <f:field bean="authorInstance" property="book.title"/> the plugin will try the i18n key author.book.title.label. If the property path contains any index it is removed so <f:field bean="authorInstance" property="books[0].title"/> would use the key author.books.title.label.

  • For classes using the same bean class as properties, it is possible to get a key without the class name prefixed. If the configuration value grails.plugin.fields.i18n.addPathFromRoot is set to true (default: false). Example: a class Publisher has two Address properties authorAddress and printAddress. With addPathFromRoot=true they will share the key address.city.label. The same goes if Author and Publisher had a Book book, the key would be book.title.label, and if they both had a List<Book> books the key would be books.title.label

  • An i18n message using the key 'objectType.propertyName`.label’. For example when using <f:field bean="personInstance" property="address.city"/> the plugin will try the i18n key address.city.label.

  • The natural property name. For example when using <f:field bean="personInstance" property="dateOfBirth"/> the plugin will use the label "Date Of Birth".

7. Locating Field Templates Directly

Rather than relying on the convention described previously to locate the template(s) to be used for a particular field, it is instead possible to directly specify the directory containing the templates. This feature was introduced in version 1.5.

  • The wrapper attribute can be used with the f:field or f:display tags to specify the directory containing the _wrapper.gsp or _displayWrapper.gsp template to be used

  • The widget attribute can be used with the f:field or f:display tags to specify the directory containing the _widget.gsp or _displayWidget.gsp template to be used

  • If the wrapper and widget templates both have the same value, the templates attribute can be used instead as a shorthand. For example:

<f:field property="startDate" templates="bootstrap3" />

is equivalent to:

<f:field property="startDate" wrapper="bootstrap3" widget="bootstrap3" />

if theme is specified, theme will be searched first to find the templates For example

<f:field property="startDate" templates="custom" theme="bs-horizontal"/>

Will search the templates first in \_fields/_themes/bs-horizontal/custom and then \_fields/custom

If a direct location is specified, and the templates cannot be found therein, the plugin will fall back to locating templates by convention.

8. Locating Templates Directly Example

// renders _fields/\_themes/bs-horizontal/custom/_wrapper.gsp:
<f:field property="startDate" wrapper="custom" theme="bs-horizontal"/>

// renders _fields/bootstrap3/_wrapper.gsp:
<f:field property="startDate" wrapper="bootstrap3"/>

// renders _fields/time/_widget.gsp:
<f:field property="startDate" widget="time"/>

// renders _fields/time/_wrapper.gsp and _fields/time/_widget.gsp:
<f:field property="startDate" templates="time"/>

// renders _fields/\_themes/bs-horizontal/custom/_displayWrapper.gsp:
<f:display property="startDate" wrapper="custom" theme="bs-horizontal"/>


// renders _fields/bootstrap3/_displayWrapper.gsp:
<f:display property="startDate" wrapper="bootstrap3"/>

// renders _fields/time/_displayWidget.gsp:
<f:display property="startDate" widget="time"/>

// renders _fields/time/_displayWrapper.gsp and _fields/time/_displayWidget.gsp:
<f:display property="startDate" templates="time"/>