1. 程式人生 > >thymeleaf 基礎教程-閱讀官方教程(二)

thymeleaf 基礎教程-閱讀官方教程(二)

通過thymeleaf 基礎教程-搭建雜貨鋪專案環境(一)我們把官方提供的示例專案進行部署 接下來我們跟著官方文件進行thymeleaf 學習。

我們這裡從 2.1 版本的文件進行演示https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html。 下面是通過遊覽器的翻譯外掛進行翻譯教程api

1介紹Thymeleaf

1.1 什麼是Thymeleaf

Thymeleaf是一個Java庫。它是一個XML / XHTML / HTML5模板引擎,能夠將一組轉換應用於模板檔案,以顯示應用程式生成的資料和/或文字。

它更適合在Web應用程式中提供XHTML / HTML5,但它可以處理任何XML檔案,無論是在Web中還是在獨立應用程式中。

Thymeleaf的主要目標是提供一種優雅且格式良好的建立模板的方法。為了實現這一點,它基於XML標籤和屬性,它們定義了DOM(文件物件模型)上預定義邏輯的執行,而不是在模板中顯式地將該邏輯寫為程式碼。

其架構允許快速處理模板,依賴於解析檔案的智慧快取,以便在執行期間使用盡可能少的I / O操作。

最後但並非最不重要的是,Thymeleaf的設計從一開始就考慮了XML和Web標準,如果您需要,可以建立完全驗證的模板。

1.2 Thymeleaf過程可以使用哪種模板?

開箱即用,Thymeleaf允許您處理六種模板,每種模板稱為模板模式:

  • XML
  • 有效的XML
  • XHTML
  • 有效的XHTML
  • HTML5
  • 舊版HTML5

所有這些模式都指的是格式良好的XML檔案,但Legacy HTML5模式除外,它允許您處理HTML5檔案,其中包括獨立(非關閉)標記,沒有值的標記屬性或不在引號之間寫入的標記屬性。為了在這種特定模式下處理檔案,Thymeleaf將首先執行轉換,將您的檔案轉換為格式良好的XML檔案,這些檔案仍然是完全有效的HTML5(實際上是建立HTML5程式碼的推薦方法)1

另請注意,驗證僅適用於XML和XHTML模板。

然而,這些並不是Thymeleaf可以處理的唯一模板型別,並且使用者始終能夠通過指定在此模式下解析

模板的方法和編寫結果的方式來定義他/她自己的模式。這樣,任何可以建模為DOM樹(無論是否為XML)的東西都可以被Thymeleaf有效地作為模板處理。

1.3方言:標準方言

Thymeleaf是一個極易擴充套件的模板引擎(實際上它應該更好地稱為模板引擎框架),它允許您完全定義將在模板中處理的DOM節點以及它們的處理方式。

將一些邏輯應用於DOM節點的物件稱為處理器,並且一組這些處理器 - 還有一些額外的工件 - 稱為方言,其中Thymeleaf的核心庫提供一個開箱即用的標準方言,這應該足以滿足大部分使用者的需求。

標準方言是本教程涵蓋的方言。您將在以下頁面中瞭解的每個屬性和語法功能都由此方言定義,即使沒有明確提及。

當然,如果使用者希望在利用庫的高階功能的同時定義自己的處理邏輯,則可以建立自己的方言(甚至擴充套件標準方言)。模板引擎可以一次配置多種方言。

官方的thymeleaf-spring3和thymeleaf-spring4整合包都定義了一種稱為“SpringStandard方言”的方言,大部分等同於標準方言,但經過少量修改以更好地利用Spring Framework中的某些功能(例如,使用Spring Expression語言而不是Thymeleaf的標準OGNL)。因此,如果您是Spring MVC使用者,那麼您不會浪費時間,因為您在此處學習的幾乎所有內容都將在Spring應用程式中使用。

Thymeleaf Standard Dialect可以在任何模式下處理模板,但特別適用於面向Web的模板模式(XHTML和HTML5模式)。除了HTML5,它還專門支援和驗證以下XHTML規範:XHTML 1.0 TransitionalXHTML 1.0 StrictXHTML 1.0 FramesetXHTML 1.1

標準方言的大多數處理器都是屬性處理器。這使得瀏覽器甚至可以在處理之前正確顯示XHTML / HTML5模板檔案,因為它們只會忽略其他屬性。例如,雖然使用標記庫的JSP可能包含不能由瀏覽器直接顯示的程式碼片段,例如:

<form:inputText name="userName" value="${user.name}" />

 

...... Thymeleaf標準方言將允許我們實現相同的功能:

<input type="text" name="userName" value="James Carrot" th:value="${user.name}" />

