某些情况下,一个表单中提交的内容数量是不一定的。比如Facebook的编辑照片界面,可以把整个相册的照片(最多60张)放在一起进行批量编辑,或者把一次上传的若干张照片一起编辑。这种情况下,如何用ActionForm来获取这些数据呢?
这个ActionForm(假设名为PhotoForm)应该有一个字段:
List<Photo> photos;
生成这个表单的时候很简单,服务器端当然清楚有多少张照片要被编辑,于是对这些照片遍历一遍,生成表单。实际上,struts form的设计考虑了链表数据的提交。以照片编辑页面为例,生成表单时,可以这样:
<logic:iterate id=”photo” name=”photos”>
<html:hidden name=”photo” property=”id” indexed=”true” />
<html:text name=”photo” property=”name” indexed=”true” />
</logic:iterate>
indexed属性便是专为链表设计的。这样的jsp代码生成的HTML类似:
<input type=”hidden” value=”5″ name=”photo[4].id”/>
<input type=”text” value=”编辑前的名字” name=”photo[4].name”/>
然而在用户将修改过的信息提交的时候,由于HTTP协议的stateless特性,服务器端已经“忘记”有多少张照片了(除非你在session中记录了这个数字,不过处于性能考虑大多数人在避免使用session,另外,假如用户在编辑信息的途中出去玩了几小时,回来继续提交的话,session已经不存在了)。
photos这个List即使放在对应的ActionForm中也无济于事,因为用户看到表单时,这个ActionForm对象已经消失了(当然,不要在那个mapping中设置scope=”session”)。
究其源,溯其本,很容易解决这种问题。请求提交给struts,struts首先根据mapping创建一个对应的ActionForm对象,当它看到表单中的属性名为photo[4].id,就会调用PhotoForm的getPhoto(4).setId(xxx)来填充表单。
注意,struts不是生成一个Photo对象,把id、name填充进去,再加到photos这个List中,它不知道要生成Photo对象。它只知道利用发射机制调用getPhoto(4).setId(xxx)。
所以我们应该在PhotoForm的getPhoto这个方法里做文章,根据参数将List扩展到适当的大小避免IndexOutOfBoundException,并且生成Photo对象保证不出现NullPointerException. 示例如下:
public Photo getPhoto(int index) { // 如果链表不存在则创建 if (photos == null) { photos = new ArrayList<Photo>(index + 1); } // 如果链表长度不够,扩展 if (photos.size() < index + 1) { for (int i = photos.size(); i < index + 1; i++) { photos.add(null); } } // 取得photo,如果为空则创建 Photo photo = photos.get(index); if (photo == null) { photo = new Photo(); photos.set(index, photo); } return photo; }
这样就可以了,没有用到session. 看起来比较繁琐,其实道理很简单,明白了struts form的填充方式,就没什么困难了。
另一个例子是客户端可以用JavaScript生成一堆这样的file input:
<input type=”file” name=”file[0]” />
<input type=”file” name=”file[1]” />
<input type=”file” name=”file[2]” />
<input type=”file” name=”file[3]” />
struts怎么填充呢?它会首先读取文件数据,创建FormFile对象,然后调用对应的ActionForm的setFile(index, file)方法填充,index就是那个“数组下标”。所以你需要写一个setFile方法来正确处理。
Leave a Reply