Tapestry-Again

一旦你已經開始使用Tapestry了,那麼你就走上了一條不歸路。。。

Once you work in Tapestry there's no going back!css

 

Page或者Component裏面的元素都是private類型的。html

 

@InjectPage,Tapestry是一個被管理的環境,咱們不直接去建立一個對象,而是讓Tapestry去管理全部對象的生命週期。若是咱們須要一個Page對象,那麼只須要用@InjectPage這個註解。java

 

When creating your own applications, make sure that the objects stored in final variables are thread safe.jquery

當建立咱們的應用時,確保咱們聲明的final類型的變量是線程安全的。ajax

 

Tapestry uses an approach based on the Post/Redirect/Get pattern.apache

Tapestry使用的是一個基於POST/REDIRECT/GET的方式來獲取服務器信息的。安全

 

<t:actionLink t:id="makeGuess" context="1">1</t:actionLink> -> <a href="/bootcss/guess.actionlink/1">1</a>服務器

<t:actionLink id="makeGuess" context="1">1</t:actionLink> -> <a id="makeGuess" href="/bootcss/guess.actionlink/1">1</a>session

 

@Persistapp

Tapestry的持久化默認是session級別的。

Flash級別的話,只會保持一個請求,下一次請求發出的時候就會消失。

 

<img src="${request.contextPath}/images/catalog/product_${productId}.png"/>

 

犯了一個超級2的毛病,

<t:loop source="1..10" value="var:index">${var:index}</t:loop>

這種寫法能夠說是沒問題的,小猿想不明白了就,爲啥他就可以循環着把1到10打印出來呢,渲染的過程就是如何把控件中的值寫成html的過程。

當afterrender返回true的時候可能就完成渲染了,若是返回的是false的話會繼續渲染。

下圖中的各個階段,據官方介紹,SetupRender, BeginRender, AfterRender, CleanupRender是常常被使用的。其它的貌似是給mixin專門設計的。

@SetupRender

This is a good place to read component parameters and use them to set temporary instance variables.

這是一個讀取組件參數,而且設定臨時變量的好地方。

 

 

The general rule is, only use the ${...} syntax in non-Tapestry-controlled locations in your template, such as in attributes of ordinary HTML elements and in plain-text areas of your template.

不要隨意地使用${...}語法,基本準則是咱們僅僅在非tapestry控制的地方使用,例如在標準的HTML元素的屬性處,或者是一個基本的描述處。。。

<t:textfield t:id="color" value="${color}"/> => <t:textfield t:id="color" value="color"/>

<img src="context:images/banner.png"/> => <img src="${context:images/banner.png}"/>

 

看以下的tml文件,其中有一個<html>標籤,而且有一個t:type,其實這個<html>標籤是必須的,在Tapestry渲染的時候,會把頁面中的<h1>和<p>放到layout.tml的<t:body />處,而後把<html>標籤去掉。

<html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <h1>Welcome to the Nifty Web Application!</h1>
   <p>
        Would you like to <t:pagelink page="login">Log In</t:pagelink>?
   </p>
</html>

 

Page Render Requests

1 Pages may have an activation context. The activation context represents persistent information about the state of the page.

頁面能夠有些激活內容(表現爲URL上的參數)。它是用來顯示頁面內容的一個決定因素,例如ID什麼的。

2 When no explicit activation context is provided, the page itself is queried for its activation context.

然而並非全部的頁面都有參數,若是沒有提供參數的時候,頁面就會本身去問(請求)這個參數。

The event name is "passivate" . The return value of the method is used as the context. 

這個請求觸發的方法就是onPassivate(),他會返回一個參數,這個參數就會被當作頁面請求的參數。

 

1 一個頁面中若是存在一個pagelink的話,若是這個pagelink沒有參數(context)的話,那麼這個pagelink的page所指定的頁面java類的onPassivate就會被觸發,以獲取相應的context;

2 若是這個頁面的pagelink存在參數的話,那麼就不會觸發java類的onPassivate方法。

3 進入到一個頁面的時候,首先會觸發它的onActivate方法,@SetupRender以後,@AfterRender以前,觸發onPassivate方法

 

下面的這個tml代碼:

<t:ActionLink context="${literal:actionLink}">ActionLink Test</t:ActionLink>
被render以後造成:
<a href="/bootcss/action/index.actionlink/actionLink">ActionLink Test</a>

<t:EventLink context="${literal:eventLink}" event="testEvent">EventLink Test</t:EventLink>
被render以後造成:
<a href="/bootcss/action/index:testevent/eventLink">EventLink Test</a>

 

@OnEvent註解的使用

1 Form或者ActionLink的時候,

@OnEvent(value="action", component="componentId")