這不僅會被瀏覽器正確顯示,而且還允許我們(可選)指定其中的值屬性(在這種情況下為“James Carrot”),當在瀏覽器中靜態開啟原型時將顯示該屬性,並且將由${user.name}在模板的Thymeleaf處理期間評估得到的值代替。

如果需要,這將允許您的設計人員和開發人員處理相同的模板檔案,並減少將靜態原型轉換為工作模板檔案所需的工作量。這樣做的能力通常稱為自然模板

1.4整體架構

Thymeleaf的核心是DOM處理引擎。具體來說,它使用自己的高效能DOM實現 - 不是標準DOM API--用於構建模板的記憶體樹表示,稍後通過遍歷節點並在其上執行修改DOM的處理器來操作模板。當前配置和傳遞給模板的資料集,用於表示 - 稱為上下文。

使用DOM模板表示使其非常適合Web應用程式,因為Web文件通常表示為物件樹(實際上DOM樹是瀏覽器在記憶體中表示網頁的方式)。此外,基於大多數Web應用程式僅使用幾十個模板的想法,這些模板不是大檔案,並且在應用程式執行時它們通常不會更改,Thymeleaf使用解析模板DOM樹的記憶體快取允許它在生產環境中快速,因為大多數模板處理操作都需要很少的I / O(如果有的話)。

如果您想了解更多細節,本教程後面將有一整章專門介紹快取以及Thymeleaf優化記憶體和資源使用的方式,以便更快地執行。

然而,存在一個限制:與其他模板解析/處理方法相比,此架構還需要為每個模板執行使用更大量的記憶體空間,這意味著您不應該使用該庫來建立大資料XML文件(而不是網路檔案)。作為一般經驗法則(並且始終取決於JVM的記憶體大小),如果在單個模板執行中生成大小在幾十兆位元組的XML檔案,則可能不應該使用Thymeleaf。

我們認為這種限制的原因僅適用於資料XML檔案而不是Web XHTML / HTML5是因為您永遠不應該生成如此大的Web文件以至於使用者的瀏覽器設定為 ablaze 和/或 explode  - 請記住,這些瀏覽器也必須建立DOM樹為您的頁面!

1.5在繼續之前,你應該閱讀......
 

Thymeleaf特別適合在Web應用程式中工作。Web應用程式基於一系列標準,每個人都應該非常清楚,但很少有人 - 即使他們多年來一直與他們合作。

隨著HTML5的出現,當今Web標準的最新技術比以往更加混亂...... 我們是否會從XHTML轉向HTML?我們會放棄XML語法嗎?為什麼沒有人再談論XHTML 2.0了?

因此,在本教程中進一步討論之前,強烈建議您閱讀有關Thymeleaf網站上的文章“從HTML到HTML(通過HTML)”,您可以在以下地址找到該文章:http//www.thymeleaf.org /doc/articles/fromhtmltohtmlviahtml.html

2 The Good Thymes虛擬雜貨店

2.1雜貨店的網站

為了更好地解釋使用Thymeleaf處理模板所涉及的概念,本教程將使用您可以從專案網站下載的演示應用程式。

此應用程式代表虛構的虛擬雜貨店的網站,並將為我們提供足夠的場景來舉例說明各種Thymeleaf功能。

我們將需要一套非常簡單的模型實體用於我們的應用程式:通過建立Products銷售。我們還將管理這些:Customers Orders Comments Products

我們的小應用程式也將有一個非常簡單的服務層,由Service包含以下方法的物件組成:

public class ProductService {

    ...

    public List<Product> findAll() {
        return ProductRepository.getInstance().findAll();
    }

    public Product findById(Integer id) {
        return ProductRepository.getInstance().findById(id);
    }
    
}

最後,在Web層,我們的應用程式將有一個過濾器,它將根據請求URL將執行委託給啟用Thymeleaf的命令:

private boolean process(HttpServletRequest request, HttpServletResponse response)
        throws ServletException {
        
    try {
            
        /*
         * Query controller/URL mapping and obtain the controller
         * that will process the request. If no controller is available,
         * return false and let other filters/servlets process the request.
         */
        IGTVGController controller = GTVGApplication.resolveControllerForRequest(request);
        if (controller == null) {
            return false;
        }
        /*
         * Obtain the TemplateEngine instance.
         */
        TemplateEngine templateEngine = GTVGApplication.getTemplateEngine();
            
        /*
         * Write the response headers
         */
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        /*
         * Execute the controller and process view template,
         * writing the results to the response writer.
         */
        controller.process(
                request, response, this.servletContext, templateEngine);

        return true;
            
    } catch (Exception e) {
        throw new ServletException(e);
    }
        
}    

這是我們的IGTVGController介面: 

public interface IGTVGController {

