0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

thymeleaf の fragment でハマったこと

Last updated at Posted at 2025-03-29

はじめに

自宅の趣味管理システムで thymeleaf を使っており、selectタグ を fragmentで共通化していました。

ある日、別のselectタグを追加したら意図しない動きになりハマりました。
パターンがわかったので、記事に残します。

同じようにハマった人がいたらご参考になれば幸いです。

環境

spring-boot-starter-thymeleaf 3.4.4

thymeleaf の fragment の前提知識

本家サイトの 8.1 テンプレートフラグメントのインクルード をご参照

何をやった?

同一ファイル内で th:fragment="select(引数) に加えて、th:fragment="selectTrainingMenu(引数) を追加しました。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Parts</title>
</head>
<body>
	<select
		th:fragment="select(selectMap, selectedKey, fieldName, hasBlank)"
		class="form-select" th:field="*{__${fieldName}__}">
		<option th:if="${hasBlank}" value=""></option>
        <option th:each="item : ${selectMap}" th:value="${item.key}"
            th:text="${item.value}" th:selected="${item.key == selectedKey}"></option>
	</select>
    
	<select
		th:fragment="selectTrainingMenu(selectList, selectedKey, fieldName, hasBlank)"
		class="form-select" th:field="*{__${fieldName}__}">
		<option th:if="${hasBlank}" value=""></option>
		<option th:each="item : ${selectList}" th:value="${item.key}" 
			th:data-parent-key="${item.parentKey}"
        	th:data-max-weight="${item.maxWeight}" 
			th:data-max-reps="${item.maxReps}" 
			th:data-max-sets="${item.maxSets}"     
		th:text="${item.value}" th:selected="${item.key == selectedKey}"></option>
	</select>

</body>
</html>

何が起こった?

「th:insert="~{parts/parts :: select(引数)」の読み込んでいる箇所で、select と selectTrainingMenuの2つのselectタグが表示されました。

意図としては、元の select だけの気持ちでした。

				<div class="col-sm-10"
					th:insert="~{parts/parts :: select(${trainingTargetAreaMap}, trainingAreaId, 'trainingAreaId', true)}">
				</div>

原因

HTMLタグと一致する名前を指定すると、HTMLタグで一致する fragmentを表示してしまうようでした。

select と書いたので、select と selectTrainingMenu の両方が一致してしまったと。

検証

SELECTタグとDIVタグで、前方一致、後方一致、部分一致、一致しない、HTMLタグと同じ。パターンを用意して試してみました。

GitHub

ソース

templates/parts.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8"></meta>
	<title>Fragment Sample</title>
</head>
<body>
	<h1>Fragment Sample</h1>
	<h2>SELECTタグ</h2>
		<div>前方一致:th:insert="~{parts :: select1}"
			<div th:insert="~{parts :: select1}"></div>
		</div>
		<div>後方一致:th:insert="~{parts :: 2select}"
			<div th:insert="~{parts :: 2select}"></div>	
		</div>
		<div>部分一致:th:insert="~{parts :: 3select3}"
			<div th:insert="~{parts :: 3select3}"></div>
		</div>
		<div>一致しない:th:insert="~{parts :: 4sel4}"
			<div th:insert="~{parts :: 4sel4}"></div>
		</div>	
		<div>th:insert="~{parts :: select}"
			<div th:insert="~{parts :: select}"></div>
		</div>
	<h2>DIVタグ</h2>
        <div>前方一致:th:insert="~{parts :: div1}"
            <div th:insert="~{parts :: div1}"></div>
        </div>
        <div>後方一致:th:insert="~{parts :: 2div}"
            <div th:insert="~{parts :: 2div}"></div>	
        </div>
        <div>部分一致:th:insert="~{parts :: 3div3}"
            <div th:insert="~{parts :: 3div3}"></div>
        </div>
        <div>一致しない:th:insert="~{parts :: 4div4}"
            <div th:insert="~{parts :: 4div4}"></div>
        </div>	
        <div>th:insert="~{parts :: div}"
            <div th:insert="~{parts :: div}"></div>
        </div>
<!--
	<h2>SPANタグ</h2>
	<div>th:insert="~{parts :: span}"
	    <div th:insert="~{parts :: span}"></div>
	</div>
-->
</body>
</html>

templates/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Parts</title>
</head>
<body>
	<select
		th:fragment="select1">
        <option value="1">select11</option>
        <option value="2">select12</option>
        <option value="3">select13</option>
	</select>
	<select
		th:fragment="2select">
	    <option value="1">2select1</option>
	    <option value="2">2select2</option>
	    <option value="3">2select3</option>
	</select>
	<select
		th:fragment="3select3">
	    <option value="1">3select31</option>
	    <option value="2">3select32</option>
	    <option value="3">3select33</option>
	</select>
	<select
		th:fragment="4sel4">
	    <option value="1">4sel41</option>
	    <option value="2">4sel42</option>
	    <option value="3">4sel43</option>
	</select>
	<select
        th:fragment="select">
        <option value="1">select1</option>
        <option value="2">select2</option>
        <option value="3">select3</option>
	</select>

	<div th:fragment="div1">div1</div>
	<div th:fragment="2div">2div</div>
	<div th:fragment="3div3">3div3</div>
	<div th:fragment="4div4">4div4</div>
	<div th:fragment="div">div</div>
</body>
</html>

結果

image.png

余談

templates/parts.html に存在しないHTMLタグ=SPANタグを書いてみたら解析エラーでした。

Caused by: org.attoparser.ParseException: Error resolving fragment: "~{parts :: span}": template or fragment could not be resolved (template: "index" - line 43, col 11)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?