=========================
无验证 绘制html页面(组件)
Tapestry 是建立在组件基础上的,这是一个什么概念呢?我认为,就是对每一个HTML元素进行和request和response。打个比方,某张页面
test.html,源程序如下:
<html>
<script language="javascript" type="text/javascript">
<!--
....
-->
</script>
<body>
<input type="submit" name="mysubmit" onClick=" ..." />
<textarea cols=number name="myTextArea" rows="5"></textarea>
</body>
</html>
test.html中的body元素,内嵌了2个元素,分别是input和textarea元素。
注意:绘制页面,要注意是render还是rewinding,
body元素就可以认为是一个组件,tapestry对应的是组件是Body, 查看一下org.apache.tapestry.html.Body,就可以很明白的,Body中最主要
是renderComponent()方法,它先是找出自己内嵌的元素个数,绘制这些元素,也就是写各个元素的html代码,然后自身写出<body></body>,把
刚才的代码放到<body></body>中间,再写出<script language="javascript" type="text/javascript"><!-- ....--></script>。一张test.html
页面就绘制出来了。至于body是如何收集其内的全部html和javascript代码,则要看下具体body的源代码的。其实说穿了,绘制页面,也就是调用了Servlet中的PrintWrite,在Tapestry中当然就是MarkupWrite了。组件Body是个顶层组件
,也可以当做是个容器,它里面包含其他组件。
处理input元素所发出submit动作的绘制,在html中submit肯定是要放在form中的,否则无法提交数据(test.html里面没有把 form元素写出来) 。input元素也是一个组件,绘制这个组件,也就是生成html语言,是
交给Tapestry中的Submit组件来完成,查看org.apache.tapestry.form.Submit,仔细看一下它的renderComponent()方法,首先是得到name属性的具体
value值,在test.html中就是得到name="mysubmit"中的mysubmit这个字符串,至于onclick是如何得到的?这段javsscript被tapestry当做是initializationscript来处理和绘制的。除此之外,还有informal parameters,当然是tapestry中组件相应的html模板中取来的了。
处理textarea 元素所发出动作的绘制和input元素的绘制是差不多,查看org.apache.tapestry.form.TextArea。
综上可以看出,我们常用的web页面,经常是包括javascript和html标签的一些元素和属性,tapestry要绘制出一张html页面,必须事先把这些
内容写好且放在一个地方,然后才能够用java语言(servlet)来绘制出页面。架构编程讲究的是弱耦合,所以把javascript和html标签的一些元素
和属性放在xml中是个不错的选择,放在xml中后就变成了模板概念,java(servlet)只要按照着这个模板读,然后写就可以完成页面的html绘制。
=======================
带验证 绘制html页面(组件)
Struts中,我常常把验证放在action中实现,结果代码往往很混乱,当然原因是自己写的不够专业。
验证逻辑应该单独做个类比较好,Tapestry 就是如此。
验证功能,Tapestry 分成delegate和validator
delegate参见org.apache.tapestry.valid.ValidationDelegate,可以自己写,它表明一种可能,就是指出该componet是要验证的,
delegate比较重要的函数是record()方法,此方法保存出错的信息,给显示错误提供帮助,假如自己要写的话,最好仔细的参照IValidationDelegate接口来写。
validator有好多种,也可以自己写,就是实现验证的逻辑,它比较重要的是renderValidatorContribution()方法, 其中有这么if (!isClientScriptingEnabled()),
它的意思是说客户端没有实现验证,那么就让tapestry 的validator 来做,怎么做?validator 中导进javascript代码,把这代码给body,body来绘制,结果就是看起来好象
客户端有javascript代码了。
参见其中的一种org.apache.tapestry.valid.StringValidator,它实现字符串的验证逻辑。
假如某个验证由textarea元素(组件)发出,首先,通过RequestCycle获取这个form, 通过form的xml模板,可以取得相应的delegate和validator,通过RequestCycle还得获取myTextArea属性所传递的具体value,
拿validator和这个value做验证逻辑,假如和逻辑不符,就可以用一些方法,比如可以抛出异常(这个异常是org.apache.tapestry.valid.ValidatorException),且把抛出的异常内容让delegate做了保存,
以上是在request cycle期间的rewinding阶段最先阶段(从request中读取值),这个值与逻辑进行验证,是绘制 component之前所要做的,可以看出它没有写出任何html代码,只把逻辑错误保存起来了。
以下是在rewinding textarea元素(组件) 要做的,就好象第一次读取该页面,没有对页面做任务操作:
一个delegate.writePrefix() 方法,可惜我没有看到代码实现这个方法,估计要自己写的,它是个空方法,本来的话,我想是应该想把错误的信息用这个方法显示
出来的,不过这样对整个页面布局不利,这样会造成一个component上面就会显示验证错误,还是把全部component的信息集中在一个地方显示比较好(???)
markupWrite.begin("textarea");markupWrite.attribute("name", name);....; markupWrite.end();再接着是
delegate.writeSuffix()方法,这个和delegate.writePrefix()相配合的,我看源程序中是空方法,是给留给人写的吧! 至于错误到底如何显示,可参看Tapestry的Delegator组件,其实也就是改组件的
绘制(render)的过程。假如这个具体的组件textarea,绘制的时候如给客户口端一段javascript代码,则需要script.execute()方法,script则是tapestry的javascript的xml模板得来。
textarea元素是嵌在body元素中的,script.execute(),我把它理解成为把javascript代码交给body来处理, 而body可以把javascript代码绘制给客户端。body放置了全部元素(组件)
的javascript代码,所以,每个有Iscript的地方都要script.excute()一下,把代码都放在body里面,这样body就 wrap了全部的javascript代码。
========================================
对于以上全部内容,有点糊涂了,呵呵,形象一点的话:
========
对于rewinding时
[ body ]--->renderComponent( ) {
rendBody() ----->
|
|
[component 1] -----> markupWriter1.begin("");
markupWriter1.attribute("name", name);
renderInformalParameters(writer, cycle);
markupWriter1.println();
markupWriter1.printRaw();
markupWriter1.end();
(component自带javascript代码,客户端验证)
Map symbols = new HashMap();
symbols.put("validTextArea", this);
symbols.put("maxlength", getMaxlength());
script1.execute(cycle, body, symbols);
[component 2] -----> markupWriter2.begin("");
markupWriter2.attribute("name", name);
renderInformalParameters(writer, cycle);
markupWriter2.println();
markupWriter2.printRaw();
markupWriter2.end();
(component自带javascript代码,客户端验证)
Map symbols = new HashMap();
symbols.put("validTextArea", this);
symbols.put("maxlength", getMaxlength());
script2.execute(cycle, body, symbols);
.....
writeScript() ----->
|
|
.....
}
注1:假如是render,则用java 的回调函数机制,实现内容的变化后再完成绘制;
注2:script.execute( cycle, processor, symbols)方法到底做了些什么?生产script.execute() 生成了一个新的对象
scriptSession,该对象为script.execute()调用org.apache.tapestry.script.AbstractToken 中的writeChildren()方法而服务。AbstractToken好比一个顶层容器,里面可以包含各个组件的script,每个组件的script都会有一个token,顶层容器就按着这个token进行writeChildren()一个一个的token.write()出来。所以真正在绘制 javascript就是这个token.write()了。token.write()写了些什么?
找不到了,我只好猜了,该方法有个stringBuffer参数,我估计,这个token.write(), 是按照dtd规则把javascript从其模板中读出边成string后,存放在stringBuffer中,这个stringBuffer在传递给body.writeScript(),绘制到整张页面中去。
=================
对于在做验证时:
[body]--->renderComponent( )
|
|
[component 1] -----> delegate1.writePrefix();
markupWriter1.begin("");
markupWriter1.attribute("name", name);
renderInformalParameters(writer, cycle);
markupWriter1.println();
markupWriter1.printRaw();
markupWriter1.end();
delegate1.writeSuffix( );
|
|
------------------------------------
| |
(让validator带javascript代码实现验证) (自带javascript代码,客户端验证)
IValidator validator = getValidator();
//此方法把javascript导入 body Map symbols = new HashMap();
validator.renderValidatorContribution(); symbols.put("validTextArea", this);
symbols.put("maxlength", getMaxlength());
script1.execute(cycle, body, symbols);
[component 2] -----> delegate2.writePrefix();
markupWriter2.begin("");
markupWriter2.attribute("name", name);
markupWriter2.println();
markupWriter2.printRaw();
renderInformalParameters(writer, cycle);
beforeCloseTag(writer, cycle);
markupWriter2.closeTag();
markupWriter2.end();
delegate2.writeSuffix();
|
|
------------------------------------
| |
(让validator带javascript代码实现验证) (自带javascript代码,客户端验证)
IValidator validator2= getValidator();
//此方法把javascript导入 body Map symbols = new HashMap();
validator2.renderValidatorContribution(); symbols.put("validTextArea", this);
symbols.put("maxlength", getMaxlength());
script2.execute(cycle, body, symbols);
注:validator.renderValidatorContribution()方法中也实现 script.execute( cycle, processor, symbols);
validField.beforeCloseTag( )调用validator.renderValidatorContribution()方法, ValidField 是 tapestry给
的用来验证的,我们自己设计的compoment假如要实现验证,最好继承于
=============================================================
不管自带还是tapestry带的 javascript 都是得人写的,当你自己设计一个组件的时
候,你自己写的javascript就变成 tapestry的了,别人可以用你设计的组件,那使用者
就不用写 javascript了。
================
最后小结:绘制页面,主要是由Body组件来完成了, 而body里的各组件,他们主要完成2件事:
第一:绘制出各自组件的 html ,html里需要的信息,取之于html的xml模板;
第二:从xml的javascript模板上,读取该组件上的javascript(假如有)代码,把它保存起来。
以上2个之间关联部分,比若,js代码需要用到html的某些内容, 就在两者之间,通过
symbols 架起桥梁。
Body的功能则为:
组合各组件绘制成的html, 取到各组件的 javascript代码(分为bodyscript和
initializationscript,分类绘制他们),然后绘制出一张真正的 html页面。
===============
tapestry 构架 web 的思想还是蛮先进,可惜,对除学者来说,确实有点难于上手的了,比若,虽然明白了
页面绘制的功能,自己亲手设计一个元素组件的话,在没有例子的情况,也确实很难一下子写出来的。现在
步入了web 2.0,如果要异步传输的话,绘制页面的地方都得动些手脚,在render的时候不用整张页面代码
response给client了,只需提交变动量的值,放在xml中,传给client,再由client的xmlHttpRequest的回调
函数实现页面的render绘制。好象tapestry 4.1就可以实现ajax吧,以上是我想的,自己没有看过,仅当是
假设。