    public void process(
            HttpServletRequest request, HttpServletResponse response,
            ServletContext servletContext, TemplateEngine templateEngine);    
    
}

我們現在要做的就是建立IGTVGController介面的實現,從服務中檢索資料並使用TemplateEngine物件處理模板。

最後,它看起來像這樣:

但首先讓我們看看該模板引擎是如何初始化的。

2.2建立和配置模板引擎

我們的過濾器中的process(...)方法包含這句話:

TemplateEngine templateEngine = GTVGApplication.getTemplateEngine();

這意味著GTVGApplication類負責建立和配置啟用Thymeleaf的應用程式中最重要的物件之一:TemplateEngine例項。

我們的org.thymeleaf.TemplateEngine物件初始化如下:

public class GTVGApplication {
  
    
    ...
    private static TemplateEngine templateEngine;
    ...
    
    
    static {
        ...
        initializeTemplateEngine();
        ...
    }
    
    
    private static void initializeTemplateEngine() {
        
        ServletContextTemplateResolver templateResolver = 
            new ServletContextTemplateResolver();
        // XHTML is the default mode, but we set it anyway for better understanding of code
        templateResolver.setTemplateMode("XHTML");
        // This will convert "home" to "/WEB-INF/templates/home.html"
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        // Template cache TTL=1h. If not set, entries would be cached until expelled by LRU
        templateResolver.setCacheTTLMs(3600000L);
        
        templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        
    }
    
    ...

}

 當然,有很多方法可以配置TemplateEngine物件,但是現在這幾行程式碼將足以告訴我們所需的步驟。

模板解析器

讓我們從模板解析器開始:

ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();

 模板解析器是實現Thymeleaf API介面的物件,稱為org.thymeleaf.templateresolver.ITemplateResolver

public interface ITemplateResolver {

    ...
  
    /*
     * Templates are resolved by String name (templateProcessingParameters.getTemplateName())
     * Will return null if template cannot be handled by this template resolver.
     */
    public TemplateResolution resolveTemplate(
            TemplateProcessingParameters templateProcessingParameters);

}

這些物件負責確定如何訪問模板,在這個GTVG應用程式中,org.thymeleaf.templateresolver.ServletContextTemplateResolver我們使用的實現指定我們將從Servlet上下文中檢索我們的模板檔案作為資源:存在的應用程式範圍的javax.servlet.ServletContext物件在每個Java Web應用程式中,考慮到Web應用程式root作為資源路徑的根,它可以解析資源。

但這並不是我們可以說的關於模板解析器的全部內容,因為我們可以在其上設定一些配置引數。首先,模板模式,標準模式之一:

 

templateResolver.setTemplateMode("XHTML");

 XHTML是預設的模板模式ServletContextTemplateResolver,但最好還是建立它,以便我們的程式碼清楚地記錄正在發生的事情。

templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");

這些字首字尾完全符合它的樣子:修改我們將傳遞給引擎的模板名稱,以獲取要使用的實際資源名稱。

使用此配置,模板名稱“product / list”將對應於:

servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")

(可選)可以通過cacheTTLMs屬性在Template Resolver中配置生成在快取中的解析模板被視為有效的時間量:

templateResolver.setCacheTTLMs(3600000L);

當然,如果達到最大快取記憶體大小並且它是當前快取記憶體的最舊條目,則在到達TTL之前可以從快取記憶體中驅逐模板。

使用者可以通過實現ICacheManager介面或僅StandardCacheManager預設修改物件集來管理快取來定義快取行為和大小。

我們稍後會詳細瞭解模板解析器。現在讓我們看一下Template Engine物件的建立。

模板引擎

Template Engine物件屬於org.thymeleaf.TemplateEngine類,這些是在當前示例中建立引擎的行:

templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);

 

相當簡單,不是嗎?我們所需要的只是建立一個例項並將模板解析器設定為它。

模板解析器是唯一需要的引數TemplateEngine,儘管當然還有許多其他引數將被覆蓋(訊息解析器,快取大小等)。現在,這就是我們所需要的。

我們的模板引擎現已準備就緒,我們可以使用Thymeleaf開始建立我們的頁面。


3使用文字 

3.1多語言歡迎

我們的第一個任務是為我們的雜貨網站建立一個主頁。

我們將編寫此頁面的第一個版本非常簡單:只是標題和歡迎訊息。這是我們的/WEB-INF/templates/home.html檔案:

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  </head>

  <body>
  
    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>
  
  </body>

</html>

你會在這裡注意的第一件事是這個檔案是XHTML,可以被任何瀏覽器正確顯示,因為它不包含任何非XHTML標籤(並且瀏覽器忽略了他們不理解的所有屬性,例如th:text)。此外,瀏覽器將以標準模式(不是在quirks mode下)顯示它,因為它具有格式良好的DOCTYPE宣告。

