thymeleaf 專題


 

Thymeleaf 是一款用於渲染 XML/XHTML/HTML5 內容的模板引擎。類似 JSP,Velocity,FreeMaker 等,它也可以輕易的與 Spring MVC 等 Web 框架進行集成作為 Web 應用的模板引擎。與其它模板引擎相比,Thymeleaf 最大的特點是能夠直接在瀏覽器中打開並正確顯示模板頁面,而不需要啟動整個 Web 應用。

Thymeleaf 初探

相比於其他的模板引擎,Thymeleaf 最大的特點是通過 HTML 的標簽屬性渲染標簽內容,以下是一個 Thymeleaf 模板例子:

 

這是一段標准的 HTML 代碼,這也就意味着通過瀏覽器直接打開它是可以正確解析它的結構並看到頁面的樣子。相比去其他的模板引擎在指定的位置通過${}等表達式進行渲染,Thymeleaf 則是一種針對 HTML/XML 定制的模板語言(當然它可以被擴展),它通過標簽中的th:text屬性來填充該標簽的一段內容。上例中,<p th:text="#{home.welcome}">Welcome to our grocery store!</p>意味着<p>標簽中的內容會被表達式#{home.welcome}的值所替代,無論模板中它的內容是什么,之所以在模板中 “多此一舉 “地填充它的內容,完全是為了它能夠作為原型在瀏覽器中直接顯示出來。

標准表達式語法

變量

Thymeleaf 模板引擎在進行模板渲染時,還會附帶一個 Context 存放進行模板渲染的變量,在模板中定義的表達式本質上就是從 Context 中獲取對應的變量的值:

假設today的值為2015年8月14日,那么渲染結果為:<p>Today is: 2015年8月14日.</p>。可見 Thymeleaf 的基本變量和 JSP 一樣,都使用${.}表示獲取變量的值。

URL

URL 在 Web 應用模板中占據着十分重要的地位,需要特別注意的是 Thymeleaf 對於 URL 的處理是通過語法@{...}來處理的。Thymeleaf 支持絕對路徑 URL: 

同時也能夠支持相對路徑 URL:

  • 當前頁面相對路徑 URL——user/login.html,通常不推薦這樣寫。
  • Context 相關 URL——/static/css/style.css

另外,如果需要 Thymeleaf 對 URL 進行渲染,那么務必使用th:hrefth:src等屬性,下面是一個例子

幾點說明:

  • 上例中 URL 最后的 (orderId=${o.id}) 表示將括號內的內容作為 URL 參數處理,該語法避免使用字符串拼接,大大提高了可讀性
  • @{...}表達式中可以通過{orderId}訪問 Context 中的orderId變量
  • @{/order}是 Context 相關的相對路徑,在渲染時會自動添加上當前 Web 應用的 Context 名字,假設 context 名字為 app,那么結果應該是/app/order

如果 URLs 寫在 style 的屬性里,那么

 

替換寫成 style 前面加 th: 鏈接的前后加上 + 號運算符,如下:

 

字符串替換

很多時候可能我們只需要對一大段文字中的某一處地方進行替換,可以通過字符串拼接操作完成:

一種更簡潔的方式是:

當然這種形式限制比較多,|...|中只能包含變量表達式${...},不能包含其他常量、條件表達式等。

運算符

在表達式中可以使用各類算術運算符,例如 +, -, *, /, %

邏輯運算符><<=,>===,!=都可以使用,唯一需要注意的是使用<,>時需要用它的 HTML 轉義符:

循環

渲染列表數據是一種非常常見的場景,例如現在有 n 條記錄需要渲染成一個表格<table>,該數據集合必須是可以遍歷的,使用th:each標簽:

可以看到,需要在被循環渲染的元素(這里是<tr>)中加入th:each標簽,其中th:each="prod : ${prods}"意味着對集合變量prods進行遍歷,循環變量是prod在循環體中可以通過表達式訪問。

條件求值

If/Unless

Thymeleaf 中使用th:ifth:unless屬性進行條件判斷,下面的例子中,<a>標簽只有在th:if中條件成立時才顯示:

th:unlessth:if恰好相反,只有表達式中的條件不成立,才會顯示其內容。

<a th:href="@{/login}" th:if=${session.user==null}>Login</a>

 

Switch

Thymeleaf 同樣支持多路選擇 Switch 結構:

默認屬性 default 可以用*表示:

 

Utilities

為了模板更加易用,Thymeleaf 還提供了一系列 Utility 對象(內置於 Context 中),可以通過#直接訪問:

  • #dates
  • #calendars
  • #numbers
  • #strings
  • arrays
  • lists
  • sets
  • maps

下面用一段代碼來舉例一些常用的方法:

#dates

 

 

#strings

 

 

頁面即原型

在 Web 開發過程中一個繞不開的話題就是前端工程師與后端工程師的協作,在傳統 Java Web 開發過程中,前端工程師和后端工程師一樣,也需要安裝一套完整的開發環境,然后各類 Java IDE 中修改模板、靜態資源文件,啟動 / 重啟 / 重新加載應用服務器,刷新頁面查看最終效果。

但實際上前端工程師的職責更多應該關注於頁面本身而非后端,使用 JSP,Velocity 等傳統的 Java 模板引擎很難做到這一點,因為它們必須在應用服務器中渲染完成后才能在瀏覽器中看到結果,而 Thymeleaf 從根本上顛覆了這一過程,通過屬性進行模板渲染不會引入任何新的瀏覽器不能識別的標簽,例如 JSP 中的<form:input>,不會在 Tag 內部寫表達式。整個頁面直接作為 HTML 文件用瀏覽器打開,幾乎就可以看到最終的效果,這大大解放了前端工程師的生產力,它們的最終交付物就是純的 HTML/CSS/JavaScript 文件。


Thymeleaf 之 內置對象、定義變量、URL參數及標簽自定義屬性

如標題所述,這篇文章主要講述Thymeleaf中的內置對象(list解析、日期格式化、數字格式化等)、定義變量、獲取URL的參數和在頁面標簽中自定義屬性的應用。

如果對Thymeleaf的基本使用、maven依賴等不清楚的可以先閱讀我的另一篇文章《Thymeleaf 之 初步使用》

  • Controller部份
@Controller public class IndexController { @GetMapping(value = "index") public String index(Model model, HttpServletRequest request) { List<String> datas = new ArrayList<String>(); datas.add("知識林"); datas.add("http://www.zslin.com"); datas.add("393156105"); model.addAttribute("datas", datas); model.addAttribute("curDate", new Date()); model.addAttribute("money", Math.random()*100); return "index"; } } 

在這個控制器的Model中存放了這樣幾個數據:一個String類型的列表、一個日期對象和一個數值,這些東西在實際應用開發過程中應用非常廣泛,下面具體看一下在Thymeleaf中是如何解析這些數據的。

  • 日期格式化
<span th:text="${#dates.format(curDate, 'yyyy-MM-dd HH:mm:ss')}"></span> 

說明: 使用內置對象datesformat函數即可對日期進行格式化,在format函數中,第一個參數是日期對象,對二兩個參數為日期格式(規則跟SimpleDateFormat一樣)

需要注意的是:

· 內置對象一般都以s結尾,如dateslistsnumbers

· 在使用內置對象是在對象名前都需要加#號。

  • 數字格式化
<span th:text="${#numbers.formatDecimal(money, 0, 2)}"></span> 

說明: 此示例表示保留兩位小數位,整數位自動;

<span th:text="${#numbers.formatDecimal(money, 3, 2)}"></span> 

說明: 此示例表示保留兩位小數位,3位整數位(不夠的前加0)

  • 獲取列表長度
<span th:text="${#lists.size(datas)}"></span> 

說明: 使用#lists.size來獲取List的長度。

  • 獲取URL參數值
<span th:text="${#httpServletRequest.getParameter('page')}"></span> 

說明: 當訪問http://localhost:1105/index?page=5時頁面將會得到page對應的值:5

  • 定義變量
<div th:with="curPage=${#httpServletRequest.getParameter('page')}"> <h3>當前頁碼:<span th:text="${curPage}"></span></h3> </div> 

說明: 同樣,當訪問http://localhost:1105/index?page=5時,頁面將顯示:當前頁碼:5,說明用th:with來定義變量,多個用,號隔開,使用范圍在當前標簽內。

  • 自定義標簽屬性

Thymeleaf中可以使用th:加上標簽的任何屬性進行賦值,但有些時候會遇到自定義的屬性,再用th:加自定義的屬性則會無效。比如:需要對<span>標簽增加objNameobjId這樣的屬性,在非Thymeleaf情況下是這樣:

