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这个注解来帮忙。这个注解放到哪个方法上面,执行哪个方法的时候事务就会被提交。