接下來,這也是有效的 XHTML 2,因為我們已經指定了一個Thymeleaf DTD,它定義了類似的屬性,th:text以便您的模板可以被認為是有效的。甚至更多:一旦模板被處理(並且所有th:*屬性都被刪除),Thymeleaf將自動用DOCTYPE標準的DTD宣告替換該子句中的DTD宣告XHTML 1.0 Strict(我們將在後面的章節中保留此DTD轉換功能)。

還為th:*屬性聲明瞭thymeleaf名稱空間:

 

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">

請注意,如果我們根本不關心模板的有效性或格式良好,我們可以簡單地指定一個標準XHTML 1.0 Strict DOCTYPE,而不是xmlns名稱空間宣告:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  </head>

  <body>
  
    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>
  
  </body>

</html>

......這仍然是Thymeleaf在XHTML模式下完全可以處理的(儘管我們的IDE可能會讓我們的生活變得非常悲慘,並且在各處顯示警告)。

但足夠的驗證。現在,對於模板中真正有趣的部分:讓我們看看該th:text屬性是關於什麼的。

Using th:text and externalizing text

 

外化文字(externalizing text)是從模板檔案中提取模板程式碼的片段,以便它們可以儲存在特定的單獨檔案(通常是.properties檔案)中,並且可以用其他語言編寫的等效文字(稱為國際化或簡稱為i18n)輕鬆替換它們。外化的文字片段通常稱為“訊息”。

訊息始終具有標識它們的鍵,Thymeleaf允許您使用以下#{...}語法指定文字應與特定訊息相對應:

 

<p th:text="#{home.welcome}">Welcome to our grocery store!</p>

我們在這裡看到的實際上是Thymeleaf標準方言的兩個不同特徵:

  • th:text屬性評估其值表示式並將此評估的結果設定為它所在標記的主體,有效地替換了我們在程式碼中看到的“歡迎來到我們的雜貨店!”文字。
  • #{home.welcome}表達,在指定的標準表示式語法,指定要由所使用的文字th:text屬性應與該訊息home.welcome對應於哪個語言環境,我們正在處理與模板鍵。

現在,這個外化文字在哪裡?

Thymeleaf中外化文字的位置是完全可配置的,它取決於org.thymeleaf.messageresolver.IMessageResolver所使用的具體實現。通常,.properties將使用基於檔案的實現,但是如果我們想要,例如,從資料庫獲取訊息,我們可以建立自己的實現。

但是,我們在初始化期間沒有為模板引擎指定訊息解析器,這意味著我們的應用程式正在使用由類實現的標準訊息解析器org.thymeleaf.messageresolver.StandardMessageResolver

此標準訊息解析程式期望/WEB-INF/templates/home.html在同一資料夾中找到.properties檔案中的訊息,並使用與模板相同的名稱,例如:

  • /WEB-INF/templates/home_en.properties 用於英文文字。
  • /WEB-INF/templates/home_es.properties 西班牙語文字。
  • /WEB-INF/templates/home_pt_BR.properties 用於葡萄牙語(巴西)語言文字。
  • /WEB-INF/templates/home.properties 對於預設文字(如果區域設定不匹配)。

我們來看看我們的home_es.properties檔案:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

這就是我們將Thymeleaf流程作為模板所需的全部內容。讓我們建立我們的Home控制器。

這裡所說的 外化文字(externalizing text) 其實就是在模板路徑下 和訪問頁面名稱一模一樣的配置檔案 如下圖

他會根據request中的getLocale()方法獲取當前的地區 如果有具體國際化配置檔案就取國際化的配置檔案 如果沒有就取 home.properties

因為HomeController 中locale 中的值是 zh_CN 但是模板目錄下沒有home_zh_CN.properties 配置檔案所以home.welcome的值取得就是 home.properties 中的值

如果我們新增  home_zh_CN.properties 並且內容如下 

 

上下文

為了處理我們的模板,我們將建立一個HomeController實現IGTVGController我們之前看到的介面的類:

ublic class HomeController implements IGTVGController {

    public void process(
            HttpServletRequest request, HttpServletResponse response,
            ServletContext servletContext, TemplateEngine templateEngine) {
        
        WebContext ctx = 
            new WebContext(request, response, servletContext, request.getLocale());
        templateEngine.process("home", ctx, response.getWriter());
        
    }

}

我們在這裡可以看到的第一件事是建立一個context。Thymeleaf context是實現org.thymeleaf.context.IContext介面的物件。context 應包含在變數對映中執行模板引擎所需的所有資料,並且還引用必須用於外部化訊息的區域設定。 

public interface IContext {

