gnl.org">OGNL library. To use nested objects in your form, simply specify the object path as the Field name. Note in the object path you exclude the root object, so the path
customer.address.state is specified as
address.state.
For example:
// The customer.address.state field TextField stateField = new TextField("address.state"); form.add(stateField); .. // Loads the customer address state into the form stateField Customer customer = getCustomer(); form.copyFrom(customer); .. // Copies form stateField value into the customer address state Customer customer = new Customer(); form.copyTo(customer);
When populating an object from a form post Click will automatically create any null nested objects so their properties can be set. To do this Click uses the no-args constructor of the nested objects class.
{@link #copyTo(Object)} and {@link #copyFrom(Object)} also supports
java.util.Map as an argument. Examples of using
java.util.Map are shown in the respective method descriptions.
Form Validation
The Form control supports automatic field validation. By default when a POST request is made the form will validate the field values. To disable automatic validation set {@link #setValidate(boolean)} to false.
Form also provides a {@link #validate()} method where subclasses can providecustom cross-field validation.
File Upload Validation The Form's {@link #validateFileUpload()} provides validation for multipartrequests (multipart requests are used for uploading files from the browser). The {@link #validateFileUpload()} method checks that files being uploaded do not exceed the{@link org.apache.click.service.CommonsFileUploadService#sizeMax maximum request size}or the {@link org.apache.click.service.CommonsFileUploadService#fileSizeMax maximum file size}.
Note: if the
maximum request size or
maximum file size is exceeded, the request is deemed invalid ( {@link #hasPostError hasPostError}will return true), and no further processing is performed on the form or fields. Instead the form will display the appropriate error message for the invalid request. See {@link #validateFileUpload()} for details of the error message properties.
JavaScript Validation The Form control also supports client side JavaScript validation. By default JavaScript validation is not enabled. To enable JavaScript validation set {@link #setJavaScriptValidation(boolean)} to true. For example:
Form form = new Form("form"); form.setJavaScriptValidation(true); // Add form fields .. form.add(new Submit("ok", " OK ", this, "onOkClicked"); Submit cancel = new Submit("cancel", "Cancel", this, "onCancelClicked"); cancel.setCancelJavaScriptValidation(true); addControl(form);
Please note in that is this example the cancel submit button has {@link Submit#setCancelJavaScriptValidation(boolean)} set to true. Thisprevents JavaScript form validation being performed when the cancel button is clicked.
CSS and JavaScript resources
The Form control makes use of the following resources (which Click automatically deploys to the application directory,
/click):
- click/control.css
- click/control.js
To import these files and any form control imports simply reference the variables
$headElements and
$jsElements in the page template. For example:
<html> <head> $headElements </head> <body> $form $jsElements </body> </html>
Form Layout
The Form control supports rendering using automatic and manual layout techniques.
Auto Layout
If you include a form variable in your template the form will be automatically laid out and rendered. Auto layout, form and field rendering options include:
{@link #buttonAlign} | button alignment: ["left", "center", "right"] |
{@link #buttonStyle} | button <td> "style" attribute value |
{@link #columns} | number of form table columns, the default value number is 1 |
{@link #errorsAlign} | validation error messages alignment: ["left", "center", "right"] |
{@link #errorsPosition} | validation error messages position: ["top", "middle", "bottom"] |
{@link #errorsStyle} | errors <td> "style" attribute value |
{@link #fieldStyle} | field <td> "style" attribute value |
{@link #labelAlign} | field label alignment: ["left", "center", "right"] |
{@link #labelsPosition} | label position relative to field: ["left", "top"] |
{@link #labelStyle} | label <td> "style" attribute value |
click/control.css | control CSS styles, automatically deployed to the click web directory |
/click-control.properties | form and field messages and HTML, located under classpath |
Manual Layout
You can also manually layout the Form in the page template specifying the fields using the named field notation:
$form. {@link #getFields fields}.usernameField
Whenever including your own Form markup in a page template or Velocity macro always specify:
- method - the form submission method ["post" | "get"]
- name - the name of your form, important when using JavaScript
- action - directs the Page where the form should be submitted to
- form_name - include a hidden field which specifies the {@link #name} of the Form
The hidden field is used by Click to determine which form was posted on a page which may contain multiple forms.
Alternatively you can use the Form {@link #startTag()} and {@link #endTag()}methods to render this information.
An example of a manually layed out Login form is provided below:
$form.startTag() <table style="margin: 1em;"> #if ($form.error) <tr> <td colspan="2" style="color: red;"> $form.error </td> </tr> #end #if ($form.fields.usernameField.error) <tr> <td colspan="2" style="color: red;"> $form.fields.usernameField.error </td> </tr> #end #if ($form.fields.passwordField.error) <tr> <td colspan="2" style="color: red;"> $form.fields.passwordField.error </td> </tr> #end <tr> <td> Username: </td> <td> $form.fields.usernameField </td> </tr> <tr> <td> Password: </td> <td> $form.fields.passwordField </td> </tr> <tr> <td> $form.fields.okSubmit $form.fields.cancelSubmit </td> </tr> </table> $form.endTag()
As you can see in this example most of the code and markup is generic and could be reused. This is where Velocity Macros come in.
Velocity Macros
Velocity Macros (
velocimacros) are a great way to encapsulate customized forms.
To create a generic form layout you can use the Form {@link #getFieldList()} and{@link #getButtonList()} properties within a Velocity macro. If you want toaccess
all Form Controls from within a Velocity template or macro use {@link #getControls()}.
The example below provides a generic
writeForm() macro which you could use through out an application. This Velocity macro code would be contained in a macro file, e.g.
macro.vm.
#* Custom Form Macro Code *# #macro( writeForm[$form] ) $form.startTag() <table width="100%"> #if ($form.error) <tr> <td colspan="2" style="color: red;"> $form.error </td> </tr> #end #foreach ($field in $form.fieldList) #if (!$field.hidden) #if (!$field.valid) <tr> <td colspan="2"> $field.error </td> </tr> #end <tr> <td> $field.label: </td><td> $field </td> </tr> #end #end <tr> <td colspan="2"> #foreach ($button in $form.buttonList) $button #end </td> </tr> </table> $form.endTag() #end
You would then call this macro in your Page template passing it your
form object:
#writeForm($form)
At render time Velocity will execute the macro using the given form and render the results to the response output stream.
Configuring Macros
To configure your application to use your macros you can:
- Put your macros if a file called macro.vm in your applications root directory.
- Put your macros in the auto deployed click/VM_global_macro.vm file.
- Create a custom named macro file and reference it in a WEB-INF/velocity.properties file under the property named velocimacro.library.
Preventing Accidental Form Posts
Users may accidentally make multiple form submissions by refreshing a page or by pressing the back button.
To prevent multiple form posts from page refreshes use the Post Redirect pattern. With this pattern once the user has posted a form you redirect to another page. If the user then presses the refresh button, they will making a GET request on the current page. Please see the
Redirect After Post article for more information on this topic.
To prevent multiple form posts from use of the browser back button use one of the Form {@link #onSubmitCheck(org.apache.click.Page,String)} methods. For example:
public class Purchase extends Page { .. public boolean onSecurityCheck() { return form.onSubmitCheck(this, "/invalid-submit.html"); } }
The form submit check methods store a special token in the users session and in a hidden field in the form to ensure a form post isn't replayed.
See also the W3C HTML reference:
FORM
@see Field
@see Submit