用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 1span>
- 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:
- 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組件來顯示內容了。
- <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後面方法中加入以下語句:
- 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方法就是:
- @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就可以和用戶指定的選定項的集合完全的綁定起來,就是說既可以取值也可以設值了,一個具有完整功能的自定義組件也就實現啦!