    public VariablesMap<String,Object> getVariables();
    public Locale getLocale();
    ...
    
}

 這個介面有一個專門的擴充套件,org.thymeleaf.context.IWebContext

public interface IWebContext extends IContext {
    
    public HttpSerlvetRequest getHttpServletRequest();
    public HttpSession getHttpSession();
    public ServletContext getServletContext();
    
    public VariablesMap<String,String[]> getRequestParameters();
    public VariablesMap<String,Object> getRequestAttributes();
    public VariablesMap<String,Object> getSessionAttributes();
    public VariablesMap<String,Object> getApplicationAttributes();
    
}

Thymeleaf核心庫提供了以下每個介面的實現: 

  • org.thymeleaf.context.Context implements IContext
  • org.thymeleaf.context.WebContext implements IWebContext

 正如您在控制器程式碼中看到的那樣,WebContext我們將使用它。實際上我們必須這樣做,因為使用a ServletContextTemplateResolver要求我們使用context 實現IWebContext

WebContext ctx = new WebContext(request, servletContext, request.getLocale());

 

這三個建構函式引數中只有兩個是必需的,因為如果沒有指定系統,則將使用系統的預設語言環境(儘管在實際應用程式中不應該發生這種情況)。

從介面定義我們可以看出,WebContext它將提供用於獲取請求引數和請求,會話和應用程式屬性的專用方法。但實際上WebContext會做的不僅僅是:

 

 

  • 將所有請求屬性新增到上下文變數對映中。
  • 新增一個param包含所有請求引數的上下文變數。
  • 新增一個session包含所有會話屬性的上下文變數。
  • 新增一個名為application包含所有ServletContext屬性的上下文變數。

在執行之前,將一個特殊變數設定到所有上下文物件(實現IContext)中,包括兩者,ContextWebContext稱為執行資訊(execInfo)。此變數包含兩個可在模板中使用的資料:

  • 模板名稱(${execInfo.templateName}),為引擎執行指定的名稱,以及與正在執行的模板相對應的名稱。
  • 當前日期和時間(${execInfo.now}),Calendar對應於模板引擎開始執行此模板的時刻的物件。 

執行模板引擎

準備好上下文物件後,我們需要的只是執行指定模板名稱和上下文的模板引擎,並傳遞響應編寫器以便可以將響應寫入它:

templateEngine.process("home", ctx, response.getWriter());

 讓我們使用西班牙語語言環境檢視結果:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
  </head>

  <body>
  
    <p>¡Bienvenido a nuestra tienda de comestibles!</p>

  </body>

</html>

3.2有關文字和變數的更多資訊

未轉義的文字

我們主頁的最簡單版本現在似乎已經準備就緒,但有一些我們沒有想過的......如果我們有這樣的訊息怎麼辦?

home.welcome=Welcome to our <b>fantastic</b> grocery store!

如果我們像以前一樣執行此模板,我們將獲得: 

<p>Welcome to our &lt;b&gt;fantastic&lt;/b&gt; grocery store!</p>

這不完全符合我們的預期,因為我們的<b>標籤已被轉義,因此它將在瀏覽器中顯示。

這是th:text屬性的預設行為。如果我們希望Thymeleaf尊重我們的XHTML標籤而不是逃避它們,我們將不得不使用不同的屬性:( th:utext對於“非轉義文字”):

 

<span style="color:#333333"><code class="language-html"><span style="color:#8bd1ff"><span style="color:#8bd1ff"><span style="color:#b9bdb6"><</span>p</span> <span style="color:#e0e8ff">th:utext</span><span style="color:#99cc33"><span style="color:#b9bdb6">=</span><span style="color:#b9bdb6">"</span>#{home.welcome}<span style="color:#b9bdb6">"</span></span><span style="color:#b9bdb6">></span></span>Welcome to our grocery store!<span style="color:#8bd1ff"><span style="color:#8bd1ff"><span style="color:#b9bdb6"></</span>p</span><span style="color:#b9bdb6">></span></span>
This will output our message just like we wanted it:
<span style="color:#8bd1ff"><span style="color:#8bd1ff"><span style="color:#b9bdb6"><</span>p</span><span style="color:#b9bdb6">></span></span>Welcome to our <span style="color:#8bd1ff"><span style="color:#8bd1ff"><span style="color:#b9bdb6"><</span>b</span><span style="color:#b9bdb6">></span></span>fantastic<span style="color:#8bd1ff"><span style="color:#8bd1ff"><span style="color:#b9bdb6"></</span>b</span><span style="color:#b9bdb6">></span></span> grocery store!<span style="color:#8bd1ff"><span style="color:#8bd1ff"><span style="color:#b9bdb6"></</span>p</span><span style="color:#b9bdb6">></span></span></code></span>

 使用和顯示變數

