内容


Wicket 开发笔记

Comments

概要

Wicket 是一个轻量级的基于组件、事件驱动的 Java Web 应用程序框架,经过几年的发展如今最新版本已经到了 6.8.0,作为一个 Java Web 开发的开源框架,它的特点是小型、轻量、UI 和 Java 逻辑的分离,美工可以专注于 HTML 的开发,而且它隔离了对于 request 和 response 的处理,把程序员从这些繁琐的处理中解放出来,只需要关心业务逻辑,而且它的事件驱动模式会非常的容易上手,这也是它最大的优点。以下示例都是基于 Wicket1.5.5 而写的一些关于 wicket 开发中用到的高级特性, Wicket1.5.5 也是 2012 年初发布的,从被 Apache 收编后,版本直接改成 Wicket6.X,所以这些示例应该没有什么变化。

关于和 Spring 的集成

Wicket 对于 Spring 的支持或者说 Spring 对于它的支持都做的很非常好,只是很简单的修改 web.xml,在 Wicket 的 filter 加入 init-param 参数,然后在添加 Spring 的 listener 就可以了。如清单 1 所示。

清单 1. Web.xml 部分代码
 <context-param> 
 <param-name>contextConfigLocation</param-name> 
 <param-value>classpath:applicationContext.xml</param-value> 
 </context-param> 
	
 <filter> 
 <filter-name>HomePageApplication</filter-name> 
 <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> 
 <init-param> 
   <param-name>applicationFactoryClassName</param-name> 
   <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value> 
 </init-param> 
 </filter> 
 
 <listener> 
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
 </listener>

对于 Spring 的 applicationContext 文件的定义这里有不详细说了,唯一不同的就是里面需要增加对于 Wicket app bean 的定义,如清单 2 所示。

清单 2. applicationContext.xml 中的 Application bean 的定义
 <bean id="wicketApplication"class="com.ibm.testrun.model.host.HostApp"> 
 <property name="baseDao"ref="baseDao"/> 
 </bean>

另外对于 Spring的使用的时候,我们对于 Spring bean的注入可以使用最普通的 property形式进行,而 Wicket增加了 annotation形式的引用,这个非常的方便,我们只需要在需要注入 bean的属性上添加 annotation就可以了,如清单 3 所示。

清单 3.WebPage 中关于 Spring Bean 的引用
 @SpringBean(name="taskDao") 
 privateTaskDao taskDao;

但是需要注意的是这个似乎是对于 WebPage及其子类有效,如果您需要在其它地方,比如 WebApplication类里面使用 Spring的 bean还是 property的形式比较好用。

关于 CheckBoxMultipleChoice 的使用

在开发中发现 CheckBoxMultipleChoice 直接显示出来的 checkbox 并没有进行排版,而是像如图 1 所示直接输出 .

图 1. 原始 CheckBoxMultipleChoice
图 1. 原始 CheckBoxMultipleChoice
图 1. 原始 CheckBoxMultipleChoice

这种形式完全不能满足美工的需要,大多数时候我们需要对输出出来的 checkbox group 进行排版,如图 2。

图 2. 经过修改后的 CheckBoxMultipleChoice 输出
图 2. 经过修改后的 CheckBoxMultipleChoice 输出
图 2. 经过修改后的 CheckBoxMultipleChoice 输出

那么这个时候我们应怎么做呢,在看了它的源代码后发现,可以通过重载 CheckBoxMultipleChoice 的 appendOptionHtml方法,通过加入 html 元素来实现美工排版的需求,实现上图中的排版的示例代码,如清单 4 所示。

清单4. 重载 CheckBoxMultipleChoice 的 appendOptionHtml 方法代码
 @Override 
 protected void appendOptionHtml(AppendingStringBuffer buffer, 
 SuitesFileHolder choice, intindex, String selected) 
 { 
 if(index == 0) 
	 { 
 buffer.append("\n<table><tr><td>\n"); 
 } else
				if((index + 1) % 4 == 1) 
	 { 
 buffer.append("\n<tr><td>\n"); 
 } else
	 { 
 buffer.append("\n<td>\n"); 
	 } 
 super.appendOptionHtml(buffer, choice, index, selected); 
 inttotalSize = this.getChoices().size(); 
 if(index == totalSize - 1) 
	 { 
 buffer.append("\n</td>"); 
 if(totalSize % 4 != 0) 
		 { 
 for(inti = 0; (i < 4 - totalSize % 4); i++) 
			 { 
 buffer.append("<td></td>"); 
			 } 
		 } 
 buffer.append("</tr></table>\n"); 
 } else
				if((index + 1) % 4 == 0) 
	 { 
 buffer.append("\n</td></tr>\n"); 
 } else
	 { 
 buffer.append("\n</td>\n"); 
	 } 
 }