其中value的值固定,EventConstants.ACTION/action,component的值是actionLink的t:id指定的值。而且,t:ActionLink的id不要指定。

INSTEAD: void onActionFromComponentId() {...}

 

2 EventLink的時候,

@OnEvent的value的值是eventLink的event指定的值。component的值不用指定也能夠。

INSTEAD: void onEventName() {...}

當有多個方法知足條件時,按照下面的順序執行:

  • Base class methods before sub-class methods.
  • 基類早於子類
  • Matching methods within a class in alphabetical order.
  • 一個類中有多個方法知足時,按照字母順序執行
  • For a single method name with multiple overrides, by number of parameters, descending.
  • 若是一個方法被重載了,按照參數個數的降序執行。

若是想要阻止其繼續尋找合適的方法的話,可讓方法返回一個true。

 

Tapestry持久化

針對於單個page而言

Tapestry有一個@Persist註解,它有三種類型,Session,Flash,Client(不推薦)。

Session,默認級別,通過屢次請求,它會保存一個頁面上的數據。

Flash,(PersistenceConstants.FLASH),信息仍然是儲存在session裏面,可是它不會被保存太長時間,一旦它在頁面上顯示過一次了就會被從session中刪除。

Tapestry的請求時Post-Redirect-Get類型的,應該說它會在一個請求完成以後就刪除。

凡是被@Persist標註的變量是不容許設定默認值的,不管直接賦值或者在構造器中設定值。

 

針對多個multi-page而言

Session State Objects for complex objects, and Session Attributes for simple types.

Session State Object(SSO),是一個被@SessionState註解標記的變量,這個變量儲存的內容被一個用戶在全部的page中使用,別的用戶不能夠。

若是一個頁面中定義了一個@SessionState類型的變量,那麼在別的頁面中定義這麼一個類型相同的,一樣被@SessionState標準的變量,那麼這兩個變量指向的就是同一個變量。

注意點: 不要把@SessionState用到一個簡單類型上。由於Tapestry在@SessionState這個事兒上是認類型不認名字的,可能會由於存在相同類型的兩個變量而把前面的變量給覆蓋掉了。

當Tapestry第一次發現一個SSO的時候,就會自動的建立這個對象。因此這個對象須要有一個無參構造器。

若是給一個SSO設定了null的話,這個SSO就會被銷燬。

@SessionState(create=false)註解決定這個變量不會被自動建立。

    public void contributeApplicationStateManager(MappedConfiguration<Class, ApplicationStateContribution> configuration)
    {
        ApplicationStateCreator<ShoppingCart> creator = new ApplicationStateCreator<ShoppingCart>()
        {
            public ShoppingCart create()
            {
                return new ShoppingCart(new Date());
            }
        };

        configuration.add(ShoppingCart.class, new ApplicationStateContribution("session", creator));
    }

Session Attribute, 是一個被@SessionAttribute標記的變量,他一樣是被全部的頁面共享的一個seesion級別的變量。

他跟SSO不一樣,並非靠類型來獲取一個對象,而是靠名稱來獲取的,這也幫助了在一個大型的項目中工程之間調用session中的內容。

如下兩個代碼是等價的:

public class Page {
    @SessionAttribute
    private User loggedInUserName;
}
public class Page {
    @SessionAttribute("loggedInUserName")
    private User userName;
}

 

在使用這個SSO或者SessionAttribute的時候,被共享的session是存在於多個module之間的。因此咱們最好定義一個與包名相關的變量,而後賦予它,以下:

public static final String USER_NAME_SESSION_ATTRIBUTE = "com.example.shoppingapp.username";
 
public class Page {
    @SessionAttribute(USER_NAME_SESSION_ATTRIBUTE)
    private User userName;
}

 

@Inject

1 Block Injection,推薦方式

@Inject
@Id("bar")
private Block barBlock;

2 Resource Injection

  • java.lang.String – The complete id of the component, which incorporates the complete class name of the containing page and the nested id of the component within the page.

java.util.Locale – The locale for the component (all components within a page use the same locale).

org.slf4j.Logger – A Logger instance configured for the component, based on the component's class name. SLF4J is a wrapper around Log4J or other logging toolkits.

org.apache.tapestry5.ComponentResources – The resources for the component, often used to generate links related to the component.

org.apache.tapestry5.ioc.Messages – The component message catalog for the component, from which localized messages can be generated.

 

3 Asset Injection

@Inject
@Path("context:images/top_banner.png")
private Asset banner;

4 Service Injection

 

 

Ajax and Zone

Zones是Tapestry用來實現刷新部分頁面的方式。Zone組件會被渲染成一個HTML元素,基本上是DIV。

Zone能夠被EventLink,ActionLink,Select,或者是一個Form來實現更新。

