Bean Fields - smart tags for command and domain objects

13 votes
Dependency:
compile ":bean-fields:1.0"

 Documentation  Source  Issues

Summary

Smart rendering of fields of domain and command objects. For Grails 2 we recommend changing to Grails Fields plugin.

Description

This plugin provides a suite of tags for rendering form fields for domain and command objects.

This plugin is deprecated for users of Grails 2 and higher. Grails Fields plugin is a cleaner and more up to date solution and it is recommended that you use this. However Grails Fields does not work with Grails 1.x apps, so Bean Fields may still be useful to you.

It takes the pain, boredom and scope for error away.

Out of the box it:

  • renders a field using a UI element appropriate for the property type. Constraints are used to set maximum lengths etc.
  • automatically renders <label> tags for fields, from i18n message bundles
  • renders "required" indicators if the field is nullable:false and/or blank:false
  • renders errors adjacent to the field that had the error
  • renders the current value as appropriate
This is all done by building on top of Grails existing tags, so all the regular tag attributes are supported where possible.

Here's one of the most optimal examples of how this can make rendering all this for a bunch of bean properties:

<bean:withBean beanName="form">
    <bean:field property="firstName"/>
    <bean:field property="lastName"/>
    <bean:field property="company"/>
    <bean:field property="email"/>
</bean:withBean>

It's quite concise and expressive isn't it!

Deprecated This plugin works but you may enjoy the new Grails Fields plugin more, although that is currently Grails 2.0 only.

Beyond this however you can customize almost every aspect of it:

  • The markup and layout of labels, errors and required indicators
  • Per-field type markup eg custom templates for select fields
  • No CSS is supplied, all standard CSS customisations are possible
You can therefore create a GSP template that sets all the field templates up the way you want them, and just g:render this template in your form GSPs - giving you a single point for controlling the markup used to render fields, labels and errors.

Known issues The CSS errorClass setting is not used when rendering fields with errors except for the default <label> template and simple text <input> fields

Commercial Support

Commercial support is available for this and other Grailsrocks plugins.

Getting started

Install the plugin:

grails install-plugin bean-fields

Then in a GSP where you have a form and a domain or command object do this:

<bean:field beanName="myFormObject" property="firstName"/>

That's it. This will expect an object in the model/pageScope called 'myFormObject' and will create an appropriate field and label for the 'firstName' field.

This tag will look at the type of the property and constraints for it and make a best-guess at the type of field to use.

It will indicate if the field is required (according to the constraints) and also show any errors on that field.

You can of course force the usage of a specific field type by using another tag, such as:

<bean:input beanName="myFormObject" property="firstName"/>
<bean:select beanName="myFormObject" property="firstName" from="['Peter', 'Paul', 'Mary']"/>
<bean:textArea beanName="myFormObject" property="firstName"/>

Any attributes you supply that are not used by bean-fields will be passed through to the underlying grails tag, eg "id" is commonly used for CSS styling.

All of the rendering is passed through templates that you can use to customize how and where labels, fields, errors and required indicators are rendered.

i18n

Labels and error messages for fields are resolved against the i18n message bundles of your application.

Labels are generally resolved as "${beanName}.${property}". The radio group tag uses "${beanName}.${property}.${thevalueofcurrentitem}".

However custom labelKey values can be provided for any of the tags, and a literal label using the "label" attribute which will bypass i18n.

If label messages cannot be resolved a "humanized" version of the camelcase property name will be used.

The core tags

Unless you are using the bean:withBean tag, all field rendering tags require the following attributes:

  • beanName - the name of the model variable that is the bean you wish to render a field for
  • property - the property path for the field on the bean, with dot notation support for nested properties
Example:

<bean:field beanName="book" property="author.name"/>

This would render a field for editing the value of ${book.author.name}

If you are rendering many fields on the same bean, you can use withBean which will set beanName for all bean:xxxx tags nested within it.