针对个别按钮对页面部分表单的 validation

默认情况下,页面中的 Button 如果设置 validation,会对页面所有表单进行。如果页面中用两个 Button,而一个按钮需要全表单进行 validation,而另一个 Button只需要对表单中的部分 input进行 validation。Button的 setDefaultFormProcessing这个方法虽然可以取消表单的 validation,但是它是会阻止表单数据的提交,所以不是一个好办法。Wicket 一个比较奇妙的地方就是 Form中嵌套 Form,这个似乎在 HTML中是不允许的,但只在 Wicket让这个成为可能,当然最终的 HTML 的实现,Wicket只是把内层的 Form用 DIV来实现,也是个很不错的想法。如清单 5 所示。

清单 5. Html 中嵌套 Form 的定义代码及 Java 实现代码
 <form  wicket:id="outerform"> 
 <input type="text"wicket:id="location"> 
 <input type="text"wicket:id="license"> 
 <form  wicket:id="innerForm"> 
 <input class="btn"type="submit"wicket:id="AddHost" value="AddHost"> 
 <select wicket:id="type"> 
 </form> 
 <input class="btn"type="submit" wicket:id="ok" value=" O K "/> 
 </form>

 InputForm form = newInputForm("outerform"); 

 private class InputForm extendsForm<?> 
 {
 add(newTextField<String>("license").setRequired(true)); 
 add(newTextField<String>("location").setRequired(true)); 

 Form<?> innerForm = newForm<?>("innerForm"); 
 innerForm .add(newDropDownChoice<SymphonyPageModel>("type", hostTypeModelList)); 
 innerForm.add(newButton("AddHost") 
			 { 
 public void onSubmit() 
				 { 
 // 您会发现现在即使 innnerForm 外面的 input 输入错误
 // 也能进来了,在这里您可以做您自己的逻辑了。
				 } 
			 });
 }

如何在 Wicket 中使用 Ajax

Web 开发中 Ajax 的使用非常普遍,Wicket 对 Ajax 的支持也做的非常的好,只需要编写 Jave代码就可以实现,html里面不需要任何代码的添加。如下面的这个例子,通过输入的 Host Name 获取对应 Host 的 IP,效果如图 3,代码如清单 6 所示。

清单 6. Ajax 的实现代码
 finalTextField<String> hostNameEle = newTextField<String>("name"); 
 finalTextField<String> hostIpEle = newTextField<String>("HostIp"); 
 hostIpEle.setOutputMarkupId(true);// 这句很重要否则会报错。

 // 这个就可以对一个控件添加一个 Ajax 事件。
 hostNameEle.add(newOnChangeAjaxBehavior() { 

 @Override 
 protected void onUpdate(AjaxRequestTarget target) 
	 { 
		 String hostIp = getAddressByHostName(hostNameEle.getValue()); 
		 hostIpEle.setModelObject(hostIp); 
 // 这里设置要对结果进行赋值的控件
		 target.add(hostIpEle); 
	 } 
				
 });
图3. Ajax根据输入的host name取得对应的IP
图3. Ajax根据输入的host name取得对应的IP
图3. Ajax根据输入的host name取得对应的IP

如何修改 DefaultDataTable 里面的某个列的数据

像下面的这个 List 我们往往从数据库里检索出来的某些字段是一些代码,而在页面上显示则要显示成代码对应的 message,例如图 4 的 status 字段,数据库中存放的都是 Status(UP, DOWN,UNKONW) 对应的数字代码,那这个时候我们应该怎么实现呢?

图 4.
图 4.
图 4.

我们以 DefaultDataTable 为例,只需在添加 Column 时候,重载它的 populateItem 函数,具体例子,如清单 7 所示。

清单 7. DefaultDataTable 的实现代码
List<IColumn<ProductBean>> columns = newArrayList<IColumn<ProductBean>>(); 
 columns.add(newPropertyColumn<ProductBean>(newModel<String>("ClusterName"), "name")); 
 columns.add(newPropertyColumn<ProductBean>(newModel<String>("Hosts"), "hosts")); 
 columns.add(newPropertyColumn<ProductBean>(newModel<String>("status"), "status"){ 

 @Override 
 public void populateItem(Item<ICellPopulator<ProductBean>> item, 
			 String componentId, IModel<ProductBean> rowModel) 
	 { 
 // 我们就可以在这里改变它的值
 item.add(newLabel(componentId, statusInfoMap.get(rowModel.getObject().getStatus()))); 
	 }
 }); 
        
 columns.add(newPropertyColumn<ProductBean>(
newModel<String>("shareBinary"), "sharebinary")); 
 columns.add(newPropertyColumn<ProductBean>(newModel<String>("product"), "product")); 

 form.add(newDefaultDataTable<ProductBean>
("clusterList", columns, newSortableClusterDataProvide(products), 20));

