Thymeleafのeachとindexの使い方を完全ガイド!初心者でもわかるループ処理の基本
新人
「Thymeleafでループ処理をしたいんですが、eachってどう使うんですか?」
先輩
「th:eachは、Thymeleafでリストや配列を繰り返し表示するときに使う属性だよ。」
新人
「なるほど、でもindexってのもよく見るんですけど、それは何ですか?」
先輩
「それはループの回数を取得するための機能だね。じゃあ基本から順番に見ていこう!」
1. Thymeleafとは?
Thymeleaf(タイムリーフ)は、HTMLテンプレートエンジンの一種で、Spring Frameworkと組み合わせてWebアプリケーションを開発する際によく使われます。Thymeleafを使うと、HTMLに直接データを埋め込んで、動的に表示することができます。
テンプレートファイルは.html形式で書かれ、実際のHTMLと同じように見える構造なので、デザイナーと開発者が共同作業しやすいのも特徴です。JSPに比べてモダンで見やすく、今ではSpring Bootと一緒に使われることが増えています。
今回はその中でも、リストなどを繰り返し表示する際に重要なth:eachの使い方と、インデックス(index)の取得方法について解説していきます。
2. th:each属性の役割と基本的な使い方
Thymeleafでは、配列やリストなどのコレクションをHTMLで繰り返し表示するために、th:eachという属性を使用します。これはJavaのfor文のような役割を持っており、非常に直感的に使えるのが特徴です。
例えば、Javaのコントローラーで以下のようなリストを渡していたとします:
@Controller
public class SampleController {
@GetMapping("/fruits")
public String showFruits(Model model) {
List<String> fruits = List.of("りんご", "バナナ", "みかん");
model.addAttribute("fruits", fruits);
return "fruits";
}
}
上記のfruitsリストをHTMLテンプレート上で繰り返し表示するには、次のように書きます:
<ul>
<li th:each="fruit : ${fruits}">
[[${fruit}]]
</li>
</ul>
th:each="fruit : ${fruits}"の意味は、fruitsリストの要素を1つずつ取り出して、fruitという変数で使うということです。これはJavaでいうところの次のようなループ処理に相当します:
for (String fruit : fruits) {
System.out.println(fruit);
}
このように、HTMLテンプレート内でループ処理を簡単に記述できるのがth:eachの魅力です。テンプレートがHTML構造を維持したまま動的にデータを表示できるので、初心者でも視覚的に理解しやすいです。
また、th:eachを使えば、テーブルやリストの中にたくさんのデータを表示する場合にも非常に便利です。繰り返し表示される要素に対応したHTML構造をそのまま記述することで、見た目を整えながら柔軟な表示が可能になります。
3. eachでインデックス番号を取得する方法(stat属性の説明)
Thymeleafのth:eachを使ってループ処理をするときに、「何番目の要素なのか」を知りたい場面があります。たとえば、連番を表示したいときや、奇数・偶数でスタイルを変えたいときなどです。
そんなときに活躍するのが、stat属性です。th:eachの中で次のように指定します:
<tr th:each="fruit, stat : ${fruits}">
<td>[[${stat.index}]]</td>
<td>[[${fruit}]]</td>
</tr>
このように書くことで、ループの状態情報をstatという名前で使えるようになります。上記の例ではstat.indexで現在のインデックス番号(0から始まる)を取得しています。
statは省略形ではなく、任意の名前にできます。たとえばiでもstatusでも構いません。
4. indexの使い方を具体的なコード例で解説
それでは実際に、インデックス番号を使って表形式で表示する例を紹介します。以下のようにコントローラでリストを渡しておきます:
@Controller
public class FruitController {
@GetMapping("/fruit-list")
public String showFruitList(Model model) {
List<String> fruits = List.of("りんご", "バナナ", "みかん", "ぶどう");
model.addAttribute("fruits", fruits);
return "fruit-list";
}
}
このリストをテンプレートで表として表示し、各行にインデックス番号を表示したい場合は、以下のように記述します:
<table border="1">
<thead>
<tr>
<th>番号</th>
<th>果物名</th>
</tr>
</thead>
<tbody>
<tr th:each="fruit, stat : ${fruits}">
<td>[[${stat.index}]]</td>
<td>[[${fruit}]]</td>
</tr>
</tbody>
</table>
stat.indexは0から始まるため、上記の表示では「0, 1, 2, 3」という番号になります。これはプログラミング的には正しいですが、ユーザーに見せるときは「1, 2, 3, 4」の方が自然ですね。その場合の対応方法は次で紹介します。
5. 0から始まるindexと1から始めたい場合の補足説明
Thymeleafのstat.indexは0から始まるのが標準ですが、人間の感覚では1から始まってほしいこともあります。たとえばランキングや表の連番などです。
そのような場合は、インデックスに+1をして表示すれば問題ありません。以下のように記述します:
<tr th:each="fruit, stat : ${fruits}">
<td>[[${stat.index + 1}]]</td>
<td>[[${fruit}]]</td>
</tr>
このように+1することで、「1, 2, 3...」と自然な連番表示になります。
なお、statで使えるプロパティはindexだけでなく、他にも色々あります。ここではよく使ういくつかを簡単に紹介しておきます。
- index:0から始まるインデックス
- count:1から始まる番号
- even / odd:偶数か奇数か(boolean)
- first / last:最初または最後の要素か(boolean)
たとえば偶数行と奇数行で背景色を変えたいときは、stat.evenやstat.oddを使って分岐させることもできます。以下のようにクラスを分けることができます:
<tr th:each="fruit, stat : ${fruits}"
th:class="${stat.odd} ? 'row-odd' : 'row-even'">
<td>[[${stat.count}]]</td>
<td>[[${fruit}]]</td>
</tr>
このように、th:eachとstat属性を組み合わせることで、ループ処理だけでなく表示の制御も柔軟にできるようになります。Thymeleafは、初心者でもわかりやすく、拡張性も高いため、JavaのWeb開発にはとてもおすすめです。
6. th:eachでindexを使って表示する具体的なユースケース
Thymeleafのeachとindexを使えば、連番付きのリストや表など、実務でもよくある表示を簡単に実装できます。ここでは、表に「番号」「名前」「備考」を表示する例を紹介します。
まず、Javaのコントローラで以下のようなサンプルデータを用意します:
@Controller
public class UserController {
@GetMapping("/users")
public String showUsers(Model model) {
List<String> users = List.of("田中", "佐藤", "鈴木", "高橋");
model.addAttribute("users", users);
return "user-list";
}
}
次に、HTMLテンプレート側では、インデックス番号を使って番号付きの表を表示します:
<table border="1">
<thead>
<tr>
<th>番号</th>
<th>名前</th>
<th>備考</th>
</tr>
</thead>
<tbody>
<tr th:each="user, stat : ${users}">
<td>[[${stat.index + 1}]]</td>
<td>[[${user}]]</td>
<td>-</td>
</tr>
</tbody>
</table>
stat.index + 1を使うことで、ユーザーには「1」から始まる自然な番号を見せることができます。こうした連番表示は、社員番号、注文リスト、テーブル一覧など、あらゆる業務アプリケーションで使われる基本パターンです。
7. よくある間違いとエラー例(indexが意図通りに動作しないケース)
初心者がth:eachとindexを使うときに、よくある間違いをいくつか紹介します。
① stat属性の指定を忘れる
以下のように書いてしまうと、stat.indexが未定義となってエラーになります。
<tr th:each="user : ${users}">
<td>[[${stat.index}]]</td>
<td>[[${user}]]</td>
</tr>
このような場合は、2つ目の変数(status変数)を必ず指定しましょう。
<tr th:each="user, stat : ${users}">
② indexとcountを混同する
indexは0から、countは1から始まります。これを知らずにindexを使った場合、表示が「0,1,2,3...」となり、ユーザーが違和感を覚えることもあります。
人が見る画面ではcountの方が自然です。どちらを使うか意図的に選ぶようにしましょう。
<td>[[${stat.count}]]</td> <!-- 1から始まる -->
<td>[[${stat.index}]]</td> <!-- 0から始まる -->
③ nullのリストでループしている
コントローラでリストを渡し忘れたり、nullになっていると、テンプレートでループ処理がうまく機能しません。必ずmodel.addAttribute()でリストを渡しているか確認してください。
8. index以外のstat属性の活用例
Thymeleafのeachに付随するstat属性では、index以外にも便利なプロパティが用意されています。それぞれの機能を使うことで、表示にバリエーションを持たせることができます。
① count(1から始まる番号)
ユーザー向けに連番を表示する場合は、stat.countを使うと便利です。+1の補正も不要なので、シンプルなコードになります。
<td>[[${stat.count}]]</td>
② even / odd(偶数・奇数判定)
行の背景色を交互に変えるようなUIを作りたいときに便利です。たとえば、以下のように書くと、行によってクラスを切り替えることができます。
<tr th:each="item, stat : ${items}"
th:class="${stat.odd} ? 'row-odd' : 'row-even'">
これにより、偶数行と奇数行で背景色を交互に表示できます。
③ first / last(最初・最後の判定)
リストの最初または最後の行だけに特別な装飾をしたいときに使えます。たとえば、最初の要素にだけ「新着」と表示したい場合は次のようにします:
<span th:if="${stat.first}">新着</span>
また、最後の要素で何か別の処理をしたい場合はstat.lastを活用します。
④ 複数の条件を組み合わせる
たとえば、「偶数行かつ最後の要素」だけにスタイルを適用したい場合は、th:classで複数の判定が可能です。
<tr th:each="item, stat : ${items}"
th:class="${stat.even} and ${stat.last} ? 'highlight' : ''">
このようにeachとstat属性を組み合わせることで、ループ処理に関する細かな制御が可能になります。index以外にも多彩なプロパティを活用することで、Thymeleafのテンプレートがさらに強力になります。