RIFE logo
This page last changed on Jan 23, 2007 by joshuah@up-bear.net.

(Form field constraints, validation, and displaying error messages for bad form input is described in Validation.)


HTML forms can be automatically generated from beans. This is fully integrated with the constraints mechanism and the validation facility.

Usage

To generate a form, you simply have to call the generateForm(Template template, Object beanInstance) method. If the bean is Constrained and contains constrained properties, RIFE will interpret them and convert them to corresponding HTML attributes. If the bean is Validated and contains validation errors, they will be properly processed: the error areas will be populated with appropriate error messages and the markings will be indicated where needed. If you have declared submission beans in your element, forms will be automatically generated when you print the template and previously submitted values will be displayed in the form fields.

Empty forms can also be generated from bean classes through the generateEmptyForm(Template template, Class beanClass) method.

[top]

Form fields

Forms are constructed from fields and it's those fields that you have to specify in your template through specific value tags. According to each value ID prefix, RIFE knows which type of field to generate. The prefix is, as usual, followed by the property name. The following fields are supported:

<!--V 'FORM:INPUT:property'/-->

Generates a regular text field.

<!--V 'FORM:SECRET:property'/-->

Generates a text field where the entered text is not displayed.

<!--V 'FORM:TEXTAREA:property'/-->

Generates a text area for the input of multi-lined text.

<!--V 'FORM:RADIO:property'/-->

Generates radio buttons for the selection of one value from a set of possibilities.

<!--V 'FORM:CHECKBOX:property'/-->

Generates checkbox buttons for the selection of several values from a set of possibilities. It can also be used with just one value as a boolean switch.

<!--V 'FORM:SELECT:property'/-->

Generates a selection list.

<!--V 'FORM:HIDDEN:property'/-->

Generates a hidden field for implicit data submission.

[top]

Collection fields

The radio, checkbox and select fields allow the user to select from a collection of values. However, these values are rarely those that you want to display to the user in the interface. Most of the time a more descriptive label is shown for each option so that the interface becomes less cryptic. You can specify these labels in blocks in the template, the format of the label block ID is: 'FORM:LABEL:property:value'.

However, before being able to generate all these options, RIFE has to know what the possible values are for the field. This is currently only possible through the use of a ConstrainedProperty and its inList(String[]) method.

For example, consider the following constrained property:

bean.addConstraint(
    new ConstrainedProperty("colors").inList(new String[] {"black", "red", "blue"}));

and the following template excerpt:

<!--V 'FORM:SELECT:colors'/-->
<!--B 'FORM:LABEL:colors:blue'-->blue spots<!--/B-->
<!--B 'FORM:LABEL:colors:red'-->red spots<!--/B-->
<!--B 'FORM:LABEL:colors:green'-->green spots<!--/B-->

will generate:

<select name="colors">

<option value="red">red spots</option>
<option value="blue">blue spots</option>
<option value="green">green spots</option>
</select>

It's also possible that your values are dynamically added to a constrained property and that the labels can't be defined statically in your template through blocks. You therefore have the option to add resource bundles to a template instance. When looking for a label, RIFE will first check if 'property:value' keys are present in a registered ResourceBundle and use their values instead.

For example, consider the same constrained property as above (whose list could have been dynamically populated) and the following code that registers a ResourceBundle in the template (which could also have been dynamically populated):

template.addResourceBundle(new ListResourceBundle() {
public Object[][] getContents()
{
    return new Object[][] {
        {"colors:blue", "blue spots"},
        {"colors:red", "red spots"},
        {"colors:green", "green spots"}
    };
}});

with only this line in the template:

<!--V 'FORM:SELECT:colors'/-->

will generate exactly the same HTML code as above.

If you dynamically generate the field with the FormBuilder, you need to add the ListResourceBundle before you add the Field with the FormBuilder. So the FormBuilder can use the ListResourceBundle to generate the correct Option values.

[top]

