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.