Tapestry 3.0 绘制页面 简单分析

来源:互联网 发布:python html网页解析 编辑:程序博客网 时间:2024/06/06 09:26

=========================
 无验证 绘制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吧,以上是我想的,自己没有看过,仅当是

假设。