有點須要說明的是,若是咱們須要使用jquery,那麼咱們須要引入一個新的包Tapestry-jquery.jar,這樣咱們就不須要再引入jquery.js了

<dependencies>
...
        <dependency>
            <groupId>org.got5</groupId>
            <artifactId>tapestry5-jquery</artifactId>
            <version>3.3.7</version>
        </dependency>
</dependencies>

    <repositories>
      <repository>
          <id>devlab722-repo</id>
          <url>http://nexus.devlab722.net/nexus/content/repositories/releases
          </url>
          <snapshots>
              <enabled>false</enabled>
          </snapshots>
      </repository>
  
      <repository>
          <id>devlab722-snapshot-repo</id>
          <url>http://nexus.devlab722.net/nexus/content/repositories/snapshots
          </url>
          <releases>
              <enabled>false</enabled>
          </releases>
      </repository>
    </repositories>

 

Zone是一個被@InjectComponent註解的變量,它的名稱跟頁面中的t:zone的id相同

@Inject private Request request;是用來解析一個請求是不是ajax請求的,如:request.isXHR() ? /* YES */ someZone.getBody() : /* NO, reture page itself */ null;

t:zone標籤有一個visible屬性,用來定義是否直接顯示該組件;

有一個update屬性,用來定義更新的時候的樣式,默認爲highlight,show,slidedown,slideup,fade可選。

 

Form and Validation

@Component private Form form;

這樣引入一個form組件,這個組件的一個用途就是在上面追加錯誤信息。

如: form.recordError("Check your information buddy~");

另外還能夠指定到某一個特定的控件,這個控件也須要被引入:

@InjectComponent private TextField username;

而後,form.recordError(username, "Check your information buddy~");

 

java類中可能會有一個方法,名稱是這樣的:onValidateFromSomeForm() {...};其中somefrom指的是頁面上的form的id。

 

當完成了驗證以後,java類中的onSuccess()方法就會被觸發。。。

 

t:textfield控件,咱們須要給它指定t:id,這個t:id會生成html中input的name和id,咱們也能夠給它指定一個value,可是這沒有什麼必要,Tapestry會自動去綁定具備相同id的變量。

 

FormValidation,輸入控件能夠有一個validate屬性,這個屬性能夠指定這些值:required, min, max, minlength, maxlength, none, email, regexp,這些都是Tapestry內置的客戶端驗證。

 

修改默認的錯誤信息,只須要追加相應的properties信息便可。
app.properties中key的形式如:formid-componentid-checkname-message;
pagename.properties中key的形式如:componentid-checkname-message;

 

File upload

在一個t:form中,<t:FileUpload t:id="file" t:value="file" validate="required"/>就會造成一個文件上傳控件。

java端代碼:

    @Persist(PersistenceConstants.FLASH)
    @Property
    private String message;
    
    @Property
    private UploadedFile file;
    
    public void onSuccess() {
        File copied = new File("c:/" + file.getFileName());
        
        file.write(copied);
    }
    
    
    Object onUploadException(FileUploadException ex)
    {
        message = "Upload exception: " + ex.getMessage();
    
        return this;
    }

 

Tapestry Hibernate

Tapestry Hibernate管理的entity們,會被自動的建立一個valueEncoder。不明白encode和decode的區別。

總之Tapestry會自動的經過一個entity的id來肯定一個entity實體。而且這個id會被強轉成string類型。

因此,咱們想要根據一個entity的id獲取一個entity只須要用:

void onActivate(SomeEntity entity) {
    this.entity = entity;
}

SomeEntity onPassivate() {
    return this.entity;
}

 

然而 ,Tapestry提供了另一種方式,即註解@PageActivationContext,

/**
    Annotation for a field for which the page activation context handlers (onActivate and onPassivate) should be created.
    In order to use this annotation you must contribute a org.apache.tapestry5.ValueEncoder for the class of the annotated property.
    You should not use this annotation within a class that already has an onActivate() or onPassivate() method; doing so will result in a runtime exception.
*/

因爲ValueEncoder已經被Tapestry Hibernate建立了,因此就無需費心了^.^, 另外,這個註解是不能跟onPassivate和onAcitivate一塊兒使用的。

 

若是給一個entity追加一個@Persist("entity")註解的話,那麼這個變量就會被保存在session中,包括類型和該類型的id。

這個時候是不須要onPassivate()方法的,Tapestry會直接去session當中去獲取這個變量類型的id,onActivate()仍是須要的。

 

@CommitAfter

全部的Hibernate操做都發生在一個事務當中,只有這個request請求完成以後事務纔會提交。所以咱們須要@CommitAfter這個註解來幫忙。這個註解放到哪個方法上面,執行哪個方法的時候事務就會被提交。

本站公眾號
   歡迎關注本站公眾號,獲取更多信息