現在讓我們在主頁上新增更多內容。例如,我們可能希望在歡迎訊息下方顯示日期,如下所示:

Welcome to our fantastic grocery store!

Today is: 12 july 2010

首先,我們必須修改控制器,以便將該日期新增為上下文變數:

我們在String上下文中添加了一個今日變數,現在我們可以在模板中顯示它: 

正如您所看到的,我們仍在使用th:text作業的屬性(這是正確的,因為我們想要替換標籤的主體),但這次語法有點不同而不是#{...}表示式值,我們使用的是${...}一。這是一個變量表達式值,它包含一個名為OGNL(物件圖導航語言)的語言表示式,該表示式將在上下文變數對映中執行。

${today}表示式只是表示“get the variable called today”,但這些表述可能更加複雜(如${user.name}“獲取被叫使用者的變數,並呼叫它的getName()方法”)。

屬性值有很多可能性:訊息,變量表達式......還有很多。下一章將向我們展示所有這些可能性。

 4標準表示式語法

 

我們將在我們的雜貨虛擬商店的開發中稍作休息,以瞭解Thymeleaf標準方言中最重要的部分之一:Thymeleaf標準表示式語法。

我們已經看到在這種語法中表達的兩種型別的有效屬性值:訊息和變量表達式:

但是我們還不知道還有更多型別的價值,還有更多有趣的細節來了解我們已經知道的。首先,讓我們看一下標準表示式功能的快速摘要:

  • 簡單表達:
    • 變量表達式: ${...}
    • 選擇變量表達式: *{...}
    • 訊息表示式: #{...}
    • 連結網址表示式: @{...}
  • 字面
    • 文字文字:'one text''Another one!',...
    • 號碼文字:0343.012.3,...
    • 布林文字:truefalse
    • 空字面: null
    • 文字標記:onesometextmain,...
  • 文字操作:
    • 字串連線: +
    • 文字替換: |The name is ${name}|
  • 算術運算:
    • 二元運算子:+-*/%
    • 減號(一元運算子): -
  • 布林運算:
    • 二元運算子:andor
    • 布林否定(一元運算子): !not
  • 比較和平等:
    • 比較:><>=<=gtltgele
    • 平等運營商:==!=eqne

有條件的運營商:

  • IF-THEN: (if) ? (then)
  • IF-THEN-ELSE: (if) ? (then) : (else)
  • 預設: (value) ?: (defaultvalue) 

所有這些功能都可以組合和巢狀:

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

4.1 訊息 

我們已經知道,#{...}訊息表示式允許我們連結:

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

......對此: 

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

但是有一個方面我們還沒有想到:如果訊息文字不是完全靜態會發生什麼?例如,如果我們的應用程式知道誰是隨時訪問該網站的使用者並且我們想要通過名字問候他/她怎麼辦? 

<p>¡Bienvenido a nuestra tienda de comestibles, John Apricot!</p>

這意味著我們需要在訊息中新增一個引數。像這樣: 

home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!

根據java.text.MessageFormat標準語法指定引數,這意味著您可以將格式新增到該類的API文件中指定的數字和日期。

為了為我們的引數指定一個值,並給定一個呼叫的HTTP會話屬性user,我們將: 

<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

如果需要,可以指定幾個引數,用逗號分隔。實際上,訊息金鑰本身可以來自變數: 

<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

4.2變數 

我們已經提到${...}表示式實際上是在上下文中包含的變數對映上執行的OGNL(物件 - 圖形導航語言)表示式。

有關OGNL語法和功能的詳細資訊,請閱讀OGNL語言指南:http//commons.apache.org/ognl/

從OGNL的語法,我們知道這個:

<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>

......實際上相當於: 

ctx.getVariables().get("today");

但是OGNL允許我們建立更強大的表示式,這就是: 

<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

...實際上通過執行以下方式獲取使用者名稱: 

((User) ctx.getVariables().get("session").get("user")).getName();

 但是getter方法導航只是OGNL的功能之一。讓我們看看更多:

/*
 * Access to properties using the point (.). Equivalent to calling property getters.
 */
${person.father.name}

/*
 * Access to properties can also be made by using brackets ([]) and writing 
 * the name of the property as a variable or between single quotes.
 */
${person['father']['name']}

/*
 * If the object is a map, both dot and bracket syntax will be equivalent to 
 * executing a call on its get(...) method.
 */
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}

/*
 * Indexed access to arrays or collections is also performed with brackets, 
 * writing the index without quotes.
 */
${personsArray[0].name}

/*
 * Methods can be called, even with arguments.
 */
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}

表示式基本物件 

