|
Recipe 8.5. Validating an Indexed PropertyProblemYou want to validate a field that is a nested property of an object in an array or Collection. SolutionSet the indexedListProperty attribute for the field element to the name of the property that returns the array or Collection. The value of the property attribute will be interpreted as the name of a nested property of an element within the Collection. If you're using Struts 1.2, you can reference the indexed property in a test for a validwhen rule. Example 8-5 shows a complete form element from validation.xml for a form containing the array property "orders." Example 8-5. Validations for an indexed list property (partial)<form name="IndexedListForm"> <field property="partNumber" indexedListProperty="orders" depends="minlength"> <arg position="0" key="prompt.partNumber"/> <arg position="1" key="${var:minlength}" resource="false"/> <var> <var-name>minlength</var-name> <var-value>5</var-value> </var> </field> <field property="quantity" indexedListProperty="orders" depends="intRange,validwhen"> <arg position="0" key="prompt.quantity"/> <arg position="1" key="${var:min}" resource="false"/> <arg position="2" key="${var:max}" resource="false"/> <msg name="validwhen" key="error.quantity.invalid"/> <var> <var-name>min</var-name> <var-value>5</var-value> </var> <var> <var-name>max</var-name> <var-value>20</var-value> </var> <var> <var-name>test</var-name> <var-value> (((orders[].partNumber != null) and (*this* != null)) or ((orders[].partNumber == null) and (*this* == null))) </var-value> </var> </field> </form> DiscussionThe indexedListProperty permits you to define one set of validation rules applied to repeated fields on a form. Say you have a JSP page like the one shown in Example 8-6 (indexed_list_test2.jsp) that allows a user to place orders for specific part numbers and quantities. Example 8-6. JSP for rendering indexed properties<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %> <html> <head> <title>Struts Cookbook Chapter 8 : Indexed List Validation</title> </head> <body bgcolor="white"> <h2>Struts Cookbook Chapter 8 : Indexed List Validation</h2> <html:form action="/ProcessIndexedListTest2"> <table> <tr> <th><bean:message key="prompt.partNumber"/></th> <th><bean:message key="prompt.quantity"/></th> </tr> <logic:iterate name="IndexedListForm" property="orders" index> <tr> <td> <html:text name="orders" property="partNumber" indexed="true"/><br /> <html:messages property='<%="orders["+ndx+"].partNumber"%>'> <font color="red"><bean:write name="error"/></font> </html:messages> </td> <td> <html:text name="orders" property="quantity" indexed="true"/><br /> <html:messages property='<%="orders["+ndx+"].quantity"%>'> <font color="red"><bean:write name="error"/></font> </html:messages> </td> </tr> </logic:iterate> </table> <html:submit/> </html:form> </body> </html> The rendered HTML form for the JSP in Example 8-6 is shown in Example 8-7. Example 8-7. Generated HTML for indexed properties<html> <head> <title>Struts Cookbook Chapter 8 : Indexed List Validation</title> </head> <body bgcolor="white"> <h2>Struts Cookbook Chapter 8 : Indexed List Validation</h2> <form name="IndexedListForm" method="post" action="/jsc-ch08/ProcessIndexedListTest2.do"> <table> <tr> <th>Part number</th> <th>Quantity</th> </tr> <tr> <td> <input type="text" name="orders[0].partNumber" value=""> <br /> </td> <td> <input type="text" name="orders[0].quantity" value=""> <br /> </td> </tr> <tr> <td> <input type="text" name="orders[1].partNumber" value=""> <br /> </td> <td> <input type="text" name="orders[1].quantity" value=""> <br /> </td> </tr> <tr> <td> <input type="text" name="orders[2].partNumber" value=""> <br /> </td> <td> <input type="text" name="orders[2].quantity" value=""> <br /> </td> </tr> <tr> <td> <input type="text" name="orders[3].partNumber" value=""> <br /> </td> <td> <input type="text" name="orders[3].quantity" value=""> <br /> </td> </tr> <tr> <td> <input type="text" name="orders[4].partNumber" value=""> <br /> </td> <td> <input type="text" name="orders[4].quantity" value=""> <br /> </td> </tr> </table> <input type="submit" value="Submit"> </form> </body> </html> By setting property="partNumber" and indexedListProperty="orders", you define a validation rule that will be evaluated for each indexed property submitted. In the Solution, each part number is checked using the minlength rule. <field property="partNumber" indexedListProperty="orders" depends="minlength"> <arg position="0" key="prompt.partNumber"/> <arg position="1" key="${var:minlength}" resource="false"/> <var> <var-name>minlength</var-name> <var-value>7</var-value> </var> </field> The rule only applies if the property being validated has a non-blank value. Users don't have to enter five part numbers if they don't want to. If you wanted to force the users to enter a value for every part number field, you would specify the required rule as well. In the Solution, the quantity is validated. The intRange rule validates that an entered quantity is between 5 and 20. This field uses the validwhen rule with a rather verbose test. <var> <var-name>test</var-name> <var-value> (((orders[].partNumber != null) and (*this* != null)) or ((orders[].partNumber == null) and (*this* == null))) </var-value> </var> The test ensures that if a quantity is entered, a part number must also be entered. Likewise, if a part number isn't entered, the quantity should be blank. The operand, orders[].partNumber, allows you to refer to a nested property of an indexed object. The [] notation tells the Validator that the expression applies to each object in the array. You can refer to a specific index in the property attribute value or a validwhen expression. If you wanted to require that a part number and quantity were entered for the first row, use the following: <field property="orders[0].partNumber" depends="required"> <arg position="0" key="prompt.partNumber"/> </field> A common problem when validating multiple rows is that one error message is generated for each rule type for a field. In the Solution, for example, the quantity range validation is generated for the first failing property. You can see the practical results of this behavior in Figure 8-1. Though both quantity fields are invalid, the error is generated for the first quantity field. Figure 8-1. Indexed list JSP with errorsUnfortunately, this is a quirk of the Validator when using indexed properties. When the Validator encounters the first error for a particular field, it won't continue to check additional indices of the same field. If this presents a problem, you may want to go back to displaying the errors at the top of the page or take the tried and true method and hand code the validate( ) method to do what you want. See AlsoFor additional details on using indexed properties in forms see Recipe 3.4. The validwhen rule is discussed in Recipe 8.4. For custom rendering of error messages, look at Recipe 9.6. You'll find a number of interesting discussions on the struts-user mailing list; search on "indexedListProperty" and "array validation." |
|