All of the field tags also support the following attributes:

  • label - a manual override for the label text, which will bypass i18n
  • labelKey - a manual override for the message code used to resolve the label for the field, in the i18n bundles
  • labelClass - a CSS class to apply to the label
  • showErrors - a manual override for whether or not the field's errors should be rendered. If not specified, falls back to the value set with bean:showErrors tag, or true if that tag is never used
  • requiredField - a manual override for the required field indicator that allows you to replace the currently set indicator (usually set with bean:requiredIndicator) for the current field only. Eg. you can use this to clear the indicator so that the current field is shown as non-required even if the constraints say it is.
  • value - a manual override for the current value of the field. This will be used instead of the current property value
  • default - a default value to use if the current value of the property is null
  • useValue - a boolean expression that if false will mean the current property value is NOT used even if it is set. Use to prevent display of current values based on a condition
  • noLabel - suppress rendering of a label for the field

bean:withBean

Use to set the beanName attribute for all nested bean:xxxx tags:

<bean:withBean beanName="form">
    <bean:field property="firstName"/>
    <bean:field property="lastName"/>
    <bean:field property="company"/>
    <bean:field property="email"/>
</bean:withBean>

This is just a shortcut to save repeating the beanName

bean:form

Renders a whole load of fields automatically. You just supply the name of the bean and the list of property names in the "properties" attribute. It will auto-sense the rest.

<bean:form beanName="book" properties="title, author.name, isbn"/>

The fields are rendered in the order you list them. Each field is rendered using a call to the bean:field tag with the appropriate values.

bean:field

Renders a field using some trivial logic based on the type and constraints of the property.

<bean:field beanName="book" property="author.name"/>

If the rules result in a field type that you don't want, you can use a more specific tag. Any other attributes are passed through to the underlying tags.

bean:input

Renders a text input field, applying the size constraints of the property to HTML attributes.

<bean:input beanName="book" property="author.name"/>

Attributes you can use to override the smart behaviour include:

  • size
  • maxLength
  • class
  • type (defaults to 'text')
If you don't supply values for these, it will "do the right thing" as per the property's constraints.

bean:select

Renders a select list field, using the inList or range constraint to supply the values (keys) if set.

Alternatively if the property type is an association, it will default to selecting an instance of the association type.

<bean:select beanName="book" property="genre"/>

Attributes you can use to override the smart behaviour include:

  • from - change the list of items
  • noSelection - specify the noSelection value to use if the field is not mandatory
All other g:select attributes like optionValue, optionKey etc can also be supplied if necessary.

bean:country

A simple wrapper around the g:countrySelect tag, following the pattern of all the other tags.

<bean:country beanName="book" property="author.country"/>

bean:date

A simple wrapper around the g:date tag, following the pattern of all the other tags.

<bean:date beanName="book" property="publishedOn"/>

bean:textArea

A simple wrapper around the g:textArea tag, following the pattern of all the other tags.

<bean:textArea beanName="book" property="notes"/>

bean:checkBox

A simple wrapper around the g:checkBox tag, following the pattern of all the other tags.

<bean:checkBox beanName="book" property="matureContent"/>

bean:radioGroup

A simple wrapper around multiple g:radio tags, following the pattern of all the other tags.

One radio field will be rendered for each value in the inList constraint of the field.

<bean:radioGroup beanName="book" property="author.gender"/>

Customizing the rendering

Most of these use an identical pattern - you specify a tag body that will be used to render the field/field element in question in future.

The body is passed variables including:

  • field - the already rendered markup for the field itself, eg <input id="x" name="x" value="y"/>
  • label - the already rendered label for the field, eg <label for="x">Name</label>
  • required - the required indicator, if any. Will be blank if the field is not required.
  • bean - the bean instance the field relates to
  • beanName - the original bean name passed into the field tag
  • propertyName - the name of the property the field relates to
  • labelKey - the generated or supplied labelKey (message code) for the field
  • errors - the already rendered list of errors, eg "Field must not be null<br/>Minimum length is 50<br/>"
The default template for rendering fields is equivalent to:

${label}
<g:if test="${errors}">
<br/><div>${errors}</div>
</g:if>
${field}<br/>

The label itself is built with the default label template of:

<label for="${fieldId}" class="${errorClassToUse}">${label}${required}</label>"

