Apache Tapestry 组件


如前所述,组件和页面是相同的,只是页面是根组件并包含一个或多个子组件。组件始终驻留在页面内,并执行页面的几乎所有动态功能。

Tapestry 组件呈现一个简单的 HTML 链接到复杂的网格功能 interactive AJAX .一个组件也可以包含另一个组件。挂毯组件由以下几项组成:

  • 组件类 :组件的主要Java类。

  • XML 模板 : XML模板类似于Page模板。组件类将模板呈现为最终输出。某些组件可能没有模板。在这种情况下,输出将由组件类本身使用 标记编写器 class.

  • Body : 页面模板内指定的组件可能有自定义标记,称为“组件体”。如果组件模板有 <正文 /> 元素,那么 元素将被组件的主体替换。这类似于前面在 XML 模板部分中讨论的布局。

  • 渲染 : 渲染是将组件的XML模板和主体转化为组件实际输出的过程。

  • 参数 : 用于创建组件和页面之间的通信,从而在它们之间传递数据。

  • Events : 将组件的功能委托给其容器/父级(页面或其他组件)。它广泛用于页面导航目的。

渲染


组件的渲染是在一系列预定义的阶段中完成的。组件系统中的每个阶段都应该有组件类中约定或注释定义的对应方法。

// 使用注解
@SetupRender 
void initializeValues() { 
    // 初始化值
}

// 使用约定
boolean afterRender() { 
    // 做逻辑
    return true;
}

下面列出了阶段、方法名称和注释。

注解 默认方法名称
@SetupRender 设置渲染()
@BeginRender 开始渲染()
@BeforeRenderTemplate beforeRenderTemplate()
@BeforeRenderBody beforeRenderBody()
@AfterRenderBody afterRenderBody()
@AfterRenderTemplate afterRenderTemplate()
@AfterRender 渲染后()
@CleanupRender 清理渲染()

每个阶段都有一个特定的目的,它们如下:

设置渲染

SetupRender 启动渲染过程。它通常设置组件的参数。

开始渲染

BeginRender 开始渲染组件。它通常呈现组件的开始/开始标签。

渲染前模板

BeforeRenderTemplate 用于装饰 XML 模板,在模板周围添加特殊标记。它还提供了跳过模板渲染的选项。

渲染前体

BeforeRenderTemplate 提供了一个选项来跳过组件的 body 元素的渲染。

AfterRenderBody

AfterRenderBody 将在组件的主体被渲染后被调用。

渲染后模板

AfterRenderTemplate 将在组件的模板渲染后被调用。

渲染后

AfterRender 是 BeginRender 的对应物,通常呈现关闭标记。

清理渲染

CleanupRender 是 SetupRender 的对应物。它释放/处置在渲染过程中创建的所有对象。

渲染阶段的流程不仅是向前的。它根据阶段的返回值在阶段之间来回切换。

例如,如果 SetupRender 方法返回 false,则渲染会跳转到 CleanupRender 阶段,反之亦然。要清楚地了解不同阶段之间的流程,请检查下图中的流程。

Annotation List

简单组件

让我们创建一个简单的组件 Hello,它将输出消息为“Hello, Tapestry”。以下是 Hello 组件及其模板的代码。

package com.example.MyFirstApplication.components;  
public class Hello {  
}
<html  
    xmlns:t = "https:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
  
    <div>
        <p>Hello, Tapestry (from component).</p>
    </div>
  
</html>

Hello 组件可以在页面模板中调用如下:

<html title = "Hello component test page" 
    xmlns:t = "https:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
<t:hello />  
</html>

类似地,组件可以使用 MarkupWriter 而不是模板呈现相同的输出,如下所示。

package com.example.MyFirstApplication.components; 
  
import org.apache.tapestry5.MarkupWriter; 
import org.apache.tapestry5.annotations.BeginRender;   

public class Hello { 
    @BeginRender
    void renderMessage(MarkupWriter writer) {
        writer.write("<p>Hello, Tapestry (from component)</p>");
    }
}

