Tapestry4学习指引(二)

十二. 自定义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文件;

你可能感兴趣的:(apache,eclipse,Web,JUnit,tapestry)