前言
這個教程介紹了Thymeleaf與Spring框架的集成,特別是SpringMvc框架。
注意Thymeleaf支持同Spring框架的3.和4.版本的集成,但是這兩個版本的支持是封裝在thymeleaf-spring3和thymeleaf-spring4這兩個獨立的庫中,項目中需要根據實際情況分別引用。
樣例代碼針對的是spring4.,但一般情況下,spring3.也可以無縫使用,所需要的僅僅是改變一下引用庫。
1 Thymeleaf同Spring的整合
Thymeleaf與Spring進行整合后,可以在SpringMVC應用中完全替代JSP文件。
集成后你將:
*就像控制JSP一樣,使用SpringMvc的@Controller注解來映射Thymeleaf的模板文件。
*在模板中使用SpringEL表達式來替換OGNL
*在模板中創建的表單,完全支持Beans和結果的綁定,包括使用PropertyEditor,轉換,和驗證等。
*可以通過Spring來管理國際化文件顯示國際化信息。
*注意,在使用本教程之前,您應該充分了解Thymeleaf的標准方言。
2 Spring標准方言
為了更加方便,更快捷的集成,Thymeleaf提供了一套能夠與Spring正確工作的特有方言。
這套方言基於Thymeleaf標准方言實現,它在類 org.thymeleaf.spring.dialect.SpringStandardDialect 中,事實上,他繼承於 org.thymeleaf.standard.StandardDialect 中。
除了已經出現在標准方言中的所有功能,Spring中還有以下特點:
*不適用OGNL,而是SpringEL做完變量表達式,因此,所有的${...}和*{...}表達式將用Spring的表達式引擎進行處理。
*訪問應用context中的beans可以使用SpringEL語法:${@myBean.doSomething()}
*基於表格處理的新屬性:th:field,th:errors和th:errorclass,除此還有一個th:object的新實現,允許它使用表單命令選擇器(??)。
*一個新的表達式:#themes.code(...),相當於jsp自定義標簽中的spring:theme。
*在spring4.0集成中的一個新的表達式:#mvc.uri(...),相當於jsp自定義標簽中的spring:mvcUrl(...)
注意,上述這些方言特性是不能再普通的TemplateEngine對象中使用的,應該配置一個org.thymeleaf.spring4.SpringTemplateEngine來執行。
其他thymeleaf基礎使用方法部分不在敘述。
創建表單
處理命令對象
SpringMVC的表單支持bean就是命令對象,這個對象通過對象領域模型的方式提供get和set方法,在瀏覽器建立獲取用戶輸入值的輸入框架。
Thymeleaf需要你顯示的在form標簽內通過th:object屬性指定命令對象:
1 <form action="#" th:action="@{/seedstartermng}" th:object="${seedStarter}" method="post"> 2 ... 3 </form>
這個th:object與其他的的地方用途是一直的,但是事實上在這種特定情況下,為了與SpringMVC框架的正確整合增加了一些特定的限制:
1.在form標簽中的th:object的值必須是變量表達式(...),只能指定屬性模型屬性的名字,而不能使用屬性導航,這意味着,表達式...),只能指定屬性模型屬性的名字,而不能使用屬性導航,
這意味着,表達式 {seedStarter}是正確的,而${seedStarter.data}則不是。
2.一個form標簽內只能指定一個th:object屬性,這與html中form標簽不能嵌套的特性相一致。
input
下面是如何將一個input插入到表單中
<input type="text" th:field="*{datePlanted}" />
正象上邊的代碼所示,新增了一個 th:field 的屬性,這是SpringMVC集成的一個重要特征,它幫你完成了表單bean和輸入框之間的繁重的綁定工作。可以看出他在from中的路徑屬性和SpringMVC的jsp標簽庫一樣。
th:field 屬性的不同行為取決於它所附加的不同標簽,包括 <input> , <select> 或 <textarea> (還包括標簽的不同type屬性類型),在這種情況下,時間上上面哪行代碼會是這樣的:
<input type="text" id="datePlanted" name="datePlanted" th:value="*{datePlanted}" />
事實上,可能比上邊的代碼還要多一些東西,因為 th:field 還可能會注冊一個Spring的轉換服務,包括之前我們看到的 DateFormatter (甚至這個表達式中沒使用雙大括號),因此,這個日期也將被正確的格式化。
th:field 的值必須使用選擇表達式,這樣將在這個環境中使用表單bean,而不是上下文變量或SpringMVC的模型屬性。
相反對於 th:object 這類,它的表達式可以使用屬性導航(事實上在JSP的 <form:input 標簽中,可以使用任何的路徑屬性表達式)
注意th:field屬性也可以在HTML5的的新增類型中使用,如 <input type="datetime"> , <input type="color"> 等,有效的增加了對SpringMVC對HTML5支持的完整性。
復選框
th:field 也可以用在 checkbox 中,比如如下代碼:
1 <div> 2 <label th:for="${#ids.next('covered')}" th:text="#{seedstarter.covered}">已種植</label> 3 <input type="checkbox" th:field="*{covered}" /> 4 </div>
注意這里有一些除了復選框之外的好東西,比如外部label和它使用的 #ids.next("covered") 方法,用於當該id的復選框執行的時候獲取它的id值。
那么為什么我們需要這個字段的id屬性動態生成呢?因為復選框可能是多值的,因此它會給id值添加一個序列號后綴(內部使用 #ids.seq(...) 函數)來保證同一屬性的復選框有不同的id值。
我們可以看看多值的復選框:
1 <ul> 2 <li th:each="feat : ${allFeatures}"> 3 <input type="checkbox" th:field="*{features}" th:value="${feat}" /> 4 <label th:for="${#ids.prev('features')}" 5 th:text="#{${'seedstarter.feature.' + feat}}">Heating</label> 6 </li> 7 </ul>
注意這次我們增加了一個 th:value 屬性,因為這次的特征屬性不是一個布爾值,而是一個數組。
一般情況下,它的輸出為:
1 <ul> 2 <li> 3 <input id="features1" name="features" type="checkbox" 4 value="SEEDSTARTER_SPECIFIC_SUBSTRATE" /> 5 <input name="_features" type="hidden" value="on" /> 6 <label for="features1">Seed starter-specific substrate</label> 7 </li> 8 <li> 9 <input id="features2" name="features" type="checkbox" 10 value="FERTILIZER" /> 11 <input name="_features" type="hidden" value="on" /> 12 <label for="features2">Fertilizer used</label> 13 </li> 14 <li> 15 <input id="features3" name="features" type="checkbox" 16 value="PH_CORRECTOR" /> 17 <input name="_features" type="hidden" value="on" /> 18 <label for="features3">PH Corrector used</label> 19 </li> 20 </ul>
我們可以看到一個序列后綴增加在每一個id的屬性中, #ids.prev(....) 函數允許我們把檢索最后一個序列值,生成的一個特定的id。
用不着擔心那些隱藏域的名稱為"_features":這是為了避免瀏覽器將未選中的復選框的值在表單提交是沒有自動發送而故意添加的。
還應注意到,如果我們的表單bean中的feature屬性已經包含了一些特定的值,那么th:field還將會自動在相應的標簽中增加checked="checked"屬性。
單選框
單選框的用法和一個非布爾值的多選框使用方式類似,只是他不是多選:
1 <ul> 2 <li th:each="ty : ${allTypes}"> 3 <input type="radio" th:field="*{type}" th:value="${ty}" /> 4 <label th:for="${#ids.prev('type')}" th:text="#{${'seedstarter.type.' + ty}}">Wireframe</label> 5 </li> 6 </ul>
下拉列表
下拉列表包含兩個部分:<select>標簽和它包含的<option>標簽。在創建這種表單域的時候,只有<select>標簽需要導入th:field屬性,但 th:value 屬性卻在 <option> 標簽中非常重要,因為他們提供了目前選選擇框的選項(使用和非布爾復選框和單選框類似的手段)
使用類型作為下拉列表:
1 <select th:field="*{type}"> 2 <option th:each="type : ${allTypes}" 3 th:value="${type}" 4 th:text="#{${'seedstarter.type.' + type}}">Wireframe</option> 5 </select>
這段代碼理解起來很容易,只是注意屬性優先級讓我們可以在option標簽內使用th:each屬性。
動態域
由於SpringMVC的高級表單綁定功能,使得我們可以使用復雜的SpringEL表達式來綁定動態表單域到表單bean中。這將允許我們在 SeedStarter bean 中創建一個新的Row對象,並將這個row的域添加到用戶請求的form中。
為了做到這一點,我們需要在控制器中提供一些新的映射方法,它將根據我們的特定請求的參數來決定添加或刪除一行我們定義的.
1 @RequestMapping(value="/seedstartermng", params={"addRow"}) 2 public String addRow(final SeedStarter seedStarter, final BindingResult bindingResult) { 3 seedStarter.getRows().add(new Row()); 4 return "seedstartermng"; 5 } 6 7 @RequestMapping(value="/seedstartermng", params={"removeRow"}) 8 public String removeRow( 9 final SeedStarter seedStarter, final BindingResult bindingResult, 10 final HttpServletRequest req) { 11 final Integer rowId = Integer.valueOf(req.getParameter("removeRow")); 12 seedStarter.getRows().remove(rowId.intValue()); 13 return "seedstartermng"; 14 }
現在給form添加一個動態table
1 <table> 2 <thead> 3 <tr> 4 <th th:text="#{seedstarter.rows.head.rownum}">Row</th> 5 <th th:text="#{seedstarter.rows.head.variety}">Variety</th> 6 <th th:text="#{seedstarter.rows.head.seedsPerCell}">Seeds per cell</th> 7 <th> 8 <button type="submit" name="addRow" th:text="#{seedstarter.row.add}">Add row</button> 9 </th> 10 </tr> 11 </thead> 12 <tbody> 13 <tr th:each="row,rowStat : *{rows}"> 14 <td th:text="${rowStat.count}">1</td> 15 <td> 16 <select th:field="*{rows[__${rowStat.index}__].variety}"> 17 <option th:each="var : ${allVarieties}" 18 th:value="${var.id}" 19 th:text="${var.name}">Thymus Thymi</option> 20 </select> 21 </td> 22 <td> 23 <input type="text" th:field="*{rows[__${rowStat.index}__].seedsPerCell}" /> 24 </td> 25 <td> 26 <button type="submit" name="removeRow" 27 th:value="${rowStat.index}" th:text="#{seedstarter.row.remove}">Remove row</button> 28 </td> 29 </tr> 30 </tbody> 31 </table>
這里出現了很多東西,但都不難理解,除了這一句:
1 <select th:field="*{rows[__${rowStat.index}__].variety}"> 2 ... 3 </select>
如果你記得Thymeleaf教程,那么應該明白 __${...}__ 是一種預處理表達式的語法。這是一個在處理整個表達式之前的內部表達式,但為什么用這種方式指定行的索引呢,下面這種方式不行么:
1 <select th:field="*{rows[rowStat.index].variety}"> 2 ... 3 </select>
嗯事實上,是不行的,他的問題是SpringEL表達式不執行數值中括號里邊的表達式變量,索引執行上邊的語句時,會得到一個錯誤的結果,就是字面形式的 row[rowStat.index] (而不是 row[0],row[1] )而不是行集合中的正確位置,這就是為什么在這里需要預處理。
讓我們看看產生的html后按"添加行"按鈕幾次:
1 <tbody> 2 <tr> 3 <td>1</td> 4 <td> 5 <select id="rows0.variety" name="rows[0].variety"> 6 <option selected="selected" value="1">Thymus vulgaris</option> 7 <option value="2">Thymus x citriodorus</option> 8 <option value="3">Thymus herba-barona</option> 9 <option value="4">Thymus pseudolaginosus</option> 10 <option value="5">Thymus serpyllum</option> 11 </select> 12 </td> 13 <td> 14 <input id="rows0.seedsPerCell" name="rows[0].seedsPerCell" type="text" value="" /> 15 </td> 16 <td> 17 <button name="removeRow" type="submit" value="0">Remove row</button> 18 </td> 19 </tr> 20 <tr> 21 <td>2</td> 22 <td> 23 <select id="rows1.variety" name="rows[1].variety"> 24 <option selected="selected" value="1">Thymus vulgaris</option> 25 <option value="2">Thymus x citriodorus</option> 26 <option value="3">Thymus herba-barona</option> 27 <option value="4">Thymus pseudolaginosus</option> 28 <option value="5">Thymus serpyllum</option> 29 </select> 30 </td> 31 <td> 32 <input id="rows1.seedsPerCell" name="rows[1].seedsPerCell" type="text" value="" /> 33 </td> 34 <td> 35 <button name="removeRow" type="submit" value="1">Remove row</button> 36 </td> 37 </tr> 38 </tbody>
驗證和錯誤信息
讓我們看看當有錯誤的時候如何給一個表單域一個CSS類:
1 <input type="text" th:field="*{datePlanted}" 2 th:class="${#fields.hasErrors('datePlanted')}? fieldError" />
可以看到, #fields.hasErrors(...) 函數接受一個表達式參數(datePlanted),返回一個布爾值告訴field該字段是否有驗證錯誤。
我們可以根據他們各自的field獲取所有的錯誤:
1 <ul> 2 <li th:each="err : ${#fields.errors('datePlanted')}" th:text="${err}" /> 3 </ul>
通過迭代,我們可以使用 th:errors ,一個專門用於創建一個通過制定選擇器篩選的錯誤列表的屬性,通過 分隔。
1 <input type="text" th:field="*{datePlanted}" /> 2 <p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p>
簡單錯誤基礎css樣式,th:errorclass
在上邊的例子中,如果字段有錯誤,將為表單的input域設置一個css類,因為這種方式很常見,Thymeleaf提供了一個特定的屬性為 th:errorclass
應用於form域的標簽(input,select,textarea等),它將從現有的name屬性或th:field屬性字段的名詞相同的屬性,如果發生錯誤,則將制定的css類追加到標簽中。
<input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" />
如果datePlanted發生錯誤,則:
<input type="text" id="datePlanted" name="datePlanted" value="2013-01-01" class="small fieldError" />
全部錯誤
如果我們想要在form中顯示所有的錯誤呢?我們只需要通過'*'或'all'(等價)來查詢 #field.hasErrors(...) 方法和 #field.errors(...) 方法:
1 <ul th:if="${#fields.hasErrors('*')}"> 2 <li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li> 3 </ul>
在上邊的例子中,我們得到所有的錯誤並迭代他們:
1 <ul> 2 <li th:each="err : ${#fields.errors('*')}" th:text="${err}" /> 3 </ul>
建立一個以<Enter>分隔的列表:
1 <p th:if="${#fields.hasErrors('all')}" th:errors="*{all}">Incorrect date</p>
最后,注意 #field.hasErrors("") 等效的屬性 #fields.hasAnyErrors() 和 #fields.errors() 的等效的 #fields.allErrors() ,可以使用喜歡的任何語法。
1 <div th:if="${#fields.hasAnyErrors()}"> 2 <p th:each="err : ${#fields.allErrors()}" th:text="${err}">...</p> 3 </div>
全局錯誤
Spring表單還有一種錯誤,全局錯誤,都是些不與窗體的任何特定字段關聯的錯誤。
Thymeleaf提供了一個global的常量來訪問這些錯誤。
1 <ul th:if="${#fields.hasErrors('global')}"> 2 <li th:each="err : ${#fields.errors('global')}" th:text="${err}">Input is incorrect</li> 3 </ul>
Incorrect date以及等效的 #field.hasGlobalErrors() 和 #field.globalErrors() 方法。
1 <div th:if="${#fields.hasGlobalErrors()}"> 2 <p th:each="err : ${#fields.globalErrors()}" th:text="${err}">...</p> 3 </div>
在表單外部顯示錯誤
表單驗證錯誤也可以在表單外部顯示,方法是通過變量(即${...})的內部選擇變量(*{...})增加表單bean的名字作為前綴的方式。
1 <form> 2 <div th:errors="${myForm}">...</div> 3 <div th:errors="${myForm.date}">...</div> 4 <div th:errors="${myForm.*}">...</div> 5 <div th:if="${#fields.hasErrors('${myForm}')}">...</div> 6 <div th:if="${#fields.hasErrors('${myForm.date}')}">...</div> 7 <div th:if="${#fields.hasErrors('${myForm.*}')}">...</div> 8 <form th:object="${myForm}"> 9 ... 10 </form>
富錯誤對象
Thymeleaf提供了以bean的形式(代替單純的String)提供錯誤信息的能力,包括fieldName(String),message(String),和global(String)屬性的錯誤。這些錯誤可以通過工具方法#fields.datailedErrors()來實現:
1 <ul> 2 <li th:each="e : ${#fields.detailedErrors()}" th:class="${e.global}? globalerr : fielderr"> 3 <span th:text="${e.global}? '*' : ${e.fieldName}">The field name</span> | 4 <span th:text="${e.message}">The error message</span> 5 </li> 6 </ul>
轉換服務
配置
就像前文所說,Thymeleaf可以在上下文中注冊一個轉換服務,再次看一下他的配置信息
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans ...> 3 ... 4 <mvc:annotation-driven conversion-service="conversionService" /> 5 ... 6 <!-- **************************************************************** --> 7 <!-- CONVERSION SERVICE --> 8 <!-- Standard Spring formatting-enabled implementation --> 9 <!-- **************************************************************** --> 10 <bean id="conversionService" 11 class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> 12 <property name="formatters"> 13 <set> 14 <bean class="thymeleafexamples.stsm.web.conversion.VarietyFormatter" /> 15 <bean class="thymeleafexamples.stsm.web.conversion.DateFormatter" /> 16 </set> 17 </property> 18 </bean> 19 ... 20 </beans>
${{...}}語法
轉換服務可以通過 ${{...}} 語法很輕松的實現對象到字符串的轉換或格式化:
變量語法 ${{...}}
選擇變量語法 *{{...}}
例如,將一個Integer型轉換為字符串類型,並通過逗號來分隔:
1 <p th:text="${val}">...</p> 2 <p th:text="${{val}}">...</p>
返回結果為:
1 <p>1234567890</p> 2 <p>1,234,567,890</p>
表單中使用
我們之前看到的每一個th:field屬性都將始終使用轉換服務:
<input type="text" th:field="*{datePlanted}" />
等效於:
<input type="text" th:field="*{{datePlanted}}" />
注意這是唯一一種在表達式中使用單大括號的轉換服務。
#conversions工具對象
conversions工具對象表達式允許手動執行轉換服務:
<p th:text="${'Val: ' + #conversions.convert(val,'String')}">...</p>
工具對象表達式的語法為:
conversions.convert(Object,Class) :將對象轉換為指定的類
conversions.convert(Object,String) :和上邊相同,但是指定的目標為String類(java.lang包名可以省略)
渲染片段模板
Thymeleaf提供了將一個模板只渲染一部分,並作為一個片段返回的能力。
這是一個非常有用的組件化工具,比如,它可以用於執行AJAX的Controller的調用,用於在已經加載的瀏覽器中返回一個片段標簽(如用於更新選擇,啟用禁用按鈕等)。
片段渲染可以使用Thymeleaf的片段規范:一個實現了 org.thymeleaf.fragment.IFragmentSpec 接口的對象。
最常用的一個實現是 org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec 類,它允許一個片段規范包括之前說過的th:insert,th:replace使用DOM選擇器。
在視圖bean中指定片段
視圖bean是在應用程序上下文中聲明的 org.thymeleaf.spring4.view.ThymeleafView 的bean,它允許這樣定義一個片段:
1 <bean name="content-part" class="org.thymeleaf.spring4.view.ThymeleafView"> 2 <property name="templateName" value="index" /> 3 <property name="fragmentSpec"> 4 <bean 5 class="org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec" 6 c:selectorExpression="content" /> 7 </property> 8 </bean>
通過上邊的bean的定義,如果controller返回一個content-part(bean的名字),
1 @RequestMapping("/showContentPart") 2 public String showContentPart() { 3 ... 4 return "content-part"; 5 }
Thymeleaf將只返回index模板的content片段。一旦前綴后綴都設置並匹配,那么它可能為/WEB-INF/templates/index.html,
1 <!DOCTYPE html> 2 <html> 3 ... 4 <body> 5 ... 6 <div th:fragment="content"> 7 只有這里渲染!! 8 </div> 9 ... 10 </body> 11 </html>
另外應該注意到,因為Thymeleaf可以使用DOM選擇器,所有我們可以不用任何 th:fragment 屬性,而只用id屬性來選擇一個片段,如:
1 <bean name="content-part" class="org.thymeleaf.spring4.view.ThymeleafView"> 2 <property name="fragmentSpec"> 3 <bean class="org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec" 4 c:selectorExpression="#content" /> 5 </property> 6 <property name="templateName" value="index" /> 7 </bean>
同樣完美的適用:
1 <!DOCTYPE html> 2 <html> 3 ... 4 <body> 5 ... 6 <div id="content"> 7 只有這里渲染!! 8 </div> 9 ... 10 </body> 11 </html>
通過控制權的返回值指定片段
不聲明一個視圖bean,可以從控制器自己就可以使用與片段相同的語法,類似於th:insert,th:rplace屬性等,如:
1 @RequestMapping("/showContentPart") 2 public String showContentPart() { 3 ... 4 return "index :: content"; 5 }
當然,同樣可以使用基於DOM選擇器的功能,所有我們也可以是選擇使用基於標准的HTML屬性,如id="content"
1 @RequestMapping("/showContentPart") 2 public String showContentPart() { 3 ... 4 return "index :: #content"; 5 }
也可以使用參數:
1 @RequestMapping("/showContentPart") 2 public String showContentPart() { 3 ... 4 return "index :: #content ('myvalue')"; 5 }
與RequestDataValueProcessor集成
現在Thymeleaf無縫的與Spring的RequestDataValueProcessor接口集成,這個接口允許攔截鏈接URLS,表達URLS和表達域的值,以及為了啟用安全,如抵御CSRF而自動透明的添加一些隱藏域。
在應用的上下文中可以簡單的配置RequestDataValueProcessor:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> 6 7 ... 8 9 <bean name="requestDataValueProcessor" 10 class="net.example.requestdata.processor.MyRequestDataValueProcessor" /> 11 12 </beans>
Thymeleaf將通過這種方式使用它:
在渲染URL之前,th:href和th:src將會調用 RequestDataValueProcessor.processUrl(...)
在渲染表單的action屬性之前,th:action會調用 RequestDataValueProcessor.processAction(...) ,另外他會檢查標簽,因為一般來說這是使用action的唯一一個地方,並且在的關閉標簽之前執行 RequstDataValueProcessor.getExtraHiddenFields(...) 用來新增返回的hidden域。
在渲染value屬性之前,th:value會調用 RequestDataProcessor.processFormFieldValue(...) ,除非在這個標簽中存在了th:field(這時候th:field屬性起作用)
當存在th:field的時候,在渲染value屬性之前會調用 RequestDataValueProcessor.processFormFieldValue(...) 處理這個屬性值(<textarea>處理內容值)
此功能只有Spring3.x以后使用
綁定地址到Controller
在Spring4.1之后的版本中,Spring允許通過注解直接從從視圖鏈接到控制器,而不需要知道這些控制器映射的URI.
在Thymeleaf中可以通過 #mvc.url(...) 表達式方法調用Controller中符合駝峰命名規則的方法(get,set),調用的方式為方法的名字,即相當於jsp的spring:mvcUrl(...)自定義方法。
比如
1 public class ExampleController { 2 @RequestMapping("/data") 3 public String getData(Model model) { ... return "template" } 4 @RequestMapping("/data") 5 public String getDataParam(@RequestParam String type) { 6 ... 7 return "template" } 8 }
下邊是一個鏈接到它的方法:
1 <a th:href="${(#mvc.url('EC#getData')).build()}">獲取Data參數</a> 2 <a th:href="${(#mvc.url('EC#getDataParam').arg(0,'internal')).build()}">獲取Data參數</a>
Spring WebFlow的集成
基礎配置
Thymeleaf-spring4集成包包括與Spring WebFlow 2.3.x的集成
WebFlow包括當特定的事件(過渡)被觸發時渲染頁面片段的一些Ajax的功能,未來讓Thymeleaf參加這些Ajax請求,我們將使用一個不通過的視圖解析器的實現,它這樣配置:
1 <bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.AjaxThymeleafViewResolver"> 2 <property name="viewClass" value="org.thymeleaf.spring4.view.FlowAjaxThymeleafView" /> 3 <property name="templateEngine" ref="templateEngine" /> 4 </bean>
然后在ViewResolver中配置WebFlow的 ViewFactoryCreator .
1 <bean id="mvcViewFactoryCreator" 2 class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"> 3 <property name="viewResolvers" ref="thymeleafViewResolver"/> 4 </bean>
在這里可以指定模板的視圖狀態
1 <view-state id="detail" view="bookingDetail"> 2 ... 3 </view-state>
在上邊的實例中,bookingDetail是Thymeleaf模板通常使用的一個方式,是模板引擎內任何模板解析器都可以懂的
Ajax片段
WebFlow的片段規范允許片段通過標簽呈現,就像這樣:
1 <view-state id="detail" view="bookingDetail"> 2 <transition on="updateData"> 3 <render fragments="hoteldata"/> 4 </transition> 5 </view-state>
這些片段(即hoteldata)可以是逗號分隔的列表標記在th:fragment標簽中。
1 <div id="data" th:fragment="hoteldata">
這里內容替換</div>
永遠記住,指定的片段必須有一個id屬性,這樣瀏覽器運行的SpringJavaScript庫才能對標簽進行替換。
標簽,也可以通過DOM選擇器設定:
1 <view-state id="detail" view="bookingDetail"> 2 <transition on="updateData"> 3 <render fragments="[//div[@id='data']]"/> 4 </transition> 5 </view-state>
這將意味着th:fragment不在需要:
1 <div id="data"> 2 This is a content to be changed 3 </div>
而出發updateData后轉換的代碼:
1 <script type="text/javascript" th:src="@{/resources/dojo/dojo.js}"></script> 2 <script type="text/javascript" th:src="@{/resources/spring/Spring.js}"></script> 3 <script type="text/javascript" th:src="@{/resources/spring/Spring-Dojo.js}"></script> 4 5 ... 6 7 <form id="triggerform" method="post" action=""> 8 <input type="submit" id="doUpdate" name="_eventId_updateData" value="Update now!" /> 9 </form> 10 11 <script type="text/javascript"> 12 Spring.addDecoration( 13 new Spring.AjaxEventDecoration({ 14 formId:'triggerform', 15 elementId:'doUpdate', 16 event:'onclick' 17 })); 18 </script>
轉載: