[本文中的程序在JDK 6,
Tapestry 4.1中测试通过]
用 Tapestry 自定义组件和创建一个page一样简单,同样要创建三个文件,html模板,配置文件,java类文件,只不过配置文件后缀不是page了,而是jwc。
定义组件的html模板
组件html模板和page的模板基本差不多,只不过组件模板可以是html的片段,也可以是完整的html文件。我们要实现的CheckboxList只需要一个html片段作为模板:
将一个表格作为模板,每一个checkbox和一个标签作为一行。
在 CheckboxList.java处理类中,添加两个组件的参数:
这两个参数接受用户指定的 所有checkbox项的集合和 组件显示的时候选中项的集合,前者是required,后者是可选的(不指定的话就是不选中任何的checkbox)
在 CheckList.jwc文件中,循环指定给组件参数的allItems:
将curItem和itemIndex输出给CheckboxList模板上的Checkbox组件和Insert组件,分别显示一个Checkbox和一个标签:
注意Checkbox中的value="mapping[temIndex]",这个mapping是一个boolean的数组,和allItems中的 项一一对应,里面标识了所有的checkbox项的选中情况,某项选中则对应的mapping[i]为true,否则为false。这个mapping 需要我们根据组件使用者给定的两个参数来生成。 renderComponent方法是BaseComponent的用于渲染整个组件的方法,我们需要 override该方法并在这里生成或者说初始化mapping:
然后,你就可以在你的页面中使用CheckboxList组件来显示内容了。
至此,我们的CheckboxList组件已经可以页面上显示所有的Checkbox项,并正确选中用户指定的Checkbox项了。
但是还没有完, 在我们修改这些Checkbox的选中状态后,如何将改动反映给处理类,使之可以将改动保存到DB中呢?
可以看到,虽然我们将选定项集合从参数selectedItems传递到了我们自定义的CheckboxList组件中, 但是真正和Checkbox组件 绑定的是mapping,所以当你改变某个Checkbox的选中状态后相应的mapping的值会改变,而不会改变参数selectedItems的内 容。因此,我们需要自己编码来把他实现了。在前面的CheckboxList.java中的renderComponent后面方法中加入以下语句:
调用cycle.isRewinding()方法告诉我们是否是用户提交表单。
完整的renderComponent方法就是:
这样,我们CheckboxList就可以和用户指定的选定项的集合完全的绑定起来,就是说既可以取值也可以设值了,一个具有完整功能的 自定义组件也就实现啦!
用 Tapestry 自定义组件和创建一个page一样简单,同样要创建三个文件,html模板,配置文件,java类文件,只不过配置文件后缀不是page了,而是jwc。
定义组件的html模板
组件html模板和page的模板基本差不多,只不过组件模板可以是html的片段,也可以是完整的html文件。我们要实现的CheckboxList只需要一个html片段作为模板:
- <table border="0" cellpadding="0" cellspacing="0">
- <tr jwcid="allItems">
- <td>
- <input type="checkbox" jwcid="curItem"/>
- <span jwcid="curItemLabel">Checkbox 1 span>
- td>
- tr>
- table>
将一个表格作为模板,每一个checkbox和一个标签作为一行。
在 CheckboxList.java处理类中,添加两个组件的参数:
- @Parameter(name = "allItems", required = true)
- public abstract List getAllItems();
- @Parameter(name = "selectedItems", required = false)
- public abstract List getSelectedItems();
这两个参数接受用户指定的 所有checkbox项的集合和 组件显示的时候选中项的集合,前者是required,后者是可选的(不指定的话就是不选中任何的checkbox)
在 CheckList.jwc文件中,循环指定给组件参数的allItems:
- <property name="curItem"/>
- <property name="itemIndex"/>
- <component id="allItems" type="For">
- <binding name="source" value="allItems"/>
- <binding name="value" value="curItem"/>
- <binding name="index" value="itemIndex"/>
- <binding name="element" value="literal:tr"/>
- component>
将curItem和itemIndex输出给CheckboxList模板上的Checkbox组件和Insert组件,分别显示一个Checkbox和一个标签:
- <component id="curItem" type="Checkbox">
- <binding name="id" value="ognl:name"/>
- <binding name="value" value="mapping[itemIndex]"/>
- component>
- <component id="curItemLabel" type="Insert">
- <binding name="value" value="curItem"/>
- component>
注意Checkbox中的value="mapping[temIndex]",这个mapping是一个boolean的数组,和allItems中的 项一一对应,里面标识了所有的checkbox项的选中情况,某项选中则对应的mapping[i]为true,否则为false。这个mapping 需要我们根据组件使用者给定的两个参数来生成。 renderComponent方法是BaseComponent的用于渲染整个组件的方法,我们需要 override该方法并在这里生成或者说初始化mapping:
CheckboxList.java
- private boolean[] mapping = null;
- @Override
- protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
- mapping = new boolean[this.getAllItems().size()];
- for(int i=0;i<this.getAllItems().size();i++){
- if(getSelectedItems()==null)continue;
- mapping[i] = getSelectedItems().contains(this.getAllItems().get(i));
- }
- super.renderComponent(writer, cycle);
- }
然后,你就可以在你的页面中使用CheckboxList组件来显示内容了。
在你的页面中使用CheckboxList组件
- <component id="userSelectionList" type="CheckboxList">
- <binding name="name" value="literal:userSelectionList"/>
- <binding name="allItems" value="ognl:allUsers"/>
- <binding name="selectedItems" value="ognl:selectdUsers"/>
- component>
至此,我们的CheckboxList组件已经可以页面上显示所有的Checkbox项,并正确选中用户指定的Checkbox项了。
但是还没有完, 在我们修改这些Checkbox的选中状态后,如何将改动反映给处理类,使之可以将改动保存到DB中呢?
可以看到,虽然我们将选定项集合从参数selectedItems传递到了我们自定义的CheckboxList组件中, 但是真正和Checkbox组件 绑定的是mapping,所以当你改变某个Checkbox的选中状态后相应的mapping的值会改变,而不会改变参数selectedItems的内 容。因此,我们需要自己编码来把他实现了。在前面的CheckboxList.java中的renderComponent后面方法中加入以下语句:
java 代码
- if(cycle.isRewinding()){
- List reselectedList = new ArrayList ();
- for(int i=0;i<this.getAllItems().size();i++){
- if(mapping[i] == false)continue;
- reselectedList.add(this.getAllItems().get(i));
- }
- this.setSelectedItems(reselectedList);
- }
完整的renderComponent方法就是:
java 代码
- @Override
- protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
- mapping = new boolean[this.getAllItems().size()];
- for(int i=0;i<this.getAllItems().size();i++){
- if(getSelectedItems()==null)continue;
- mapping[i] = getSelectedItems().contains(this.getAllItems().get(i));
- }
- super.renderComponent(writer, cycle);
- if(cycle.isRewinding()){
- List reselectedList = new ArrayList ();
- for(int i=0;i<this.getAllItems().size();i++){
- if(mapping[i] == false)continue;
- reselectedList.add(this.getAllItems().get(i));
- }
- this.setSelectedItems(reselectedList);
- }
- }
这样,我们CheckboxList就可以和用户指定的选定项的集合完全的绑定起来,就是说既可以取值也可以设值了,一个具有完整功能的 自定义组件也就实现啦!