在上下文變數上評估OGNL表示式時,某些物件可用於表示式以獲得更高的靈活性。將從#符號開始引用這些物件(根據OGNL標準):

  • #ctx:上下文物件。
  • #vars: 上下文變數。
  • #locale:上下文區域設定。
  • #httpServletRequest:(僅限Web Contexts)HttpServletRequest物件。
  • #httpSession:(僅限Web Contexts)HttpSession物件。

 所以我們可以這樣做:

Established locale country: <span th:text="${#locale.country}">US</span>.

 您可以在附錄A中閱讀這些物件的完整參考。

Expression Utility物件

 

除了這些基本物件,Thymeleaf還將為我們提供一組實用程式物件,幫助我們在表示式中執行常見任務。

  • #datesjava.util.Date物件的實用方法:格式化,元件提取等。
  • #calendars:類似於#dates,但java.util.Calendar物件。
  • #numbers:用於格式化數字物件的實用方法。
  • #stringsString物件的實用方法:contains,startsWith,prepending / appending等。
  • #objects:一般的物件的實用方法。
  • #bools:布林評估的實用方法。
  • #arrays:陣列的實用方法。
  • #lists:列表的實用方法。
  • #sets:集合的實用方法。
  • #maps:地圖的實用方法。
  • #aggregates:用於在陣列或集合上建立聚合的實用程式方法。
  • #messages:用於在變量表達式中獲取外部化訊息的實用程式方法,與使用#{...}語法獲取它們的方式相同。
  • #ids:用於處理可能重複的id屬性的實用程式方法(例如,作為迭代的結果)。

您可以在附錄B中檢視每個實用程式物件提供的功能。

在我們的主頁中重新格式化日期

現在我們瞭解這些實用程式物件,我們可以使用它們來改變我們在主頁中顯示日期的方式。而不是在我們這樣做HomeController

SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();

WebContext ctx = new WebContext(request, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());

......我們可以做到這一點: 

WebContext ctx = new WebContext(request, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());

templateEngine.process("home", ctx, response.getWriter());

...然後在檢視層本身中執行日期格式設定: 

<p>
  Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>

4.3選擇表示式(星號語法) 

變量表達式不僅可以用${...}表示式編寫,還可以用於表示式*{...}

但是有一個重要的區別:星號語法評估所選物件的表示式而不是整個上下文變數對映。這是:只要沒有選定物件,$和*語法就會完全相同。

什麼是物件選擇的東西?一個th:object屬性。讓我們在我們的使用者個人資料(userprofile.html)頁面中使用它:

 

  <div th:object="${session.user}">
    <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
    <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
  </div>

這完全等同於:

<div>
  <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

 當然,$ 和 * 語法可以混合使用:

<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

當物件選擇到位時,所選物件也可用作美元表示式作為#object表示式變數:

<div th:object="${session.user}">
  <p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

如上所述,如果沒有執行任何物件選擇,則美元和星號語法完全相同。

<div>
  <p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
  <p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>

 4.4連結URL

由於它們的重要性,URL是Web應用程式模板中的一等公民,而Thymeleaf Standard Dialect具有特殊的語法,@語法:@{...}

有不同型別的網址:

  • 絕對的URL,比如 http://www.thymeleaf.org
  • 相對URL,可以是:
    • 頁面相對,像 user/login.html
    • 上下文相關,如/itemdetails?id=3(伺服器中的上下文名稱將自動新增)
    • 與伺服器相關,~/billing/processInvoice(允許在同一伺服器中呼叫另一個上下文(=應用程式)中的URL)。
    • 協議相對URL,如 //code.jquery.com/jquery-2.0.3.min.js

Thymeleaf可以在任何情況下處理絕對URL,但對於相對URL,它將要求您使用實現IWebContext介面的上下文物件,其中包含來自HTTP請求的一些資訊以及建立相對連結所需的資訊。

讓我們使用這種新語法。符合th:href屬性:

 

<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" 
   th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

 

這裡要注意的一些事情:

  • th:href屬性修飾符屬性:一旦處理,它將計算要使用的連結URL,並將<a>標記的href屬性設定為此URL。
  • 我們被允許對URL引數使用表示式(如您所見orderId=${o.id})。還將自動執行所需的URL編碼操作。
  • 如果需要幾個引數,這些引數將用逗號分隔 @{/order/process(execId=${execId},execType='FAST')}
  • URL路徑中也允許使用變數模板,例如 @{/order/{orderId}/details(orderId=${orderId})}
  • /(like /order/details)開頭的相對URL 將自動作為應用程式上下文名稱的字首。
  • 如果未啟用cookie或尚未知道cookie,則";jsessionid=..."可能會在相對URL中新增字尾,以便保留會話。這稱為URL重寫,Thymeleaf允許您通過response.encodeURL(...)Servlet API為每個URL 插入自己的重寫過濾器。
  • th:href標記允許我們(可選)有一個靜態的工作href在我們的模板屬性,所以,當為原型的目的直接開啟我們的模板連結仍然通航由瀏覽器。

