Forms and Form Components
Main article: Forms and Validation
Contents
Table of Contents |
---|
exclude | Contents|Forms and Form Components |
---|
printable | false |
---|
|
What is the t:formdata
hidden field for?
In Tapestry, rendering a form can be a complicated process; inside the body of the Form component are many of field components: TextField, Select, TextArea, and so forth. Each of these must pull data out of your data model and convert it to the string form used inside the client web browser. In addition, JavaScript to support client-side validation must be generated. This can be further complicated by the use of Loop and If components, or made really complicated by the use of Block (to render portions of other pages: this is what the BeanEditForm component does).
...
Code Block |
---|
controls | true |
---|
title | FormSupport.java (partial) |
---|
linenumbers | true |
---|
|
public interface FormSupport extends ClientElement
{
/**
* Stores an action for execution during a later request. If the action contains any mutable state, it should be in
* its final state before invoking this method and its internal state should not be changed subsequently.
*/
<T> void store(T component, ComponentAction<T> action);
/**
* As with {@link #store(Object, org.apache.tapestry5.ComponentAction)}}, but the action is also invoked
* immediately. This is useful for defining an action that should occur symmetrically in both the render request and
* the form submission's event request.
*
* @param component component against which to trigger the action
* @param action the action that will be triggered (and passed the component)
*/
<T> void storeAndExecute(T component, ComponentAction<T> action);
|
The ComponentAction
objects are the callbacks. t:formdata
is simply an object stream of these callbacks, compressed and encoded in Base64. When using Ajax, you may see multiple t:formdata
hidden fields (they are processed one after another).
Tapestry tries to be smart about generating the label string for a field. It has some smart default logic, first checking for the component-id-label
in the container's message catalog, then ultimately converting the component's id into a user-presentable label.
...
Code Block |
---|
controls | true |
---|
linenumbers | true |
---|
|
<t:label for="username">${usernameLabel}</t:label>
<t:textfield t:id="username"/>
|
...
Code Block |
---|
controls | true |
---|
linenumbers | true |
---|
|
public String getUsernameLabel()
{
return systemPreferences.useEmailAddressForUserName() ? "Email address" : "User name";
}
|
...
Code Block |
---|
controls | true |
---|
linenumbers | true |
---|
|
<t:label for="username"/>
<t:textfield t:id="username" label="prop:usernameLabel"/>
|
The "prop:" prefix identifies that "usernameLabel" is to be interpreted as a property expression (normally, the binding for the label
parameter is interpreted as a string literal). The Label component gets the text it displays from the TextField component, and the TextField component uses the same text when generating server-side and client-side validation messages.
Tapestry normally figures out the correct field in your form to initially receive focus; this is based on assigning a FieldFocusPriority to each field as it renders, which works out to the following logic:
...
Here's an example
Code Block |
---|
<t:textfield t:id="email" t:mixins="OverrideFieldFocus" .../>
|
Code Block |
---|
@InjectComponent
private Field email;
@Environmental
private JavaScriptSupport javaScriptSupport;
void afterRender() {
javaScriptSupport.autofocus(FieldFocusPriority.OVERRIDE, email.getClientId());
}
|
In addition, there's a FormFieldFocus mixin, however at the time of writing, it had a specific bug and should not be usedThe OverrideFieldFocus mixin forces the email field to be the focus field, regardless.