The errors are also rendered via template, which defaults to code which is equivalent to:

<g:message code="${message}" encodeAs="HTML"/><br/>

Changing the templates used for the various types is done with the following tags. You can call these as many times as you like in a page, and the template will take effect for the rest of the current request unless changed again.

A code example. Say we want to show a colon between label and field and we want to render errors under the field, and wrap them all in a div:

<bean:inputTemplate>
${label}: ${field}
<g:if test="${errors}"><br/>${errors}</g:if>
</bean:inputTemplate>

Field template customization tags

bean:labelTemplate

The template used for the label part of all fields. This is rendered and passed to the field's template for final layout.

Example:

<bean:labelTemplate>
<label for="${fieldId}" class="${errorClassToUse}">${label}${required}</label>
</bean:labelTemplate>

This is passed the following variables:

  • errorClassToUse - The CSS class to use to indicate errors. Blank if no errors
  • required - The required field indicator. Blank if field is not required
  • fieldName - The original property name, typically used as field name
  • label - The label, as rendered from label template
  • fieldValue - The current value of the field
  • fieldId - The id attribute of the field
  • defaultValue - The default value of the field
  • varArgs - The extra attributes supplied for the field
  • bean - The bean instance
  • beanConstraints - The constraints of the bean
  • beanName - The name of the bean
  • propertyName - The final resolved property, last component of fieldName if it was a nested property path
  • errors - Any errors for the field

bean:inputTemplate

The template used for rendering <input> text fields. $field contains the completed <input> tag

bean:textAreaTemplate

The template used for rendering <textarea> fields. $field contains the completed <textarea> tag

bean:checkBoxTemplate

The template used for rendering <input type="checkbox"> fields. $field contains the completed <input> tag

bean:radioGroupTemplate

The template used for rendering a single <input type="radio"> field in a radiogroup. $field contains the completed <input> tag

bean:dateTemplate

The template used for rendering Date fields using g:datePicker. $field contains the completed date fields, which will be several fields

bean:selectTemplate

The template used for rendering <select> fields. $field contains the completed <select> tag

bean:customTemplate

The template used when rendering a custom field, where you supply the markup for the $field variable but label and errors are supplied for you

Other configuration tags

bean:maxInputLength

Sets the maximum display length in characters for text fields. Even if the maxSize constraint for a string exceeds this, the display length will be clamped at this value.

<bean:maxInputLength>50</bean:maxInputLength>

bean:labelClass

The CSS class name to use when rendering a label (whether there is an error or not)

<bean:labelClass>form-field-label</bean:labelClass>

This value will be used for all fields tags that are invoked without an explicit labelClass attribute. The default is blank.

bean:errorClass

The CSS class name to use when rendering a label for a field that has an error

<bean:errorClass>fieldError</bean:errorClass>

bean:errorTemplate

A template to use for rendering a single error on a field, called repeatedly with variables "error" (the error string) and "message" (the error string resolved against i18n bundle)

<bean:errorTemplate>
<div class="fieldError">Ooops! ${message.encodeAsHTML()}</div>
</bean:errorTemplate>

bean:requiredIndicator

The body markup to use to indicate a field is required. This is rendered into the other templates, so those templates control the positioning of it.

<bean:requiredIndicator>
<img src="/images/required-field.png" alt="(!)">
</bean:requiredIndicator>

bean:showErrors

The value attribute is used to set whether or not errors are shown inside fields by default. You can control this per-field also.

<bean:showErrors>true</bean:showErrors>

bean:maxAutoRadioButtons

Sets the threshold at which bean:field tag will switch from using radio buttons to a select box for a property with an inList constraint

<bean:maxAutoRadioButtons>3</bean:maxAutoRadioButtons>

Tags for niche rendering requirements

bean:label

Render just the label for the given beanName and property. Like bean:field but does not render the field, just the label using the relevant templates

bean:customField

Render a bean field but with your custom markup as the $field variable in the templates. Use to supply arbitrary advanced markup for a field but still retain the convenience of common label and error template handling

bean:required

Render just the required indicator, if and only if the field specified by beanName and property attributes is a required field