让我们更改组件模板并包含 元素,如下面的代码块所示。

<html>  
    xmlns:t = "https:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
   
    <div>
        <t:body />
    </div>
</html>

现在,页面模板可能会在组件标记中包含正文,如下所示。

<html title = "Hello component test page" 
    xmlns:t = "https:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
   
    <t:hello>
        <p>Hello, Tapestry (from page).</p>
    </t:hello>
</html>

输出如下:

<html> 
    <div>
        <p>Hello, Tapestry (from page).</p>
    </div>
</html>

参数


这些参数的主要目的是在组件的字段和页面的属性/资源之间创建连接。使用参数,组件和它对应的页面相互通信和传输数据。这就是所谓的 双向数据绑定 .

例如,在用户管理页面中用于表示年龄的文本框组件通过参数获取其初始值(在数据库中可用)。同样,在用户年龄更新并提交回来后,组件将通过相同的参数发回更新的年龄。

要在组件类中创建新参数,请声明一个字段并指定一个 @范围 注解。这个@Parameter 有两个可选参数,分别是:

  • required :使参数为必填项。如果未提供 Tapestry,则会引发异常。

  • value :指定参数的默认值。

该参数应在页面模板中指定为组件标记的属性。属性的值应该使用我们在前面章节中讨论过的绑定表达式/扩展来指定。我们之前学到的一些扩展是:

  • 属性扩展 (prop:«val») : 从页面类的属性中获取数据。

  • 消息扩展(消息:«val») : 从 index.properties 文件中定义的 key 中获取数据。

  • 上下文扩展(上下文:«val») : 从 web context 文件夹 /src/main/webapp 中获取数据。

  • 资产扩张(资产:«val») :从jar文件/META-INF/assets中嵌入的资源中获取数据。

  • 符号扩展(符号:«val») : 从 AppModule.java 文件中定义的符号中获取数据。

Tapestry 还有很多有用的扩展,下面给出其中一些:

  • 文字扩展(文字:«val») : 文字串。

  • Var 扩展 (var:«val») :允许读取或更新组件的某个渲染变量。

  • 验证扩展(验证:«val») : 一个专门的字符串,用来指定一个对象的验证规则。例如,验证:必需,minLength = 5。

  • 翻译(翻译:«val») : 用于在输入验证中指定Translator类(将客户端转换为服务器端表示)。

  • 块(块:«val») :模板内块元素的id。

  • 组件(组件:«val») :模板内另一个组件的id。

上述所有扩展都是只读的,除了 Property 扩展和 Var 扩展。组件使用它们与页面交换数据。当使用扩展作为属性值时, ${...} 不应该使用。相反,只需使用没有美元和大括号符号的扩展。

使用参数的组件

让我们通过修改 Hello 组件来创建一个新组件 HelloWithParameter 以通过添加一个 name 组件类中的参数并相应地更改组件模板和页面模板。

  • 创建一个新的组件类 HelloWithParameter.java .

  • 添加一个私有字段并将其命名为 @范围 注解。使用 required 参数使其成为强制性的。

@Parameter(required = true) 
private String name;
  • 添加一个私有字段,结果为 @Propery 注解。 result 属性将在组件模板中使用。组件模板无权访问带有注释的字段 @范围 并且只能访问带有注释的字段 @财产 .组件模板中可用的变量称为渲染变量。

@Property 
 private String result;
  • 添加一个 RenderBody 方法并将值从 name 参数复制到 result 属性。

@BeginRender 
void initializeValues() { 
    result = name;
}
  • 添加新的组件模板 HelloWithParamter.tml 并使用 result 属性来呈现消息。

<div> Hello, ${result} </div>
  • 在测试页面 (testhello.java) 中添加一个新属性 Username。

public String getUsername() { 
    return "User1";
}
  • 在页面模板中使用新创建的组件,并在 name 参数中设置 Username 属性 HelloWithParameter 零件。

<t:helloWithParameter name = "username" /> 

完整清单如下:

package com.example.MyFirstApplication.components;  

import org.apache.tapestry5.annotations.*;  
public class HelloWithParameter { 
    @Parameter(required = true)
    private String name;
     
    @Property
    private String result;
   
    @BeginRender
    void initializeValues() {
        result = name;
    }
}
<html  
    xmlns:t = "https:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
   
    <div> Hello, ${result} </div>
  
</html>
package com.example.MyFirstApplication.pages;  

import org.apache.tapestry5.annotations.*;  
public class TestHello { 
    public String getUsername() {
        return "User1";
    }
}
<html title = "Hello component test page" 
    xmlns:t = "https:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
    <t:helloWithParameter name = "username" />
   
</html> 

结果如下:

<div> Hello, User1 </div>

高级参数

在前面的章节中,我们分析了如何在自定义组件中创建和使用简单的参数。高级参数也可能包含完整的标记。在这种情况下,标记应在组件标记内指定,例如页面模板中的子部分。内置的 if 组件具有成功和失败条件的标记。成功的标记被指定为组件标签的主体,失败的标记使用 其他参数 .

让我们看看如何使用 if 零件。 if 组件有两个参数:

  • test : 简单的基于属性的参数。

  • Else : 高级参数,用于指定替代标记,如果条件失败

Tapestry 将使用以下逻辑检查 test 属性的值并返回 true 或 false。这就是所谓的 类型强制 ,一种将一种类型的对象转换为具有相同内容的另一种类型的方法。

  • 如果数据类型是 String , 如果非空白且不是文字字符串“False”(不区分大小写),则为“True”。

  • 如果数据类型是 Number , 如果非零则为真。

  • 如果数据类型是 收藏 , 如果非空则为真。

  • 如果数据类型是 Object , 真(只要不为空)。

如果条件通过,则组件渲染其主体;否则,它会呈现 else 参数的主体。

完整清单如下:

package com.example.MyFirstApplication.pages; 
public class TestIf { 
    public String getUser() {
        return "User1";
    }
}

<html title = "If Test Page" 
    xmlns:t = "http:// Tapestry.apache.org/schema/tapestry_5_4.xsd"
    xmlns:p = "tapestry:parameter">
   
    <body>
        <h1>Welcome!</h1>
        <t:if test = "user">
            Welcome back, ${user}
            <p:else>
                Please <t:pagelink page = "login">Login</t:pagelink>
            </p:else>
        </t:if>
    </body>
   
</html>

组件事件/页面导航


Tapestry 应用程序是一个 页面集合 interacting with each other. Till now, we have learned how to create individual pages without any communication between them. A Component event's primary purpose is to provide interaction between pages (within pages as well) using server-side events. Most of the component events originate from client-side events.

例如,当用户单击页面中的链接时,Tapestry 将使用目标信息调用同一页面本身,而不是调用目标页面并引发服务器端事件。 Tapestry 页面将捕获事件,处理目标信息并执行服务器端重定向到目标页面。

挂毯遵循 发布/重定向/获取 (RPG) 设计模式 用于页面导航。在 RPG 中,当用户通过提交表单进行发布请求时,服务器会处理发布的数据,但不会直接返回响应。相反,它将执行客户端重定向到另一个页面,该页面将输出结果。 RPG 模式用于防止通过浏览器返回按钮、浏览器刷新按钮等重复提交表单,Tapestry 通过提供以下两种类型的请求来提供 RPG 模式。

  • 组件事件请求 : 此类请求针对页面中的特定组件,并在该组件内引发事件。该请求仅进行重定向,不输出响应。

  • 渲染请求 :这些类型的请求以页面为目标,并将响应流回客户端。

要了解组件事件和页面导航,我们需要知道 Tapestry 请求的 URL 模式。两种请求的 URL 模式如下:

  • 组件事件请求

/<<page_name_with_path>>.<<component_id|event_id>>/<<context_information>>
  • 渲染请求

/<<page_name_with_path>>/<<context_information>>

URL模式的一些例子是:

  • 可以通过以下方式请求索引页面 https://«域名»/«应用»/索引 .

  • 如果索引页面在子文件夹管理员下可用,则可以通过以下方式请求 https://«域名»/«应用程序»/admin/index .

  • 如果用户点击 动作链接组件 with id test 在索引页面中,则 URL 将是 https://«domain»/«app»/index.test .

Events


默认情况下,Tapestry 会引发 钝化 and 激活 所有请求的事件。对于组件事件请求类型,tapestry 会根据组件引发额外的一个或多个事件。 ActionLink 组件引发 Action 事件,而 Form 组件引发多个事件,例如 验证,成功 , etc.,

可以使用相应的方法处理程序在页面类中处理事件。方法处理程序是通过方法命名约定或通过 @OnEvent 注解。方法命名约定的格式是 On«EventName»From«ComponentId» .

ActionLink 组件的一个动作事件 id test 可通过以下任一方法处理:

void OnActionFromTest() { 
}  
@OnEvent(component = "test", name = "action") 
void CustomFunctionName() { 
} 

如果方法名称没有任何特定的组件,则将为所有具有匹配事件的组件调用该方法。

void OnAction() { 
} 

OnPassivate 和 OnActivate 事件

OnPassivate 用于为 OnActivate 事件处理程序提供上下文信息。一般来说,Tapestry 提供上下文信息,它可以用作 OnActivateevent 处理程序中的参数。

例如,如果上下文信息是int类型的3,那么OnActivate事件可以调用为:

void OnActivate(int id) { 
} 

在某些情况下,上下文信息可能不可用。在这种情况下,我们可以通过 OnPassivate 事件处理程序向 OnActivate 事件处理程序提供上下文信息。 OnPassivate 事件处理程序的返回类型应用作 OnActivate 事件处理程序的参数。

int OnPassivate() { 
    int id = 3;
    return id;
} 
void OnActivate(int id) { 
} 

事件处理程序返回值

Tapestry 根据事件处理程序的返回值发出页面重定向。事件处理程序应返回以下任一值。

  • 空响应 :返回空值。 Tapestry 将构建当前页面 URL 并作为重定向发送到客户端。

public Object onAction() { 
    return null;
}
  • String Response :返回字符串值。 Tapestry 将构造与该值匹配的页面的 URL,并作为重定向发送给客户端。

public String onAction() { 
    return "Index";
}
  • 类响应 :返回一个页面类。 Tapestry 将构造返回页面类的 URL 并作为重定向发送给客户端。

public Object onAction() { 
    return Index.class
}
  • 页面响应 :返回一个带有@InjectPage注解的字段。 Tapestry 将构造注入页面的 URL 并作为重定向发送给客户端。

@InjectPage 
private Index index;  

public Object onAction(){ 
    return index;
}
  • HttpError :返回HTTPError对象。 Tapestry 将发出客户端 HTTP 错误。

public Object onAction(){ 
    return new HttpError(302, "The Error message);
}
  • 链接响应 :直接返回一个链接实例。 Tapestry 将从 Link 对象构造 URL 并作为重定向发送到客户端。

  • 流响应 : 返回 流响应 目的。 Tapestry 会将流作为响应直接发送到客户端浏览器。它用于直接生成报告和图像并将其发送给客户端。

  • 网址响应 : 返回 java.net.URL 目的。 Tapestry 将从对象中获取相应的 URL 并作为重定向发送给客户端。

  • Object Response :返回上述指定值以外的任意值。 Tapestry 会报错。

事件上下文

通常,事件处理程序可以使用参数获取上下文信息。例如,如果上下文信息是 int 类型的 3,那么事件处理程序将是:

Object onActionFromTest(int id) {  
} 

Tapestry 正确处理上下文信息并通过参数将其提供给方法。有时,由于编程的复杂性,Tapestry 可能无法正确处理它。届时,我们可能会得到完整的上下文信息并自行处理。

Object onActionFromEdit(EventContext context) { 
    if (context.getCount() > 0) {
        this.selectedId = context.get(0);
    } else {
        alertManager.warn("Please select a document.");
        return null;
    }
}