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() {...}
當有多個方法知足條件時,按照下面的順序執行:
若是想要阻止其繼續尋找合適的方法的話,可讓方法返回一個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.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這個註解來幫忙。這個註解放到哪個方法上面,執行哪個方法的時候事務就會被提交。