技术开发 频道

取代JSP的新技术-tapestry

    一个复杂的应用

    在这个应用中我们以一个简单的学生管理系统为例介绍一下Tapestry的常用功能。我们要实现学生的增加和显示,因此我们需要两个html页面。至于StudentServlet类和Student.application我们就不描述了,在Student.application中定义了两个page:Home和EditStudent,具体看附件。学生数据存放在数据库中,我们用Student类表示数据中的一条记录,用StudentFactory类检索学生数据,这两个类用到了一个JDBC包装器,关于这个JDBC包装器可以见我的另外一篇文章<<对一个简单的 JDBC 包装器的扩展及应用>>。
    首先看一下Home.html
<html> <head> <title>学生管理</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body bgcolor="#FFFFFF"> <p align="center">学生列表</p> <table width="100%" border="1"> <tr> <td >学号</td> <td >姓名</td> <td >性别</td> <td >班级</td> </tr> <span jwcid="liststudent"> <tr> <td><span jwcid="id">20012400</span></td> <td><span jwcid="sname">宗锋</span></td> <td><span jwcid="gender"></span></td> <td><span jwcid="department">计算机研一</span></td> </tr> </span> <tr jwcid="$remove$"> <td>20011389</td> <td>桑一珊</td> <td></td> <td>计算机研一</td> </tr> </table> <a jwcid="add">添加学生</a> </body> </html>
    与前面的简单应用不同,我们在这个页面中定义了七个组件,下面看一下部分Home.jwc文件,我们将详细讲述一下怎样描述这些组件。
<specification class="test.ListStudent"> <component id="liststudent" type="Foreach"> <binding name="source" property-path="student"/> <binding name="value" property-path="eachstudent"/> </component> <component id="id" type="Insert"> <binding name="value" property-path="eachstudent.id"/> </component> <component id="add" type="Page"> <static-binding name="page">EditStudent</static-binding> </component> </specification>
    在这里,我们的specification的class属性值不再是BasePage,而是其派生类ListStudent。对于每一个组件,id属性指定唯一的标识符,这个值与html文件中的jwcid值相对应,type 指定组件名,binding指定组件怎得到数据,property-path是一系列属性的集合,这些属性一般定义在javabean中,例如上面的property-path="student",则在相应的javabean类ListStudent中应该有个函数getStudent。liststudent是一个Foreach组件,这个组件其实是一个for循环,它从source中读入一个数组,将其一个一个的赋值给value参数指定的属性。id,name,gender,department四个是Insert组件,这个组件用来插入文本数据,参数value指定要插入的值,property-path指定怎样获取这些值,eachstudent.id相当于调用javabean的getEachstudent().getId()。add是一个Page组件,page属性指定页面名(定义在application文件中),static-binding表明要绑定的数据是不可修改的。$remove$组件没有在这个文件中描述,因为Tapestry运行时会自动删除这种组件。
下面看一下ListStudent类:
package test; import com.primix.tapestry.*; import sun.jdbc.odbc.JdbcOdbcDriver ; /** * 返回每个学生的数据 * */ public class ListStudent extends BasePage { private Student eachstudent; private Student[] student; public void detach() { eachstudent=null; student=null; super.detach(); } public Student getEachstudent() { return eachstudent; } public void setEachstudent(Student value) { eachstudent = value; } public Student[] getStudent() { try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); student=StudentFactory.findAllStudents(); }catch(Exception e){ e.printStackTrace(); } return student; } }

    这个类有四个函数,其中detach函数是将页面放入缓冲池时执行的操作,getStudent函数返回所有的学生记录,这是给jwc文件中liststudent组件的source参数赋值,getEachstudent给这个组件的value参数赋值,因为source是一个数组,每次循环需要从中取出一条记录赋值给eachstudent,所以还有一个函数为setEachstudent,你会注意到这个函数很简单,其实是Tapestry帮你做了大部分工作。

    至此,显示学生的部分已经完成,下面看一下EditStudent.html

<html> <head> <title>增加学生</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body> <p><img src="student.gif" width="32" height="32"/> 学生管理系统</p> <form jwcid="form"> <span jwcid="ifError"> <font size=+2 color=red><span jwcid="insertError"/></font> </span> <p>学号: <input jwcid="id"/> </p> <p>姓名: <input jwcid="name"/> </p> <span jwcid="gender"> <p>性别: <input jwcid="male"/> <input jwcid="female"/> </p> </span> <p>班级: <input jwcid="department"/> </p> <p> <input type="submit" value="确定"> </p> </form> </body> </html>

    在这个文件中,用到了另外一些常用的组件,先看一下EditStudent.jwc中的这些组件的描述:

<specification class="test.EditStudent"> <component id="form" type="Form"> <binding name="listener" property-path="listeners.formSubmit"/> </component> <component id="gender" type="RadioGroup"> <binding name="selected" property-path="gender"/> </component> <component id="ifError" type="Conditional"> <binding name="condition" property-path="error"/> </component> <component id="insertError" type="Insert"> <binding name="value" property-path="error"/> </component> <component id="id" type="TextField"> <binding name="value" property-path="id"/> </component> <component id="male" type="Radio"> <field-binding name="value" field-name="test.EditStudent.MALE"/> </component> </specification>

    form是一个Form组件,它的参数listener指定submit这个form时有那个函数处理。ifError是一个Conditional组件,这个组件指定当condition满足时才会显示,在本例中,如果error不为空,则condition满足。在这个组件中,有嵌套了一个Insert类型的组件,用于将错误显示。这是Tapestry中经常用到的处理错误的方式。gender是一个RadioGroup组件,它绑定了javabean中的gender属性,selected参数指定那个radio被选中,在这个组件中,又嵌套了两个Radio组件,分别用来表示男,女。Radio的value参数指定当用户选定这个radio时,RadioGroup绑定的属性值将会等于field-name中指定的值(这个值必须是static的),在本例中,gender=test.EditStudent.MALE。id是一个TextField组件,其参数value绑定到javabean中的id属性。
下面是相应的EditStudent类:

package test; import com.primix.tapestry.*; public class EditStudent extends BasePage { public static final int MALE = 1; public static final int FEMALE = 2; private int gender; private String error; private String id; private String sname; private String department; public void detach() { error = null; id=null; sname=null; gender=0; department=null; super.detach(); } public int getGender() { return gender; } public String getId() { return id; } public String getSname() { return sname; } public String getDepartment() { return department; } public void setGender(int value) { gender = value; fireObservedChange("gender", value); } public void setId(String value) { id = value; fireObservedChange("id", value); } public String getError() { return error; } public void setSname(String value) { sname = value; fireObservedChange("sname", value); } public void setDepartment(String value) { department = value; fireObservedChange("department", value); } public void formSubmit(IRequestCycle cycle) { //判断用户是否添完了所有数据 if (gender== 0||id==null||id.equals("")||sname==null||sname.equals("")|| department==null||department.equals("")) { error = "请填充完所有选项"; return; } //将学生保存 try{ Student student=new Student(); student.setId(id); student.setName(sname); if(gender==1) student.setGender(""); else student.setGender(""); student.setDepartment(department); student.save(null); }catch(Exception e){ e.printStackTrace(); } //清空当前的各个属性,以免再次进入此页面时,各属性仍旧保留原来的值 setSname(null); setDepartment(null); setId(null); setGender(0); //重定向到Home页面 cycle.setPage("Home"); } }

    在本类的一些设置属性的函数中使用了fireObservedChange这个函数,这个函数激发一个改变事件,通知当前的属性的值已经改变。

0
相关文章