在 TableList 这类的控件中添加 Link、Button 之类的事件响应元素

在 TableList 这类的控件中添加 Link、Button 之类的事件响应元素

还是关于 List 的问题,有时候我们需要往行里面添加一些 Action 按钮,这个也是比较常见的,下面图 5 这样的例子,具体实现如清单 8、清单 9 所示

图 5.
image005.png
image005.png
清单 8.ActionPanel 的 Java 实现代码
// 首先我们需要创建这个 action 对应的 Panel 容器。
 private class ActionPanel extendsPanel 
 { 
 public ActionPanel(String id, finalIModel<ProductBean> model) 
	 { 
 super(id, model); 
 /* 这个是给 Panel 里面添加各个 link 标签,在各自的 onclick 事件里来做自己的
                    逻辑处理,同时 model.getObject() 通过这个方法我们就可以拿到绑定到当
                   前 Panel 上的 Model 对象,一般我们会绑定当前行对应的 databean。*/ 
 add(newLink<ProductBean>("Startup", model) 
		 { 
 @Override 
 public void onClick() 
			 { 
				 ProductBean pb = model.getObject(); 
			 } 
		 }); 
 add(newLink<ProductBean>("Shutdown", model) 
		 { 
 @Override 
 public
				voidonClick() 
			 { 
				 ProductBean pb = model.getObject(); 				
			 } 
		 }); 
 add(newLink<ProductBean>("Delete", model) 
		 { 
 @Override 
 public void onClick() 
			 { 
				 ProductBean pb = model.getObject(); 
				
			 } 
		 }); 
	 } 
 }
// 对于 column 的设定,同样的利用 column 的 populateItem 方法对 actions 设置 Panel。
 columns.add(newAbstractColumn<ProductBean>(newModel<String>("Actions")){ 

 public void populateItem(
Item<ICellPopulator<ProductBean>> cellItem, String componentId,  IModel<ProductBean> model) 
	 { 
 cellItem.add(newActionPanel(componentId, model)); 
	 } 
 });
清单 9.ActionPanel 的 Html 代码
 <wicket:panel xmlns:wicket="http://wicket.apache.org"> 
 <a href="#"wicket:id="Startup">Startup</a>&nbsp;|&nbsp; 
 <a href="#"wicket:id="Shutdown">Shutdown</a>&nbsp;|&nbsp; 
 <a href="#"wicket:id="Delete">Delete</a> 
 </wicket:panel>

定制自己的 Validator

对于页面上的有些输入框,有时候我们需要定制自己的 Validator,在 Wicket里面我们应该怎么做呢,下面举个例子:

比如我们在要给 Textbox里输入多个 email地址,以逗号分割,然后我们要实现这样的 Validator。代码如清单 10 所示。

清单 10. EmailAddressValidatorEx 的实现代码
protected class EmailAddressValidatorEx extendsEmailAddressValidator 
 {
 @Override 
 protected void onValidate(IValidatable<String> validatable) 
	 { 
 String[] mails = validatable.getValue().split(","); 
 for(String mail : mails) 
		 { 
 Validatable<String> vMail = newValidatable<String>(mail); 
			 vMail.setModel(validatable.getModel()); 
 super.onValidate(vMail); 
 if(vMail.getErrors().size() > 0) 
			 { 
 ValidationError vee = newValidationError(); 
 // 其实这个地方我们也可以直接设置 message,而不是
 // 从 properties 去取 message,当然是不在需要国际化的时候。
 //vee.setMessage(mail + " is not a valid email address."); 
 vee.addMessageKey("EmailAddressValidatorEx"); 
				 validatable.error(vee); 
			 } 
		 } 
	 } 
 } 
 // 使用的地方我们只需要对 components 加入这个 validator 就可以了。
 taskForm.add(new TextField<String>("reportTo").add(new EmailAddressValidatorEx()));

定时刷新页面或者页面中的单独控件

AjaxSelfUpdatingTimerBehavior这个类将很方便的帮助我们解决这个问题。下面来看看这个怎么用。

比如我们需要让一个 Table里的数据定时刷新,以 DefaultDataTable为例,

DefaultDataTable<TaskBean> dataTable = 
newDefaultDataTable<TaskBean>
("taskHost", colsArr, newSortableTaskDataProvide(taskDao), 20);

然后我们 new 一个 AjaxSelfUpdatingTimerBehavior

AjaxSelfUpdatingTimerBehavior asutb = newAjaxSelfUpdatingTimerBehavior
(Duration.seconds(AUTO_REFRESH_TIME));

之后将这个 Behaviour 添加到 DataTable 里就可以了,

 dataTable.add(asutb);

