Indexed Properties

Skip to end of metadata
Go to start of metadata

This how-to will cover how to use both numeric indexed properties and string-indexed or mapped properties. Indexed properties are hard to define, but easy to show by example. Imagine we wanted to edit information about a bug on a page. We might create a form with fields like "bug.name", "bug.description", "bug.priority" etc. Now imagine we want to edit multiple bugs at once, on the same page. We could write a form (and an ActionBean) with an awful lot of properties, like "bug1.name", "bug2.name", etc. but that's way too much work. So instead we use a notation that is understood by Stripes (and lots of other tools), and call our form fields bug[0].name and bug[1].name and so on.

To accomplish this there are two aspects to consider: How to generate the names of the fields on the form and how to code an ActionBean that can receive them. Both are actually quite simple.

Numeric Indexed Properties on the JSP

Essentially constructing the names on the JSP is up to you. This is good and bad news. It means slightly more work for you (the developer) - but it really is only a tiny bit more work. On the upside it means a lot more flexibility. Some of the advantages of this approach are:

  • Indexes can be embedded anywhere inside a property name
  • A single property can have multiple indexing (e.g. bugs[0].watchers[3].name)
  • The source of the index can be anything - allowing interopation with any looping tag that gives you access to it's index, including the c:for* tags, the display:table tag etc.

The following is an example fragment of a JSP using indexed properties (it is a simplified version of a page from the Bugzooky Sample Application):

"Using indexed properties in a JSP"
<stripes:form action="/bugzooky/EditPeople.action">
    <table class="display">
        <tr>
            <th>ID</th>
            <th>Username</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
        </tr>
        <c:forEach items="${personManager.allPeople}" var="person" varStatus="loop">
            <tr>
                <td>
                    ${person.id}
                    <stripes:hidden name="people[${loop.index}].id" value="${person.id}"/>
                </td>
                <td><stripes:text name="people[${loop.index}].username"  value="${person.username}"/></td>
                <td><stripes:text name="people[${loop.index}].firstName" value="${person.firstName}"/></td>
                <td><stripes:text name="people[${loop.index}].lastName"  value="${person.lastName}"/></td>
                <td><stripes:text name="people[${loop.index}].email"     value="${person.email}"/></td>
            </tr>
            <c:set var="newIndex" value="${loop.index + 1}" scope="page"/>
        </c:forEach>
        <%-- And now, an empty row, to allow the adding of new users. --%>
        <tr>
            <td></td>
            <td></td>
            <td><stripes:text name="people[${newIndex}].username"/></td>
            <td><stripes:text name="people[${newIndex}].firstName"/></td>
            <td><stripes:text name="people[${newIndex}].lastName"/></td>
            <td><stripes:text name="people[${newIndex}].email"/></td>
        </tr>
    </table>

    <div class="buttons"><stripes:submit name="Save" value="Save Changes"/></div>
</stripes:form>

It's pretty easy with EL. Using the c:forEach tag the varStatus (which contains the index of the current iteration) is assigned to the name loop. Then, in the form fields the loop.index is inserted into the form field name using EL. E.g. people[${loop.index}].username will translate at runtime into people[0].username, people[1].username etc.

Numeric Indexed Properties in the ActionBean

This is an area where Stripes really shines. The relevant piece of the ActionBean which corresponds to the above form is:

"Using indexed properties in an ActionBean"
    private List<Person> people;

    @ValidateNestedProperties ({
        @Validate(field="username", required=true, minlength=3, maxlength=15),
        @Validate(field="firstName", required=true, maxlength=25),
        @Validate(field="lastName", required=true,  maxlength=25),
        @Validate(field="email", mask="[\\w\\.]+@[\\w\\.]+\\.\\w+")
    })
    public List<Person> getPeople() { return people; }
    public void setPeople(List<Person> people) { this.people = people; }

As you can see, all that is involved is declaring a List (or implementation thereof) property, and providing a getter and setter for the List that have the appropriate generic types. Stripes will introspect your bean at run time and figure out that the "people" property is a list property and will fill in the list for you, instantiating person objects and binding properties to them. You don't even have to instantiate your list - Stripes will do that for you too.

Validation of Indexed Properties

You might have noticed that the getPeople() method above was annotated, not with validations that make sense for a list, but with validations that make sense for each item in the list - in this case a Person object. Stripes performs validations for each index or row in the list as if it were a regular property. There is however one big change. Required field validations are only applied if at least one value with the same index was supplied. This is easier to comprehend with an example.

In Bugzooky the Administer Bugzooky page shows the form we have been using as an example. Look back at the JSP example and you'll notice that an extra row is put in at the end, with no values in it. If the user does not enter anything in this row, the browser will submit it, but because all the fields are empty, Stripes will ignore it and raise no errors. However, if the user entered any field, say username, and left the others blank then a host of validation error messages would show up.

String Indexed Properties

Just like you can use numeric indices to construct Lists in ActionBeans, you can also use String indices to construct Maps. The syntax is similar, but with one difference. The value between the square brackets must be quoted - usually using single quotes since it's easier to do without escaping.

Good examples of String Indexed property usage are hard to come by. A reasonable example from my recent work is as follows. I needed to provide a web interface that added some intelligence but eventually delegated work to a command line tool. The command line tool took on the order to 60 different input option, all in the same format (in this case foo=bar). The page I was writing didn't need to be smart about these parameters, so I used String Indexed properties to get the job done as cleanly as possible. Code examples are below:

String indexed properties in the JSP
<stripes:form ... >
    ...
    <table>
        <c:forEach items="${toolParams} var="toolParam>
            <tr>
                <td>${toolParam.name}:</td>
                <td><stripes:text name="toolParameters['${toolParam.name}']"/></td>
            </tr>
        </c:forEach>
    </table>
    ...
</stripes:form>

The relevant section of the ActionBean would look like:

String indexed properties in the ActionBean
private Map<String,Double> toolParameters;

public Map<String,Double> getToolParameters() { return toolParameters; }
public void setToolParameters(Map<String,Double> toolParameters) { 
    this.toolParameters = toolParameters;
}

Other types of indexed properties

While it does not make specific efforts to support them, Stripes does permit the use of indexed properties in the JavaBean specification sense of the word. In this case the backing object is really your choice, and can be a List, an Array or even a Set (though it takes iteration through the set each time to find the nth element).

In actuality, Stripes uses Ognl under the covers, with some extensions, to do a lot of the property binding. Ognl supports the use of various syntaxes and styles of interaction with indexed properties. If you cannot acheive what you are looking to do with the List and Map backed properties as described above, you may like to take a look at the Ognl Language Guide.

With great power comes great responsibility
While you are free to use almost any syntax in your properties that works with Ognl you should know that Stripes has specific support to make certain operations painless. If you use syntax above and beyond that documented here for indexed properties you will likely not be able to use Stripes' built in validation (you can of course still implement Validatable). You may also experience issues with other areas of Stripes that are indexed-property aware such as error messaging.
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.