Note: the approach described below has become part of the standard Spring distribution since version 1.1. See the documentation for <spring:nestedPath>
. There's no longer any need to roll your own springx:setNestedPath.
Minimizing Typing with <spring:nestedPath> Tag
The <spring:bind>
tag is the standard way to work with beans in a JSP/JSTL manner. It provides some extra value add above and beyond regular JSTL constructs such as <c:out>. Due to its flexibility, though, it can be a bit verbose to use. Below is a technique to minimize the amount of typing and configuration while creating JSPs with <spring:bind>.
Typically, to use <spring:bind>, you must specify the object name and the property of the object. For example:
<spring:bind path="commandName.firstName">
<input type="text" name="${status.expression}" value="${status.value}" />
</spring:bind>
The above code instructs the bind tag to find the bean named commandName and then its firstName property. There are problems with this approach: the command object name is repeated many times in a typical JSP and it must match with the Handler configuration.
The commandName is a key in
the model for the backing form object. The handler configuration specifies
this key, and is responsible for placing the form object into the model so
it is available to the view. While Spring will default the name of the command object if none is given, you might have reason to uniquely name your command objects.
This means that you must remember, for each JSP page, the name of the command object. Therefore, to minimize chances for error, the command object name should be specified a minimum number of times. The current usage of <spring:bind> requires the command name be specified each time. There is no nesting or scope.
Solution
Thanks to JSP 2.0, we can built a few tagfiles to make working with <spring:bind> faster and less error prone. Thanks to Oliver Hutchison
for the suggestion.
By creating a <spring:setNestedPath> tag, we can specify the current path once, and all <spring:bind> tags within <spring:setNestedPath> would prepend the nested path to its path.
For example:
<spring:setNestedPath path="commandName">
<spring:bind path="firstName">
<input type="text" name="${status.expression}" value="${status.value}"/>
</spring:bind>
</spring:setNestedPath>
While the savings don't look like much now, imagine having a form with 20 fields. You can then wrap all <spring:bind> inside the single <spring:setNestedPath>.
Also, you can nest paths within paths. For example:
<spring:setNestedPath path="commandName">
<spring:bind path="firstName">
<input type="text" name="${status.expression}" value="${status.value}"/>
</spring:bind>
<spring:bind path="lastName">
<input type="text" name="${status.expression}" value="${status.value}"/>
</spring:bind>
<spring:setNestedPath path="address">
<spring:bind path="street1">
...
</spring:bind>
</spring:setNestedPath>
</spring:setNestedPath>
The above code would create bindings in this order:
commandName.firstName
commandName.lastName
commandName.address.street1
To begin using this improvement, two tagfiles have been created that provide setNestedPath and wrap <spring:bind>. Hopefully, others will find this useful, and true Tag classes will be created an included in the spring distribution.
Place these files inside /WEB-INF/tags/springx
setNestedPath.tag
<%@ tag body-content="scriptless" %>
<%@ attribute name="path" required="true" %>
<%@ taglib prefix="c" uri="http: %>
<c:choose>
<c:when test="${empty nestedPath}">
<c:set value="${path}" var="nestedPath" scope="request" />
</c:when>
<c:otherwise>
<c:set value="${nestedPath}" var="savedNestedPath" scope="page" />
<c:set value="${nestedPath}.${path}" var="nestedPath" scope="request" />
</c:otherwise>
</c:choose>
<jsp:doBody/>
<c:set value="${savedNestedPath}" var="nestedPath" scope="request"/>
bind.tag
<%@ tag body-content="scriptless" %>
<%@ attribute name="path" required="true" %>
<%@ taglib prefix="spring" uri="http: %>
<spring:bind path="${nestedPath}.${path}">
<jsp:doBody/>
</spring:bind>
Usage example:
<%@ page language="java" %>
<%@ taglib prefix="springx" tagdir="/WEB-INF/tags/springx" %>
<springx:setNestedPath path="beanName">
<springx:bind path="fieldName" /><br />
<springx:setNestedPath path="subBeanName">
<springx:bind path="subBeanFieldName" /><br />
<springx:setNestedPath path="subSubBeanName">
<springx:bind path="subSubFieldName"/><br/>
<springx:bind path="subSubFieldName2"/><br/>
</springx:setNestedPath>
</springx:setNestedPath>
<springx:bind path="anotherFieldName" /><br />
</springx:setNestedPath>