这里需要注意的是对于 Component 绑定的数据对象要能实时的拿到新的数据,比如这个例子里的 SortableTaskDataProvide 的实现需要注意。

完整代码如清单 11 所示:

清单 11. SortableTaskDataProvide 的实现代码
class SortableTaskDataProvide extendsSortableDataProvider<TaskBean> { 

 private TaskDao taskDao; 
		
 public SortableTaskDataProvide(TaskDao taskDao) { 
 this.taskDao = taskDao; 
		 } 
 private static final long serialVersionUID= 1L; 

 public Iterator<TaskBean> iterator(final intarg0, final intarg1) { 
 List<TaskBean> taskList = null; 
 try
			 { 
 taskList = taskDao.getAllTask(); 
 } catch(SQLException e) 
			 { 
				 e.printStackTrace(); 
			 } 
 return taskList.subList(arg0, arg0+arg1).iterator(); 
		 }
			public IModel<TaskBean> model(finalTaskBean arg0) { 
 return new DetachableTasksModel(arg0); 
		 }			public
				intsize() { 
 try
			 { 
 return taskDao.countAllTask(); 
 } catch(SQLException e) 
			 { 
				 e.printStackTrace(); 
 return0; 
			 } 
		 } 
	 } 
 class DetachableTasksModel extendsLoadableDetachableModel<TaskBean> { 

 private static final long serialVersionUID= 1L; 
		
 private TaskBean task; 
			
 public DetachableTasksModel(TaskBean task) { 
 this.task = task; 
		 } 
		
 @Override 
 protected TaskBean load() { 
 returntask; 
		 } 
	 }

修改 Wicket 并未提供的对应 HTML 元素的属性(如 DIV、SPAN)

修改 Wicket 并未提供的对应 HTML 元素的属性(如 DIV、SPAN)

在开发中我遇到了一个问题,就是想要控制 DIV 或者 SPAN 这样的 HTML 元素,但 Wicket 并没有提供的对应的标签,我们要修改它们的一些属性,或者给它的里面添加文字,这个时候不知道怎么办了,最后在一个论坛里发现的一个问题给了我启示,我们可以通过 WebMarkupContainer 这个类来绑定到 DIV 这些 HTML标签上去,然后对其添加相应的 Behavior 来修改其属性,这里我们使用 AttributeModifier 这个来修改 DIV 的属性:代码如清单 12 所示。

清单 12.如何修改控件属性
 WebMarkupContainer wmc = newWebMarkupContainer("progress"); 
 // 这我们修改 DIV 的 style 属性
 wmc.add(AttributeModifier.append("style", "width:"+percentage+"%"));

如果我们要给 DIV 里添加文字,该如何做呢?其实通过重载 WebMarkupContainer#onComponentTagBody 的这个方法就能够实现。

代码如清单 13 所示。

清单 13.重载 onComponentTagBody 方法的代码
 WebMarkupContainer wmc = newWebMarkupContainer("progress"){ 

 @Override 
 public void onComponentTagBody(MarkupStream markupStream, 
					 ComponentTag openTag) 
	 { 
 // 这里,我们可以通过 replaceComponentTagBody 这个方法来,修改 DIV 内的文字。
 replaceComponentTagBody(markupStream, openTag, percentage + "%"); 
 super.onComponentTagBody(markupStream, openTag); 
	 } 
			
 };

关于 properties 文件使用时候的搜索顺序

对于 message 放置的 properties 文件,为了方便搜索查找可以遵循下面的规则直接写到 yourapp.properties 文件里面。

关于 properties 文件的查找顺序:

Wicket will search up the hierarchy 

YourPanel.properties 

YourCoolPage.properties (Panel所在的Page) 

YourBaseWebPage.properties (你所用于所有Page的基类 (用于共享公共的布局或者功能))
 
YourApp.properties

结束语

Wicket 的编程模式在之前也是提过,它更像 JavaSwing 的编程,开发者若是之前做过 Swing 的开发会很容易上手,也不会像 Struts、Spring 这些框架,过度使用 XML 进行繁琐的配置。

上面的这些示例都是我自己在做开发过程中所遇到的一些问题,在这里做的一些总结,由于关于 Wicket 的资料确实很少也希望这些总结能给大家带来一些帮助,有什么错误的地方还望大家多多指正。


相关主题

  • 这个是 Wicket 的 Wiki,在这里您可以看到关于 Wicket 的介绍以及一些很有用的链接。
  • 访问 Wicket 主页您可以从这里得到关于 Wicket 的很多官方的入门资料信息。
  • Wicket Sample 这里您可以得到官方提供的很多例程。
  • apache-wicket 论坛 里,您可以在这里找到很多问题的答案或者得到非常有用的帮助。
  • developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=952445
ArticleTitle=Wicket 开发笔记
publish-date=11142013