與訊息語法(#{...})的情況一樣,URL基數也可以是評估另一個表示式的結果:

 

<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>

我們主頁的選單

現在我們知道如何建立連結URL,如何在我們的家中為網站中的其他一些頁面新增一個小選單?

<p>Please select an option</p>
<ol>
  <li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
  <li><a href="order/list.html" th:href="@{/order/list}">Order List</a></li>
  <li><a href="subscribe.html" th:href="@{/subscribe}">Subscribe to our Newsletter</a></li>
  <li><a href="userprofile.html" th:href="@{/userprofile}">See User Profile</a></li>
</ol>

 伺服器根目錄相對URL

可以使用其他語法來建立伺服器根相對(而不是上下文根相對)URL,以便連結到同一伺服器中的不同上下文。這些URL將被指定為@{~/path/to/something}

4.5文字

 ###文字文字

文字文字只是在單引號之間指定的字串。它們可以包含任何字元,但您應該將其中的任何單引號轉義為\'

<p>
  Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>

 ###數字文字

數字文字看起來與它們完全相同:數字。

<p>The year is <span th:text="2013">1492</span>.</p>
<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>

###布林文字 

 布林文字是truefalse。例如:

<div th:if="${user.isAdmin()} == false"> ...

 請注意,在上面的例子中,它== false被寫在括號之外,因此是Thymeleaf本身負責它。如果它是在大括號內寫的,那麼它將由OGNL / SpringEL引擎負責:

<div th:if="${user.isAdmin() == false}"> ...

 

### null文字

null文字也可用於:

 

<div th:if="${variable.something} == null"> ...

### Literal標記

實際上,數字,布林和空文字是文字標記的特例。

這些令牌允許在標準表示式中進行一些簡化。它們的工作方式與文字文字('...')完全相同,但它們只允許使用字母(A-Za-z),數字(0-9),括號([]),點(.),連字元(-)和下劃線(_)。所以沒有空格,沒有逗號等。

好的部分?令牌不需要任何圍繞它們的引號。所以我們可以這樣做:

<div th:class="content">...</div>

代替:

<div th:class="'content'">...</div>

 4.6附加文字

文字,無論是文字還是評估變數或訊息表示式的結果,都可以使用+運算子輕鬆附加:

th:text="'The name of the user is ' + ${user.name}"

 4.7字面替換

文字替換允許輕鬆格式化包含變數值的字串,而無需附加文字'...' + '...'

這些替換必須用豎線(|)包圍,如:

 

<span th:text="|Welcome to our application, ${user.name}!|">

這實際上相當於:

<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

 文字替換可以與其他型別的表達相結合:

<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">

注意:${...}|...|文字替換中只允許使用變量表達式()。沒有其他文字('...'),布林/數字標記,條件表示式等。 

4.8算術運算 

一些算術運算也可用:+-*/%

th:with="isEven=(${prodStat.count} % 2 == 0)"

請注意,其中一些運算子存在文字別名:div/),mod%)。 

4.9比較器和平等

在表示式中的值可以與進行比較><>=<=符號,像往常一樣,也是==!=運營商可以被用於檢查平等(或缺乏)。請注意,XML確定不應在屬性值中使用<>符號,因此應將它們替換為&lt;&gt;

<span style="color:#333333"><code class="language-html">th:if="${prodStat.count} &gt; 1"
th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')"</code></span>

 請注意,其中一些運算子存在文字別名:gt>),lt<),ge>=),le<=),not!)。還eq==),neqne!=)。

4.10條件表示式

 

條件表示式僅用於評估兩個表示式中的一個,具體取決於評估條件的結果(它本身就是另一個表示式)。

讓我們看一個示例片段(這次引入另一個屬性修飾符th:class):

條件表示式(conditionthenelse)的所有三個部分本身都是表示式,這意味著它們可以是變數(${...}*{...}),訊息(#{...}),URL(@{...})或文字('...')。

條件表示式也可以使用括號巢狀:

其他表示式也可以省略,在這種情況下,如果條件為false,則返回null值: 

4.11預設表示式(Elvis運算子) 

一個預設的表情是一種特殊的條件值的沒有那麼一部分。它等同於某些語言(如Groovy)中存在的Elvis運算子,並允許指定兩個表示式,即第二個僅在第一個返回null的情況下計算的表示式。

讓我們在使用者個人資料頁面中看到它:

 

正如您所看到的,運算子是?:,並且我們在此處使用它來指定名稱的預設值(在這種情況下為文字值),僅當評估結果*{age}為null時。因此,這相當於:

<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>

與條件值