<span objId="1" objName="知識林"></span> 

變量情況是:

<span objId="${obj.id}" objName="${obj.name}"></span> 

Thymeleaf下則是:

<span th:attr="myDate=${#dates.format(curDate, 'yyyy-MM-dd')}, myMoney=${money}"></span> 

說明: 在頁面上查看源代碼可以看到:<span myMoney="91.6059494319957" myDate="2016-31-02"></span>,說明自定義屬性用:th:attr,多個屬性用,隔開。

  • 內置對象

上面簡單描述了比較常用的dateslistsnumbers這幾個內置對象,在Thymeleaf還有很多的內置對象,像strings也非常常用,用法跟java.lang.String類的用法一樣。

Thymeleaf中的內置對象有:

#dates:日期格式化內置對象,具體方法可以參照java.util.Date

#calendars:類似於#dates,但是是java.util.Calendar類的方法;

#numbers: 數字格式化;

#strings:字符串格式化,具體方法可以參照java.lang.String,如startsWithcontains等;

#objects:參照java.lang.Object

#bools:判斷boolean類型的工具;

#arrays:數組操作的工具;

#lists:列表操作的工具,參照java.util.List

#sets:Set操作工具,參照java.util.Set

#maps:Map操作工具,參照java.util.Map

#aggregates:操作數組或集合的工具;

#messages:操作消息的工具。

thymeleaf+bootstrap,onclick傳參

<span title="刪除"
      th:onclick="'javascript:delCfmModel(\'/user/' + ${user.id} + '/delete\')'"
      style="cursor: pointer"
      class="glyphicon glyphicon-trash">
</span>

或 使用字符串拼接的另外一種簡潔寫法:<span th:text="|Welcome to our application, ${user.name}!|">

<span title="刪除"
    th:onclick="|javascript:delCfmModel('/sys/user/${user.id}/delete')|"
      style="cursor: pointer"
      class="glyphicon glyphicon-trash">
</span>



如果參數是字符串,需要加引號,如上圖所示。否則會報錯:Uncaught SyntaxError:Invalid regular expressiong flags

如果只傳數字,可以不加引號,如下圖所示:

<a href="#editModal"
   role="button"
   data-toggle="modal"
   th:onclick="'javascript:editUser('+${prod.id}+');'">
    Edit
</a>
<script>
    function editUser(id) {
        $.get("/projectName/user/edit",
                {objectid: id},
                function (data) {
                    $("#frm_container1").html(data);
                });
    }
</script>

https://yq.aliyun.com/articles/60696

 

<!-- 信息刪除確認 -->
<div class="modal fade" id="deleteModal">
    <div class="modal-dialog">
        <div class="modal-content message_align">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
                <h4 class="modal-title">Delete</h4>
            </div>
            <div class="modal-body" style="text-align: center">
                <p>
                    <i class="glyphicon glyphicon-alert"></i>
                    Do You Really Want to Delete This?
                </p>
            </div>
            <div class="modal-footer">
                <input type="hidden" id="modalUrl"/>
                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
                <a id="modalConfirmBtn" class="btn btn-success" data-dismiss="modal">Delete</a>
            </div>
        </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script type="text/javascript">

    function delCfmModel(url) {
        $('#modalUrl').val(url);//給會話中的隱藏屬性URL賦值
        $('#deleteModal').modal();
    }

    $("#modalConfirmBtn").click(function () {
        $.get($('#modalUrl').val(),function () {
            window.location.reload(); //重新加載頁面
        });
    });
</script>

 如果需要彈出的Modal窗口垂直居中,水平居中,可以添加以下css樣式(這個樣式是覆蓋了bootstrap的。tips,這個css是為上面的Modal div深度定制的,如需更改,需確定上面的html是否同步更改)

        .modal {
            text-align: center;
        }

        @media screen and (min-width: 768px) {
            .modal:before {
                display: inline-block;
                vertical-align: middle;
                content: " ";
                height: 100%;
            }
        }

        .modal-dialog {
            display: inline-block;
            text-align: left;
            vertical-align: middle;
        }

 

 



 

 

 

 

Standard Expression syntax

Most Thymeleaf attributes allow their values to be set as or containing expressions, 
which we will call Standard Expressions because of the dialects they are used in. These can be of five types:

${...} : Variable expressions.
*{...} : Selection expressions.
#{...} : Message (i18n) expressions.
@{...} : Link (URL) expressions.
~{...} : Fragment expressions.
2017-09-06 23:06:20.026  WARN 9824 --- [nio-8081-exec-1] n.n.u.t.expressions.ExpressionProcessor  : 
Fragment expression "layout/default" is being wrapped as a Thymeleaf 3 fragment expression (~{...}) for backwards compatibility purposes.  "
This wrapping will be dropped in the next major version of the expression processor, 
so please rewrite as a Thymeleaf 3 fragment expression to future-proof your code.  
See https://github.com/thymeleaf/thymeleaf/issues/451 for more information.

消除掉上面的WARN日志,

use:

<html lang="zh"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/default}">

replace

<html lang="zh"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="layout/default">

 

http://www.thymeleaf.org/doc/articles/standarddialect5minutes.html

 

Create a common layout to be used for several pages, defining extension points in the body with the layout:fragment/data-layout-fragment processors:

<!DOCTYPE html>
<html>
<head> <title>Layout page</title> <script src="common-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section layout:fragment="content"> <p>Page content goes here</p> </section> <footer> <p>My footer</p> <p layout:fragment="custom-footer">Custom footer here</p> </footer> </body> </html>

Create a content page that will use the layout, defining any HTML to use for the extension points in the layout, and specified by a layout:decorate/data-layout-decorate processor at the root element of your page:

<html layout:decorate="~{layout.html}"> <head> <title>Content page</title> <script src="content-script.js"></script> </head> <body> <section layout:fragment="content"> <p>This is a paragraph from the content page</p> </section> <footer> <p layout:fragment="custom-footer">This is some footer content from the content page</p> </footer> </body> </html>

Get Thymeleaf to process your content page. The result will be the layout template decorated by your content page, meaning that the content page will fill out the layout's extension points, replace titles, and merge <head> items:

<!DOCTYPE html>
<html>
<head> <title>Content page</title> <script src="common-script.js"></script> <script src="content-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section> <p>This is a paragraph from the content page</p> </section> <footer> <p>My footer</p> <p>This is some footer content from the content page</p> </footer> </body> </html>

Intrigued? Check out the documentation links near the top of this readme to learn more.

https://github.com/ultraq/thymeleaf-layout-dialect

 https://ultraq.github.io/thymeleaf-layout-dialect/

[Please make sure to select the branch corresponding to the version of Thymeleaf you are using]

Status

This is a thymeleaf extras module, not a part of the Thymeleaf core (and as such following its own versioning schema), but fully supported by the Thymeleaf team.

This repository contains two projects:

  • thymeleaf-extras-springsecurity3 for integration with Spring Security 3.x
  • thymeleaf-extras-springsecurity4 for integration with Spring Security 4.x

Current versions:

  • Version 3.0.2.RELEASE - for Thymeleaf 3.0 (requires Thymeleaf 3.0.3+)
  • Version 2.1.3.RELEASE - for Thymeleaf 2.1 (requires Thymeleaf 2.1.2+)

Features

This module provides a new dialect called org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect or org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect (depending on the Spring Security version), with default prefix sec. It includes:

  • New expression utility objects:
  • #authentication representing the Spring Security authentication object (an object implementing the org.springframework.security.core.Authentication interface).
  • #authorization: a expression utility object with methods for checking authorization based on expressions, URLs and Access Control Lists.
  • New attributes:
  • sec:authentication="prop" outputs a prop property of the authentication object, similar to the Spring Security <sec:authentication/> JSP tag.
  • sec:authorize="expr" or sec:authorize-expr="expr" renders the element children (tag content) if the authenticated user is authorized to see it according to the specified Spring Security expression.
  • sec:authorize-url="url" renders the element children (tag content) if the authenticated user is authorized to see the specified URL.
  • sec:authorize-acl="object :: permissions" renders the element children (tag content) if the authenticated user has the specified permissions on the specified domain object, according to Spring Source's Access Control List system.

Configuration

In order to use the thymeleaf-extras-springsecurity3 or thymeleaf-extras-springsecurity4 modules in our Spring MVC application, we will first need to configure our application in the usual way for Spring + Thymeleaf applications (TemplateEngine bean, template resolvers, etc.), and add the SpringSecurity dialect to our Template Engine so that we can use the sec:* attributes and special expression utility objects:

    <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
      ...
      <property name="additionalDialects">
        <set>
          <!-- Note the package would change to 'springsecurity3' if you are using that version -->
          <bean class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect"/>
        </set>
      </property>
      ...
    </bean>

And that's all!

Using the expression utility objects

The #authentication object can be easily used, like this:

    <div th:text="${#authentication.name}">
        The value of the "name" property of the authentication object should appear here.
    </div>

 

The #authorization object can be used in a similar way, normally in th:if or th:unlesstags:

    <div th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}">
        This will only be displayed if authenticated user has role ROLE_ADMIN.
    </div>

 

The #authorization object is an instance of org.thymeleaf.extras.springsecurity[3|4].auth.Authorization, see this class and its documentation to understand all the methods offered.

Using the attributes

Using the sec:authentication attribute is equivalent to using the #authentication object, but using its own attribute:

    <div sec:authentication="name">
        The value of the "name" property of the authentication object should appear here.
    </div>

 

The sec:authorize and sec:authorize-expr attributes are exactly the same. They work equivalently to a th:if that evaluated an #authorization.expression(...) expression, by evaluating a Spring Security Expression:

    <div sec:authorize="hasRole('ROLE_ADMIN')">
        This will only be displayed if authenticated user has role ROLE_ADMIN.
    </div>

 

 

These Spring Security Expressions in sec:authorize attributes are in fact Spring EL expressions evaluated on a SpringSecurity-specific root object containing methods such as hasRole(...)getPrincipal(), etc.

As with normal Spring EL expressions, Thymeleaf allows you to access a series of objects from them including the context variables map (the #vars object). In fact, you are allowed to surround your access expression with ${...} if it makes you feel more comfortable:

    <div sec:authorize="${hasRole(#vars.expectedRole)}">
        This will only be displayed if authenticated user has a role computed by the controller.
    </div>

 

Remember that Spring Security sets a special security-oriented object as expression root, which is why you would not be able to access the expectedRole variable directly in the above expression.

Another way of checking authorization is sec:authorize-url, which allows you to check whether a user is authorized to visit a specific URL or not:

    <div sec:authorize-url="/admin">
        This will only be displayed if authenticated user can call the "/admin" URL.
    </div>

 

For specifying a specific HTTP method, do:

    <div sec:authorize-url="POST /admin">
        This will only be displayed if authenticated user can call the "/admin" URL
        using the POST HTTP method.
    </div>

Finally, there is an attribute for checking authorization using Spring Security's Access Control Lists, which needs the specification of a domain object and the permissions defined on it that we are asking for.

    <div sec:authorize-acl="${obj} :: '1,3'">
        This will only be displayed if authenticated user has permissions "1" and "3"
        on domain object referenced by context variable "obj".
    </div>

In this attribute, both domain object and permission specifications are considered to be thymeleaf Standard Expressions.

Namespace

The namespace for both Spring 3 and 4 versions of this dialect is http://www.thymeleaf.org/extras/spring-security.

<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

Getting the namespace incorrect won't impact processing of your template. It might however impact your IDE when it comes to things like suggestions/auto-completion in your templates.

https://github.com/thymeleaf/thymeleaf-extras-springsecurity

 

 

https://github.com/ultraq/thymeleaf-layout-dialect

 

http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#including-template-fragments 

 

https://github.com/zsl131/thymeleaf-study

 

https://github.com/kolorobot/spring-boot-thymeleaf

http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-use-thymeleaf-3

 

https://github.com/spring-projects/spring-boot/blob/v1.4.1.RELEASE/spring-boot-samples/spring-boot-sample-web-thymeleaf3/src/main/resources/application.properties

http://blog.imyxiao.com/2016/11/26/Spring-Boot-thymeleaf-3/

https://github.com/kolorobot/spring-boot-thymeleaf

 在spring-boot1.4之后,支持thymeleaf3,可以更改版本號來進行修改支持. 
3相比2極大的提高了效率,並且不需要標簽閉合,類似的link,img等都有了很好的支持,按照如下配置即可

 

解決下面的問題的辦法是升級到Thymeleaf3

 

74.9 Use Thymeleaf 3

By default, spring-boot-starter-thymeleaf uses Thymeleaf 2.1. If you are using the spring-boot-starter-parent, you can use Thymeleaf 3 by overriding thethymeleaf.version and thymeleaf-layout-dialect.version properties, for example:

    <properties>
        <thymeleaf.version>3.0.7.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
 <thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity4.version>     </properties>

Tips:

如果不同時更改thymeleaf-extras-springsecurity4的version,則 thymeleaf spring security4相關的標簽會不生效

 

 
[Note]

if you are managing dependencies yourself, look at spring-boot-dependencies for the list of artifacts that are related to those two versions.

To avoid a warning message about the HTML 5 template mode being deprecated and the HTML template mode being used instead, you may also want to explicitly configure spring.thymeleaf.mode to be HTML, for example:

spring.thymeleaf.mode: HTML

Please refer to the Thymeleaf 3 sample to see this in action.

If you are using any of the other auto-configured Thymeleaf Extras (Spring Security, Data Attribute, or Java 8 Time) you should also override each of their versions to one that is compatible with Thymeleaf 3.0.

http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-use-thymeleaf-3

 

It's much simpler, just read this: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-use-thymeleaf-3

<properties>
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
</properties>

You might also want to add <thymeleaf-extras-java8time.version>3.0.0.RELEASE</thymeleaf‌​-extras-java8time.ve‌​rsion> – Samuel EUSTACHI Jun 6 at 9:42

https://stackoverflow.com/questions/37439369/spring-boot-and-thymeleaf-3-0-0-release-integration

http://blog.imyxiao.com/2016/11/26/Spring-Boot-thymeleaf-3/

 

http://www.thymeleaf.org/doc/articles/thymeleaf3migration.html

https://github.com/spring-projects/spring-boot/tree/v1.4.1.RELEASE/spring-boot-samples/spring-boot-sample-web-thymeleaf3

 

org.xml.sax.SAXParseException: 元素類型 "img" 必須由匹配的結束標記 "</img>" 終止。

 

 

1、必須要有結束標簽,這個很煩,不加就報錯,像下面這樣:

org.xml.sax.SAXParseException: 元素類型 "input" 必須由匹配的結束標記 "</input>" 終止。

org.xml.sax.SAXParseException: 元素類型 "img" 必須由匹配的結束標記 "</img>" 終止。

org.xml.sax.SAXParseException: 元素類型 "hr" 必須由匹配的結束標記 "</hr>" 終止。

org.xml.sax.SAXParseException: 元素類型 "br" 必須由匹配的結束標記 "</br>" 終止。

2、包含的模板,也必須有閉合標簽。我要引入公共的頭部和底部,頭部還好說,外面有個<head>包着,底部必須得在外面套個div,引入js的代碼也得包括在div里,不爽啊,像這樣:

<div class="container" th:fragment="footer">
    <hr></hr>
    <div>
      <p>AntsClub 2013-2015</p>
    </div>
    <script th:src="@{bootstrap334/js/jquery.min.js}"></script>
    <script th:src="@{bootstrap334/js/bootstrap.min.js}"></script>
</div>

3、引入個css文件,js文件也得用thymeleaf的標簽,就像上面的例子

 

如果你的代碼使用了 HTML5 的標准,而Thymeleaf 版本來停留在 2.x ,那么如果沒有把<input>閉合,如下:

<form>
 First name:<br>
<input type="text" name="firstname">
<br>
 Last name:<br>
<input type="text" name="lastname">
</form> 

就會拋出如下錯誤。

org.xml.sax.SAXParseException: 元素類型 "input" 必須由匹配的結束標記 "</input>" 終止。

解決方案

1. 沿用 Thymeleaf 老版本

如果你的 Thymeleaf 不能變更,那么你的 HTML 標准也只能停留在老版本了。你必須嚴格遵守 XML 定義,在<input>加上結束標記</input>。這顯然,對於 HTML5 不友好。

2. 升級至 Thymeleaf 3 新版本

是時候嘗試下使用 Thymeleaf 3 了。Thymeleaf 3 使用了新的解析系統。

Thymeleaf 3 不再是基於XML結構的。由於引入新的解析引擎,模板的內容格式不再需要嚴格遵守XML規范。即不在要求標簽閉合,屬性加引號等等。當然,出於易讀性考慮,還是推薦你按找XML的標准去編寫模板。

Thymeleaf 3 使用一個名為 AttoParser 2的新解析器。 一個新的、基於事件(不符合SAX標准)的解析器,AttoParser由 Thymeleaf 的作者開發,符合 Thymeleaf 的風格。

AttoParser 提供 Thymeleaf 3 兩個重要功能:

  • 完全支持XML和HTML5(非XML化)標記,從而不再需要外部標記平衡操作。
  • 無損解析,以便在處理的輸出的標記類似於具有最高精度的原始模板。

所以下面的格式在 Thymeleaf 3 里面是合法的:

<div><img alt=logo th:src='@{/images/logo.png}'>

Thymeleaf 3 其他方面的解析改進

1. 啟用驗證的解析

在 Thymeleaf 2.1提供了兩種VALID*模板模式,名為VALIDXHTMLVALIDXML,在而 Thymeleaf 3 中將不再存在。 新的解析基礎結構不提供HTML或XML驗證,即在解析期間無法驗證模板標記是否符合指定的DTD或XML模式定義。

2. 不再需要<![CDATA[ ... ]]>

Thymeleaf 2.1 要求將<script>標記的內容封裝在 CDATA 中,以便所使用的任何<>符號不會干擾基於XML的解析:

<script>
/*<![CDATA[*/
  var user = ...
  if (user.signupYear < 1990) {
    alert('You\'ve been here for a long time!');
  }
/*]]>*/
</script>

而在 Thymeleaf 3 中則不需要這樣做,代碼立馬變得簡潔干凈:

<script>
  var user = ...
  if (user.signupYear < 1990) {
    alert('You\'ve been here for a long time!');
  }
</script>

參考文獻

  • https://github.com/thymeleaf/thymeleaf/issues/390

https://waylau.com/thymeleaf-3-adopts-a-new-parsing-system/

 

THYMELEAF 3 - GET STARTED QUICKLY WITH THYMELEAF 3 AND SPRING MVC

 

Thymeleaf 3 release arrived. The new version brings plenty of new features like HTML5 support as well as Text templates support with no markup - [# th:utext="${thymeleaf.version}" /] , improved inline capabilities - <p>Thymeleaf [[${thymeleaf.version}]] is great!</p>, performence improvements and much more.

The easiest way the get starter with Thymeleaf 3 and Spring MVC is by using Spring MVC 4 Quickstart Maven Archetype. The archetype was updated to support Thymeleaf 3. The changes that are made to the archetype are described below.

Dependencies

The project uses Spring Platform BOM for dependencies management, but it does not yet (as time of writing this post) declare dependency on Thymeleaf 3, so I needed to declare the versions manually.

  • Thymeleaf:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.0.RELEASE</version> </dependency>
  • Thymeleaf Spring 4:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>3.0.0.RELEASE</version> </dependency>
  • Thymeleaf Spring Security 4:
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.0.RELEASE</version> </dependency>

The application generated with the archetype uses Java 8 Time Dialect and since Thymeleaf API changed, the dialect dependency must be updated too. Before it is available in Maven Central, we must add snapshot repository to POM:

<repository> <id>sonatype-nexus-snapshots</id> <name>Sonatype Nexus Snapshots</name> <url>https://oss.sonatype.org/content/repositories/snapshots</url> <snapshots> <enabled>true</enabled> </snapshots> </repository>

And then declare the dependency:

<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> <version>3.0.0-SNAPSHOT</version> </dependency>

Configuration changes

  • Template resolver

Template resolver before:

@Bean public TemplateResolver templateResolver() { TemplateResolver resolver = new ServletContextTemplateResolver(); resolver.setPrefix(VIEWS); resolver.setSuffix(".html"); resolver.setTemplateMode("HTML5"); resolver.setCacheable(false); return resolver; }

Template resolver after:

@Bean public ITemplateResolver templateResolver() { SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); resolver.setPrefix(VIEWS); resolver.setSuffix(".html"); resolver.setTemplateMode(TemplateMode.HTML); resolver.setCacheable(false); return resolver; }
  • Template Engine
@Bean public SpringTemplateEngine templateEngine() { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); templateEngine.addDialect(new SpringSecurityDialect()); templateEngine.addDialect(new Java8TimeDialect()); return templateEngine; }
  • View Resolver:
@Bean public ViewResolver viewResolver() { ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver(); thymeleafViewResolver.setTemplateEngine(templateEngine()); thymeleafViewResolver.setCharacterEncoding("UTF-8"); return thymeleafViewResolver; }

Templates

The templates did not change in this project. But if you are migrating a real project, you may be interested in reading migration guide.

References

You may be also interested in

http://blog.codeleak.pl/2016/05/thymeleaf-3-get-started-quickly-with.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM