Showing posts with label Component. Show all posts
Showing posts with label Component. Show all posts

Monday, October 27, 2008

Ajax in Tapestry5

在Tapestry5中使用一些简单的ajax还是比较方便的,ajax返回结果可以是JSONObject/JSONArray/Component/Block/String/ResponseStream等,可设置zone组件中的show/update这二个自定义的javascript方法实现复杂效果。

返回的Block和Component是以JSON对象形式返回,其中key必须为"content",value即为要渲染的内容,这是用Zone组件更新页面的最基本用法。也可以将ResponseStream以"application/json"形式返回客户端,见下面例子。

Component是指org.apache.tapestry5.runtime.Component对象,可以通过ComponentSource.getPage()或者ComponentSource.getComponent()方法获取。
具体使用方法见以下例子:

import java.util.Date;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.StreamResponse;
import org.apache.tapestry5.annotations.BeginRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.BeanDisplay;
import org.apache.tapestry5.internal.services.ClientBehaviorSupport;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.services.ComponentSource;
import org.apache.tapestry5.util.TextStreamResponse;

public class Test {

@Environmental
private ClientBehaviorSupport clientBehaviorSupport;

@Inject
@Property
private Block block;

@Inject
private ComponentSource componentSource;

@InjectComponent
private BeanDisplay beanDisplay;

@BeginRender
void begin() {
// clientBehaviorSupport.addZone("link1", "show", "update");
clientBehaviorSupport.linkZone("link1", "zone1");
}

StreamResponse onActionFromLink1() {
StreamResponse response = new TextStreamResponse("application/json", "{\"content\":\"更新zone1中原来的内容\"}");
return response;
}

JSONObject onActionFromLink2() {
return new JSONObject().put("content", "update zone2.");
}

Block onActionFromLink3() {
return block;
}

BeanDisplay onActionFromLink4() {
// return a component as ajax result.
return beanDisplay;
}

// JSONArray onActionFromLink2() {
// return new JSONArray("[{user: 'test', firstName: 'yu'}]");
// }

// Component onActionFromLink4() {
// return componentSource.getPage("Start");
// }

public Date getStr() {
return new Date();
}

}
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head>
<title>ajax in tapestry5</title>
</head>
<body>
<div style="margin-left: 50px">
zone1:<t:zone t:id="zone1">ajax</t:zone>
<t:actionLink id="link1" t:id="link1">用ajax返回ResponseStream更新zone1,此链接通过ClientBehaviorSupport关联到zone1</t:actionLink>
<br />
zone2:<t:zone t:id="zone2">ajax</t:zone>
<t:actionLink t:id="link2" t:zone="zone2">update zone2 using json Object</t:actionLink>
<br />
zone3:<t:block t:id="block">${str}</t:block>
<t:zone t:id="zone3">
<t:delegate to="block"/>
</t:zone>
<a t:type="actionlink" t:id="link3" href="#" t:zone="zone3">update zone3 using Block</a>
<br />
<t:block><t:beanDisplay t:object="this"/></t:block>
zone4:<t:zone t:id="zone4">ajax</t:zone>
<t:actionLink t:id="link4" t:zone="zone4">update zone4 using Component</t:actionLink>
</div>
</body>
</html>

Sunday, October 26, 2008

RenderSupport Usage in Tapestry5

RenderSupport是在写组件中非常有用的一个service,主要用于提供对组件渲染的支持,可以用其导入css/javascript文件,最重要的一个功能是其可以为组件生成客户端HTML标签的唯一id。
要生成此客户端的id,可以用二个方法产生,一是RenderSupport.allocateClientId(String id),由自己指定一个id字符串,如果一个页面中包含多个相同组件,则第二个之后的组件会在此id后加上"_"和0开始的数字做为其id。
另外是用RenderSupport.allocateClientId(ComponentResources resources),resources会抽取其组件的id做为其客户端的id值。
举例写一个checkBoxes组件,需要提一点是不要分配"checkbox"做为组件的id,这个会导致server崩掉,Tapestry5的一个bug,还没有解决。

public class CheckBoxes {

@Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String value;

@Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String name;

@Parameter
private boolean checked;

@Inject
private Request request;

@Inject
private ComponentResources resources;

@Inject
private RenderSupport renderSupport;

@BeginRender
void begin(MarkupWriter writer) {
String id = renderSupport.allocateClientId("chk"); // don't use checkbox.
System.out.println(id);
Element checkbox = writer.element("input", "type", "checkbox", "name", name, "id", id, "value", value);
checkbox.attribute("checked", checked ? "checked" : null);
}

@AfterRender
void after(MarkupWriter writer) {
writer.end();
}

}

Explain onActivate/onPassivate of Tapestry5

用Tapestry5开发页面过程中,最常用到的二个页面方法(事件处理器)就是onActivate()和onPassivate()。
onActivate()方法是在此页面被激活后调用的,可以用此方法接收传给页面的参数(用官方的说法是以Array/List形式返回页面的activation context),此方法可以通过重载来接收数量不同的参数,并且其中参数少于等于URL InternalConstants.PAGE_CONTEXT_NAME传进来的参数数量的onActivate()方法都会被调用一次,其中无参的onActivate()方法肯定会被调用,并且是最后被调用,如后面例子所示。

注意,如果在Tapestry5中集成了tapestry-hibernate包,并且在passivate context中返回的是Hibernate实体的情况下,则可以在onActivate()中直接接收这些Hibernate实体对象,Tapestry会在passivate时自动从这些实体对象中提取id到页面URL ,并且在activate时自动转回Hibernate实体对象。Tapestry会在后台去查询数据库,如果页面很简单可以这么用,复杂页面不建议这么做。

onPassivate()方法是在页面渲染中,由LinkFactory生成每个actionLink或者返回自身页的pageLink时都会触发此事件,因此这个passivate事件在一个页面渲染中可能会重复调用多次。会将此方法返回的结果被encode后赋给InternalConstants.PAGE_CONTEXT_NAME,固化到所有返回自身页面的URL后面。对于actionLink和pageLink稍有不同的是,actionLink不管事件处理后重定向去哪,URL后都会跟上此参数,因其事件触发后先返回自身页面,而pageLink如果是指向别的页面,则不会触发此事件,URL后也不会带上此参数,如果需要传参给其他页面则通过pageLink的context参数指明参数值,如果pageLink是返回自身页面,则跟actionLink一样会在URL上直接跟上onPassivate返回的值,这样当前页面的一些变量状态就可以不用@Persist,而用onPassivate/onActivate进行传递。

关于onActivate/onPassivate更详细的内容可从以下接口和类中了解:ComponentResources/ComponentEventRequestHandler/ComponentEventRequestFilter/ComponentEventRequestHandler/ComponentEventRequestParameters/ComponentEventDispatcher/EventConstants/InternalConstants/LinkFactory/LinkFactoryImpl/PageLink/EventLink/ActionLink等。

其中ComponentEventRequestHandler可处理的事件请求形式有:

* /context/pagename:eventname -- event on the page, no action context
* /context/pagename:eventname/foo/bar -- event on the page with action context "foo", "bar"
* /context/pagename.foo.bar -- event on component foo.bar within the page, default event, no action context
* /context/pagename.foo.bar/baz.gnu -- event on component foo.bar within the page, default event, with action context "baz", "gnu"
* /context/pagename.bar.baz:eventname/foo/gnu -- event on component bar.baz within the page with action context "foo" , "gnu"


关于onActivate/onPassivate的一个测试例子:
java:
public class Test {

void onActivate() {
System.out.println("must be revoked when page be requested");
}

void onActivate(int id) {
System.out.println(id);
}

void onActivate(int id1, int id2) {
System.out.println(id1);
System.out.println(id2);
}

int onPassivate() {
System.out.println("passivate context");
return 1;
}

void onActionFromLink() {
}

void onRedirect() {
}
}
tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<a href="/Test">Test</a>
<br />
<t:actionLink t:id="link">ActionLink</t:actionLink>
<br />
<t:eventLink t:event="redirect" t:context="literal:1">EventLink1</t:eventLink>
<br />
<t:eventLink t:event="redirect">EventLink2</t:eventLink>
<br />
<t:pageLink t:page="Test">Self page Link</t:pageLink>
<br />
<t:pageLink t:page="Start">Another page Link</t:pageLink>
</html>
其中onPassivate()方法会被调用4次。
Reference:
http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/internal/services/ComponentEventDispatcher.html

Friday, July 04, 2008

Difference between Annotation @InjectComponent and @Component in Tapestry5

@InjectComponent 和 @Component 需要注意二者区别。@InjectComponent这个注释是指在page中注入此组件,其中说明里的readonly是指注入的组件对象是只读注入的,不能用new来构建或者将其置为null,但是对象内部的状态可以通过在page中调用此对象本身的方法加以改变。
@Component 注释是在component中注入内置组件component,如常用到的Form组件。