十二. 自定义tapestry组件时,如果定义的组件包含有.html模板的话(即:allow-body="no",不使用组件的body,直接用.html的body),则该自定义组件对应的java类必须extends BaseComponent类,否则如果定义的组件的输出是通过对应的java类来输出html标签(没有.html模板,allow-body="yes"),则必须继承AbstractComponent类,并覆盖renderComponent()方法.;
在java中输出Html代码需要使用Markup Writer, 对于有.html模板的自定义组件,在定义该组件的时候需要用 "$content$"的形式来处理该模板中其他的<html/>,<body/>等元素。
对于"$content$"的形式的说明及必要性:
如果自定义的组件模板如下:
<html>
<body jwcid="$content$">
<hr>
Copyright <span jwcid="year">2005</span>. Foo Inc. All rights reserved.
</body>
</html>
说明: <body>有一个"jwcid"属性,说明了是Tapestry 组件.并且发现该组件的 id是"$content$". 这个id对于tapestry来说有相当的意义:它不是一个真实的组件的It ,它主要告诉Tapestry 使之丢掉到当前时间为止的所有该模板的html脚本,只使用其以后的元素(starting from the <hr> tag)作为真实的也面元素。
结果,则在实际的运行过程中被解析为:
<hr>
Copyright <span jwcid="year">2005</span>. Foo Inc. All rights reserved.
另例:<tr jwcid="$remove$"><td>2</td><td>Elton</td><td>John</td><td>285984</td></tr>表示为空的html代码;
在自定义组件的时候如果要用到 informal 参数,则要在调用时在.jwc中设置inherit-informal-parameters="yes" 属性.
其他:如果组件使用<reserved-parameter>元素来声明它的保留名称,这些保留名称不允许使用非正式参数,因为组件不想让非正式参数去覆盖它的值,并会自己产生这些命名属性.
十三. Contrib组件应用
<page-specification ...>
<component id="table" type="Contrib:Table">
<binding name="source" value="entries"/>
<binding name="columns"
value="literal:id, firstName:name.firstName, lastName:name.lastName, telNo"/>
</component>
</page-specification>
注释:其中的<binding name="columns" value="literal:id, firstName:name.firstName, lastName:name.lastName, telNo"/> 中的firstName:name.firstName表示通过OGNL的方式解析id为firstName的组件,该组件没有指定标题,表示调用getName()方法,获得Name对象,然后调用其getFirstName()方法,获得name的first属性.
如果改为<binding name="columns" value="literal:id, firstName:First Name:name.firstName, lastName:name.lastName, telNo"/>具有上面的特色外,另外在表头的标题为First Name;
十四. 如果程序中用注入元数据的方式给元素属性赋值时, 可以利用 @InjectMeta 的注入方式;
例如:
@InjectMeta("com.ttdev.album.image-folder")
public abstract String getImageFolder();
表示通过注入的方式给属性赋值,其中的 "com.ttdev.album.image-folder" 属性可以按下列方法找:
1. 在.page 文件中找<meta key="???" value="???"/>,如果找不到则
2. 在.application 文件中找
<application ...>
<meta key="???" value="???"/>
</application>
3. 在context部署描述中找:
<Context ...>
<Parameter name="???" value="???"/>
</Context>
4.在web.xml文件中找:
<web-app ...>
<context-param>
<param-name>???</param-name>
<param-value>???</param-value>
</context-param>
</web-app>
十五. 在页面中嵌入 .script脚本,可以利用Script组件
A. 例如有脚本文件:Confirm.script放在web-inf下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script PUBLIC
"-//Apache Software Foundation//Tapestry Script Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Script_3_0.dtd">
<script>
<input-symbol key="msg" required="yes"/>
<body>
function getConfirmation() {
return confirm("${msg}");
}
</body>
</script>
其中的<input-symbol key="msg" required="yes"/>中的msg表示是要求调用者输入的参数
B. 如果要在home.html中调用刚才的script,则要声明如下:
.page文件中:
<page-specification class="com.ttdev.confirmdelete.Home">
<component id="insertConfirmScript" type="Script">
<binding name="script" value="literal:Confirm.script"/>
<binding name="symbols" value="ognl:#{'msg': 'Are you sure?'}"/>
</component>
</page-specification>
其中<binding name="symbols" value="ognl:#{'msg': 'Are you sure?'}"/> 表示给msg赋值为"Are you sure?" ,在此也可以在.java中动态赋值;
.html文件中
<body jwcid="@Body">
<span jwcid="confirmDeleteScript"/>
<form action="http://www.foo.com">
<input type="Submit" value="Delete" onclick="return getConfirmation()"/>
</form>
</body>
十六. HtmlUnit用于页面单元测试。
配置:从http://htmlunit.sourceforge.net下载htmlunit-1.6.zip 或之后版本,将其解压,将解压后的文件夹打开,将htmlunit\lib下的所有.jar文件加入到Eclipse 的buildPath中;另外还要将eclipse\org.junit_3.8.8\junit.jar也加入。
步骤及方法:
1.新建一个test class继承TestCase类。
2.该该类中定义测试方法,测试方法只有以test开头才能被junit测试到,否则不被处理;
3.如果该类中有setUp()方法存在,则先执行该方法,该方法中可以放许多初试化的信息。
4.如果工程中有许多的测试类,我想一次测试所有的测试类中的方法,则可以考虑用suite()方法,例如:
static public TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(TestsForCalculation.class); //将要执行的测试类加入到该对象中
suite.addTestSuite(TestsForHistory.class); //将要执行的测试类加入到该对象中
return suite;
}
说明:
HtmlUnit can help you create automated tests for your web application. You can use it to simulate user actions through a simulated browser.
After getting a response page for a URL, you can check it by getting various elements such as <span>, <input>, <select>, <table> using their id's or names and then check their contents.
To check a Javascript alert dialog, use an alert handler to collect the alert message.
To check the contents of a popup window, use a web window listener.
To open a link in a popup window, use a PopupLinkRenderer with the link component.
十七. 开发tapetsry时,注意的事项:
关闭Tapestry缓存池,提高开发效率
Tapestry对页面有自己的缓存策略。一旦页面被第一次访问,页面对象就会被加载到缓存中。因此,每次对HTML、page或java的修改,并不会马上生效,必须重新启动服务器。在我们开发程序的时候,这样相当不方便。因此我们可以关闭Tapestry的缓存策略,等到项目发布的时候,再开启。
在MyTapestry.application中添加:
<meta key="org.apache.tapestry.disable-caching" value="true"/>
<meta key="org.apache.tapestry.enable-reset-service" value="true"/>
并在JVM中添加:
-Dorg.apache.tapestry.disable-caching=true
-Dorg.apache.tapestry.enable-reset-service=true
十八。对session和application对象的管理
a.对session对象的管理:
tapestry中我们将session的管理交给了visit对象,在MyTapestry.application中声明visit对象:
1。 <meta key="org.apache.tapestry.visit-class" value="com.tapestry4.MyVisit"/>
然后我们在页面中,可以这样调用visit对象:
MyVisit visit = (MyVisit)this.getVisit();
2。 我们可以通过annotation来声明对visit对象的调用:
@InjectState("visit")
public abstract MyVisit getMyVisit();
3。如果不使用annotation,可以在page中声明:
<inject property="myVisit" type="state" object="visit"/>
然后在页面中仍然以抽象方法获取visit对象。
b.对application对象的管理:
对于session对象的管理,Tapestry是放到一个独立的visit对象中的,@InjectState有两个默认参数,一个是visit,另外一个是global,用于获取visit对象和global对象。
对于application对象的管理,Tapestry是放到一个独立的global对象中的,该对象的用法与visit一摸一样,不同的仅仅是对象的生命周期。同样,我们可以定义一个自己的global对象,在MyTapestry.application文件中声明:
<meta key="org.apache.tapestry.global-class" value="com.tapestry4.MyGlobal"/>
十九. DirectLink组件
作用:当用户点击时,触发页面java中一个特定的监听方法。
例如:
html中: <a href="#" jwcid="buyFruitLink"> XXX</a>
page中:
<component id="buyFruitLink" type="DirectLink">
<binding name="listener" value="listener:buyFruit"/>
<binding name="parameters" value="fruit.id"/>
</component>
java中: 直接将DirectLink中传递过来的参数作为监听方法的参数.(但在T3中不支持)
public IPage buyFruit(Integer ig) {
BuyFruit page = this.getBuyFruitPage();
page.setFruitId(ig);
return page;
}
在这个监听方法中,我们获取到了DirectLink传递的参数,并且我们向下一个页面传递水果的ID值,在下一个页面,我们就可以根据这个ID值,查找到对应的水果详细信息。
区别T3: T4中监听方法的用法有很大区别,现在我们看到的是监听方法的另外一种用法。直接将DirectLink传递的输入参数作为其监听方法的参数。一旦DirectLink触发监听方法,我们直接就可以从监听方法的输入参数中获取到被传递过来的fruit.id。当然,监听方法的输入参数是与fruit.id类型相对应的。
二十. 数据类型自动转换机制(translator)
translator目前支持的类型有三种:date,number,String。
例如:
<component id="age" type="TextField">
<binding name="value" value="age"/>
<binding name="translator" value="translator:number"/>
<binding name="displayName" value="literal:Age"/>
</component>
此处translator的作用是:如果用户输入的值不是数值类型,则脚本提示输入有误.
二十一. @Persist("client") 和 Hidden
使用@Persist("client")的效果与Hidden一样。实际上,@Persist("client")的作用远比Hidden强大。细心的朋友在页面运行之后,查看BuyFruit页面在浏览器中的源代码会发现,Tapestry4向页面中添加了下面这样的内容:
<input type="hidden" name="state:BuyFruit" value="BrO0ABXcOAAAAAQAAB2ZydWl0S
WRzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmE
ubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAQ=="/>
<input type="hidden" name="reservedids" value="state:BuyFruit"/>
Hidden组件只能在Form表单中使用,并且只能够被Form表单获取。而@Persist("client")保存的数据却可以被页面上的任意监听方法获取。比如DirectLink,ActionLink等。呵呵,很方便。
二十一. @Persist("client") 和 @Persist("session")
@Persist("client"):每个页面有不同的属性值,则用该持久属性;
注意:在tapestry的page pool中可能有一个页面的不同示例; 例如:一个productDetailPage页面,可能有productId=1的productDetailPage页面实例和可能有productId=2 的productDetailPage页面实例存在; 当用户想从 page pool 取页面事例的时候,会根据productID来取得已经存在的页面.
@Persist("session") :每个用户有不同的属性,则用该属性;
注意: 当使用 持久化到session的时候,可能出现的问题:
例如:
如果将客户产品明细页面中用到的productId放在session中, 当一个客户在网上浏览产品,该产品有 id=01, id=02 两件, 可能出现的错误:
首先,客户从list页面查看 id=01产品的详细信息,那么session中保存了的id=01,
然后客户回到list页面,查看id=02的产品信息,那么session中就会保存id=02.
现在,客户按 浏览器的"后退"按钮,返回到id=01的产品明细页面. 直接按"Add to cart" 按钮, 此时,
客户以为将id=01的产品放入了购物车, 但实际上在session中保存的id=02, 该页面提交时,会从session中取的id,所以放到购物车的id=02,而不是01;
二十二. Tatestry内部js.
当我们用tapestry开发页面的时候,tapestry会自动将对应的js引入进来,开发人员只需要直接调用即可:
例如:
function checkBlankSelect(form,field,msg,event){
Tapestry.onsubmit(form,
function(event) {
var fieldObj = document.getElementById(field);
if(fieldObj.value == 0){
event.invalid_field(fieldObj , msg);
}
}
);
当页面在运行生成的html中会产生如下的代码:<script type="text/javascript" src="/tapestry4test/assets/1f7008d3e65882519740d925d6c5a813/org/apache/tapestry/form/Form.js"></script>
也就是tapestry帮我们做了一些工作,具体实现可以查找 org/apache/tapestry/form/目录下的具体.js文件;