Displaying values

If you're not generating an empty form, RIFE will look at the actual values of your bean's properties and include them in the template generation. Text fields will thus be populated, checkboxes ticked, radio buttons selected, and select options highlighted. This makes it very easy to create forms where the user has to correct invalid data or where existing data can be edited.

[top]

Custom field attributes

While all relevant constraints of the properties will be examined and the corresponding HTML attributes generated, you'll often want to add other attributes that have nothing to do with the limits that are imposed on the data type. This is very easily done, all you have to do is write these custom attributes inside the field value tag as a default value. They will be added as attributes that are generated automatically.

For example, consider the following ConstrainedProperty:

bean.addConstraint(
    new ConstrainedProperty("login").maxLength(8);

and the following field value tag:

<!--V 'FORM:INPUT:property'-->size="10"<!--/V-->

they will generate the following HTML:

<input type="text" name="login" size="10" maxlength="8" />

[top]

Dynamic submission parameters

There are situations where it's useful to have dynamic parameter names in a form, as opposed to a static list. We'll explain with an example where the user is asked to translate a string into a number of languages. The application then checks if all the translations have been provided.

The languages and correct translations are stored in static arrays in the Java implementation. In a real world case this information would probably be stored and read from a database. Having to update the element definition every time a new language is added would be very tiresome, and to solve this kind of situation RIFE uses dynamic submission parameters.

Element using dynamic submission parameters
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE element SYSTEM "/dtd/element.dtd">

<element implementation="Dynamic">
  <submission name="translate">
    <param regexp="lang_\w+"/>
  </submission>
</element>

Take a look at the {{*param*}} tag. Instead of using the attribute name like we've done so far in the examples, we're using the regexp attribute. This makes the engine match the parameter names against a regular expression, so in the example above any parameter with a name that starts with "lang_" will be a match, for example "lang_English".

Template used for demonstrating dynamic submission parameters
<html>
  <head>
    <title>Dynamic submission parameters</title>
  </head>

  <body>
    <h1>Translate phrase</h1>

    The phrase is '<!--V 'phrase'/-->'<br/><br/>

    <form action="<!--V 'SUBMISSION:FORM:translate'/-->" method="GET">
      <!--V 'SUBMISSION:PARAMS:translate'/-->
      <table>
        <tr>
          <th>Language:</th><th>Translation:</th>
        </tr>
        <!--V 'langs'/-->
        <!--B 'lang_block'-->
        <tr>
          <td><!--V 'lang'/--></td>
          <td>
            <input type="text" name="<!--V 'name'/-->"/>
          </td>
         </tr>
        <!--/B-->
      </table>
      <input type="submit" name="Submit"/>
    </form>
  </body>
</html>

The example above shows the template that is used by the Java implementation. The only difference from the templates shown earlier is that the name of the input is a value instead of a hard coded string, to make it possible to set the name from the Java implementation.

Java implementation of element using dynamic submission parameters
import com.uwyn.rife.engine.Element;
import com.uwyn.rife.template.Template;

public class Dynamic extends Element
{
  static String[] langs = {"Swedish", "French"};
  static String[] translations = {"God kväll!", "Bon soir!"};

  public void processElement ()
  {
    if (!hasSubmission("translate"))
    {
      Template template = getHtmlTemplate("dynamic");
      template.setValue("phrase", "Good evening!");

      for (int i = 0; i < langs.length; ++i)
      {
        template.setValue("lang", langs[i]);
        template.setValue("name", "lang_" + langs[i]);
        template.appendBlock("langs", "lang_block");
      }

      print(template);
    }
    else
    {
      print("Translated");
    }
  }
}

Two parameters are constructed and appended on the template, both with the prefix "lang_", and therefore matching the regular expression used in the element. It would be easy to add more languages, without having to add submission parameters to the element definition.

[top]

File uploads

Here is a quick example to upload a file using RIFE.

Submission declaration snippet
<submission name="upload">
  <file name="thefile"/>
</submission>

The above will make uploaded files available under the thefile name, for use with getUploadedFile() as shown in the Java snippet.

HTML template declaration snippet
<form name="upload" action="[!V 'SUBMISSION:FORM:upload'/]"
      method="post" enctype="multipart/form-data"> <!-- standard form declaration -->
  <!--V 'SUBMISSION:PARAMS:upload'/-->             <!-- RIFE metadata to keep track of submissions -->
  <input name="thefile" type="file" size="50"/>    <!-- file field, browsers add a 'browse' button -->
  <input type="submit" value="Upload!"/>           <!-- submit button -->
</form>

This HTML snippet for a form is fairly standard and will allow the following Java snippet to access a file, once uploaded.

Java code snippet
public void doUpload() {
  UploadedFile upload = getUploadedFile("thefile");
  System.err.println("Upload filename: " + upload.getName());
}

Additionally, if your element implementation has a setter with the same name as the uploaded file and has UploadedFile as its sole argument, the uploaded file will be automatically injected through it:

public void setThefile(UploadedFile upload) {
  System.err.println("Upload filename: " + upload.getName());
}

Dynamic file uploads are also supported via the same regexp feature as regular params:

Submission declaration snippet
<submission name="upload">
  <!-- NOTE: Your element MUST use the getUploadedFileNames(String) rather than the usual
    -- getUploadedFileNames() which only retrieves normal file submissions.
    -->
  <file regexp="file_(\d+)"/>
</submission>

The above will make uploaded files available under the file_1, file_2, etc. names, for use with getUploadedFile(String) as shown in the Java snippet.

HTML template declaration snippet
<form name="upload" action="[!V 'SUBMISSION:FORM:upload'/]"
      method="post" enctype="multipart/form-data"> <!-- standard form declaration -->
  <!--V 'SUBMISSION:PARAMS:upload'/-->             <!-- RIFE metadata to keep track of submissions -->
  <input name="file_1" type="file" size="50"/><br/>    <!-- file field, browsers add a 'browse' button -->
  <input name="file_2" type="file" size="50"/><br/>    <!-- file field, browsers add a 'browse' button -->
  <input type="submit" value="Upload!"/>           <!-- submit button -->
</form>
Java implementation of element using dynamic submission files
...
		Logger logger =  Logger.getLogger(this.getClass().getName());
		if( hasSubmission("upload") ) { 
			// We have to use getUploadedFileNames(String) rather than the
			// normal getUploadedFileNames(), which only retrieves normal file submissions.
			List<String> files = getUploadedFileNames("file_.*");
			if( null == files || files.size() == 0 ) {
				logger.info("No uploaded files detected.");
			} else {
				int i = 0;
				for( String file : files ) {
					UploadedFile uFile = getUploadedFile(file);
					logger.info("Uploaded file["+i+"]:\n"
							+ "name=["+file+"]\n"
							+ "file=["+uFile.getFile()+"]\n"
							+ "name=["+uFile.getName()+"]\n"
							+ "type=["+uFile.getType()+"]\n"
							+ "sizeExceeded=["+uFile.wasSizeExceeded()+"]\n"
					);
					++i;
				}
			}
		}
		...

In addition to the above snippets, you may want to limit uploaded file sizes. This can be done as part of your site configuration, in the RIFE-jumpstart download bundle, this will be in the rep/config-base.xml file, using the following parameters:

Parameters for limiting file upload sizes
<param name="FILEUPLOAD_SIZE_LIMIT">4096000</param>   <!-- value in bytes -->
<param name="FILEUPLOAD_SIZE_CHECK">true</param>      <!-- check upload sizes using above maximum? true/false -->
<param name="FILEUPLOAD_SIZE_EXCEPTION">false</param> <!-- throw exception if size exceeded? true/false -->

[top]

Document generated by Confluence on Oct 19, 2010 14:57