📜  Apache Tapestry-组件

📅  最后修改于: 2020-10-27 03:05:12             🧑  作者: Mango


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

Tapestry组件使用交互式AJAX呈现到复杂网格功能的简单HTML链接。一个组件也可以包括另一个组件。挂毯组件包括以下项目-

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

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

  • 机构-网页模板中指定的组件可定制的标记,它被称为“组件的身体”。如果组件模板具有元素,则元素将被组件的主体替换。这类似于前面在XML模板部分讨论的布局。

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

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

  • 事件-将功能从组件委派到其容器/父级(页面或其他组件)。它广泛用于页面导航目的。

渲染图

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

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

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

下面列出了阶段,其方法名称及其注释。

Annotation Default Method Names
@SetupRender setupRender()
@BeginRender beginRender()
@BeforeRenderTemplate beforeRenderTemplate()
@BeforeRenderBody beforeRenderBody()
@AfterRenderBody afterRenderBody()
@AfterRenderTemplate afterRenderTemplate()
@AfterRender afterRender()
@CleanupRender cleanupRender()

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

设置渲染

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

BeginRender

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

BeforeRenderTemplate

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

BeforeRenderBody

BeforeRenderTemplate提供了一个选项,以跳过组件的body元素的呈现。

AfterRenderBody

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

AfterRenderTemplate

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

AfterRender

AfterRender是BeginRender的对应部分,通常呈现close标签。

清理渲染

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

渲染阶段的流程不仅仅向前。它在阶段之间来回移动,具体取决于阶段的返回值。

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

注释清单

简单组件

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

package com.example.MyFirstApplication.components;  
public class Hello {  
}

Hello, Tapestry (from component).

您可以在页面模板中将Hello组件称为-

  

类似地,组件可以使用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("

Hello, Tapestry (from component)

"); } }

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

xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   

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

 
      

Hello, Tapestry (from page).

输出如下-

Hello, Tapestry (from page).

参量

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

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

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

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

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

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

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

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

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

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

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

Tapestry有更多有用的扩展,其中一些在下面给出-

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

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

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

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

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

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

除Property扩展和Var扩展外,以上所有扩展均为只读。组件使用它们与页面交换数据。将扩展用作属性值时,不应使用$ {…} 。而是只使用不带美元和花括号符号的扩展。

组件使用参数

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

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

  • 添加一个私有字段,并使用@Parameter批注对其进行命名。使用必需的参数使其强制。

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

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

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

Hello, ${result}
  • 在测试页面(testhello.java)中添加一个新属性“用户名”。

public String getUsername() { 
   return "User1"; 
}
  • 使用页面模板中新创建的组件,并在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; 
   } 
}
Hello, ${result}
package com.example.MyFirstApplication.pages;  

import org.apache.tapestry5.annotations.*;  
public class TestHello { 
   public String getUsername() { 
      return "User1"; 
   } 
}

   
 

结果将如下-

Hello, User1

进阶参数

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

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

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

  • 否则-如果条件失败,则用于指定替代标记的高级参数

Tapestry将使用以下逻辑检查测试属性的值,并返回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"; 
   } 
}

Welcome!

Welcome back, ${user} Please Login

组件事件/页面导航

Tapestry应用程序是相互交互的Pages集合。到现在为止,我们已经学习了如何创建单个页面而无需它们之间的任何通信。组件事件的主要目的是使用服务器端事件在页面之间(以及页面内)提供交互。大多数组件事件都来自客户端事件。

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

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

  • 组件事件请求-这种类型的请求以页面中的特定组件为目标,并引发该组件内的事件。该请求仅进行重定向,不输出响应。

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

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

  • 组件事件请求

/<>.<>/<>
  • 渲染请求

/<>/<>

URL模式的一些示例是-

  • 可以通过https://«domain»/«app»/ index请求索引页。

  • 如果“索引”页面在子文件夹admin下可用,则可以通过https://«domain»/«app»/ admin / index进行请求。

  • 如果用户单击索引页面中ID为testActionLink组件,则URL将为https://«domain»/«app»/index.test

大事记

默认情况下,Tapestry为所有请求引发OnPassivateOnActivate事件。对于组件事件请求类型,挂毯根据组件引发另外的一个或多个事件。 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提供上下文信息,并且可以将其用作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; 
}
  • 字符串响应-返回字符串值。 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将从链接对象构造URL,并将其作为重定向发送到客户端。

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

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

  • 对象响应-返回除上述指定值以外的任何值。挂毯将引发错误。

事件上下文

通常,事件处理程序可以使用参数获取上下文信息。例如,如果上下文信息是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; 
   } 
}