Apache Tapestry - 组件



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

Tapestry 组件可以呈现从简单的 HTML 链接到复杂的网格功能,并具有交互式 AJAX。一个组件也可以包含另一个组件。Tapestry 组件包含以下项目:

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

  • XML 模板 - XML 模板类似于页面模板。组件类将模板呈现为最终输出。有些组件可能没有模板。在这种情况下,输出将由组件类本身使用MarkupWriter类生成。

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

  • 渲染 - 渲染是一个将组件的 XML 模板和主体转换为组件实际输出的过程。

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

  • 事件 - 将功能从组件委派给其容器/父级(页面或另一个组件)。它广泛用于页面导航。

渲染

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

// Using annotaion 
@SetupRender 
void initializeValues() { 
   // initialize values 
}

// using convention 
boolean afterRender() { 
   // do logic 
   return true; 
}

阶段、其方法名称及其注解列在下面。

注解 默认方法名称
@SetupRender setupRender()
@BeginRender beginRender()
@BeforeRenderTemplate beforeRenderTemplate()
@BeforeRenderBody beforeRenderBody()
@AfterRenderBody afterRenderBody()
@AfterRenderTemplate afterRenderTemplate()
@AfterRender afterRender()
@CleanupRender cleanupRender()

每个阶段都有其特定的用途,如下所示:

SetupRender

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

BeginRender

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

BeforeRenderTemplate

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

BeforeRenderBody

BeforeRenderTemplate 提供跳过渲染组件主体元素的选项。

AfterRenderBody

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

AfterRenderTemplate

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

AfterRender

AfterRender 是 BeginRender 的对应项,通常渲染结束标签。

CleanupRender

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>"); 
   } 
}

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

<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注解。此 @Parameter 具有两个可选参数,它们是:

  • required - 将参数设为必填。如果未提供,Tapestry 会引发异常。

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

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

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

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

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

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

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

Tapestry 还有许多其他有用的扩展,其中一些列在下面:

  • 字面量扩展 (literal:«val») - 一个字面字符串。

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

  • 验证扩展 (validate:«val») - 用于指定对象验证规则的专用字符串。例如,validate:required, minLength = 5。

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

  • 块 (block:«val») - 模板内块元素的 ID。

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

除属性扩展和变量扩展外,以上所有扩展都是只读的。它们由组件用于与页面交换数据。将扩展用作属性值时,不应使用${...}。而应仅使用不带美元符号和大括号符号的扩展。

使用参数的组件

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

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

  • 添加一个私有字段,并使用@Parameter注解对其命名。使用 required 参数将其设为必填。

@Parameter(required = true) 
private String name;
  • 添加一个私有字段 result,并使用@Propery注解。result 属性将在组件模板中使用。组件模板无法访问使用@Parameter注解的字段,只能访问使用@Property注解的字段。组件模板中可用的变量称为渲染变量。

@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"; 
}
  • 在页面模板中使用新创建的组件,并在HelloWithParameter组件的 name 参数中设置 Username 属性。

<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 组件具有成功和失败条件的标记。成功标记指定为组件标记的主体,失败标记使用elseparameter指定。

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

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

  • Else - 用于指定备用标记的高级参数,如果条件失败。

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

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

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

  • 如果数据类型是Collection,则非空为 True。

  • 如果数据类型是Object,则为 True(只要它不是 null)。

如果条件成立,则组件渲染其主体;否则,它渲染 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 应用程序是相互交互的页面集合。到目前为止,我们已经学习了如何创建单个页面,而无需它们之间进行任何通信。组件事件的主要目的是使用服务器端事件在页面之间(以及页面内部)提供交互。大多数组件事件都源自客户端事件。

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

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

  • 组件事件请求 - 此类请求以页面中的特定组件为目标,并在组件内引发事件。此请求仅执行重定向,而不输出响应。

  • 渲染请求 - 此类请求以页面为目标,并将响应流回客户端。

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

  • 组件事件请求 -

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

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

一些 URL 模式的示例:

  • 索引页面可以通过https://«domain»/«app»/index访问。

  • 如果索引页面位于子文件夹admin下,则可以通过https://«domain»/«app»/admin/index访问。

  • 如果用户点击索引页面中id为test的ActionLink组件,则URL将变为https://«domain»/«app»/index.test

事件

默认情况下,Tapestry为所有请求触发OnPassivateOnActivate事件。对于组件事件请求类型,Tapestry会根据组件触发一个或多个附加事件。ActionLink组件触发Action事件,而Form组件触发多个事件,例如Validate、Success等。

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

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

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

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

void OnAction() { 
} 

OnPassivate和OnActivate事件

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

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

void OnActivate(int id) { 
} 

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

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

事件处理程序返回值

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

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

public Object onAction() { 
   return null; 
}
  • 字符串响应 - 返回字符串值。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并将其作为重定向发送到客户端。

  • 流响应 - 返回StreamResponse对象。Tapestry将直接将流作为响应发送到客户端浏览器。它用于直接生成报表和图像并将其发送到客户端。

  • URL响应 - 返回java.net.URL对象。Tapestry将从该对象获取相应的URL并将其作为重定向发送到客户端。

  • 对象响应 - 返回除上述指定值以外的任何值。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; 
   } 
}
广告
© . All rights reserved.