Thymeleafを使用すると、データベース等から取得したデータを表示する動的なサイトを簡単に作ることができます。今回はデータをhtmlのテキストや属性に設定するときに使用するThymeleafの 変数式 について紹介します。
開発環境
・Spring Boot:3.5.0
・Java:17
変数式について
まず変数式とは何か、そしてどんなメリットがあるのかを説明します。
変数式は単純式のひとつ
変数式はThymeleafで用意されている 単純式(Simple Expressions) の一つです。
単純式はhtmlの属性に値やオブジェクトを渡す際に使用する式で、変数式以外に下記のものが用意されています。
- 選択変数式(Selection Variable Expressions)
- メッセージ式(Message Expressions)
- リンクURL式(Link URL Expressions)
- フラグメント式(Fragment Expressions)
これらについては別の記事にて説明しています。
変数式のメリット
変数式を用いると、modelに格納した値をhtmlのテキストや各属性に動的かつ簡潔に設定できます。
JavaScriptを使用しても実現は可能ですが、変数式を用いるとhtmlだけで記載が完結するためコードがシンプルでわかりやすくなります。
実際にpタグのテキストを変更するコードで比較してみましょう。
<p id="fruit">apple</p>
const p = document.getElementById('fruit');
p.textContent = 'orange';
JavaScriptではgetElementByIdにて取得したpタグにtextContentで新たな値orangeを設定しています。
<!--/* 文字列'orange'が設定された fruit が Model に格納されている場合 */-->
<p th:text="${fruit}">apple</p>
Thymeleafの実装はコメント行を除いた1行で記載が完了しており、テキストにfruitの値が設定されるということが理解しやすいと思います。
変数式の記載方法
基本
変数式は${fruit}のように先頭に$をつけて取得する対象を{}で囲むように記載します。
また変数式はThymeleafで用意されている独自の属性に渡すことで効果を発揮します。その属性を使用できるようにするには下記の2行目のようにxmlネームスペース(xmlns)を設定する必要があります。
使用できる属性はたくさんありますが、今回は一番シンプルなtextという属性を使用します。textは画面に表示する値を渡したいときに使用する属性で、pタグではコンテンツ部分に相当します。
<!--/* prefixには"th"がよく使用される */-->
<html xmlns:th="http://www.thymeleaf.org" >
<!--/* headタグやbodyタグは省略 */-->
<!--/* 基本の記載方法 prefix:属性="${変数名}" */-->
<p th:text="${fruit}"></p>
また取得する対象の記載方法は変数の構造(リストやマップ等)によって変わってきます。以降ではそんな各変数に対する記載方法を説明します。(以降、xmlnsやhead、bodyタグは省略)
文字列や数値
変数に文字列や数値がそのまま格納されている場合は基本の記載方法でアクセスが可能です。
Java側からfruitとpriceという変数名をmodelに設定してhtmlに渡しています。
// modelは「org.springframework.ui.Model」
model.addAttribute("fruit", "orange");
model.addAttribute("price", 100);
<p th:text="${fruit}"></p>
<p th:text="${price}"></p>
〇結果
リストの各要素
変数がリストになっている場合、変数名[インデックス]で各要素にアクセスが可能です。
Java側からnameListとpriceListリスト変数をmodelに設定してhtmlに渡しています。
// nameList、priceListは「java.util.List」
nameList.add("melon");
priceList.add(600);
nameList.add("water melon");
priceList.add(500);
// modelは「org.springframework.ui.Model」
model.addAttribute("nameList", nameList);
model.addAttribute("priceList", priceList);
<p th:text="${nameList[0]}"></p>
<p th:text="${priceList[0]}"></p>
<p th:text="${nameList[1]}"></p>
<p th:text="${priceList[1]}"></p>
〇結果
マップの各値
変数がマップになっている場合、変数名.キーで各値にアクセスが可能です。
Java側からfruitとpriceというキーを持ったマップ変数fruitMapをmodelに設定してhtmlに渡しています。
// mapは「java.util.Map」
map.put("fruit", "banana");
map.put("price", 90);
// modelは「org.springframework.ui.Model」
model.addAttribute("fruitMap", map);
<p th:text="${fruitMap.fruit}"></p>
<p th:text="${fruitMap.price}"></p>
〇結果
また変数名[キー]という記載でもアクセス可能です。そのため下記の記載でも同じ結果が出力されます。
<p th:text="${fruitMap[fruit]}"></p>
<p th:text="${fruitMap[price]}"></p>
リストに格納されているリストの各要素
リストの中に格納されたリストの各要素にアクセスしたい場合、${変数名[インデックス][インデックス]}と記載します。
Java側からlist1の最初と2番目の要素にそれぞれlist1_1、list1_2を格納し、modelに設定してhtmlに渡しています。
// list1、list1_1、list1_2は「java.util.List」
list1_1.add("grape");
list1_1.add("muscat");
list1_2.add("apple");
list1_2.add("pear");
list1.add(list1_1);
list1.add(list1_2);
// modelは「org.springframework.ui.Model」
model.addAttribute("list", list1);
<!--/* list[n]でリスト変数を取得し、その変数にインデックスを使用して各要素にアクセス */-->
<p th:text="${list[0][0]}"></p>
<p th:text="${list[0][1]}"></p>
<p th:text="${list[1][0]}"></p>
<p th:text="${list[1][1]}"></p>
〇結果
リストに格納されているマップの各値
リストの中に格納されたマップの各値にアクセスしたい場合、${変数名[インデックス].キー}(もしくは${変数名[インデックス][キー]})と記載します。
org.springframework.jdbc.core.JdbcTemplateを使う際に、データベースからこの構造でレコードを取得することが多いため覚えておくと便利です。
Java側からlistの最初と2番目の要素にそれぞれfruitMap、vegetableMapを格納し、modelに設定してhtmlに渡しています。
// listは「java.util.List」
// fruitMap、vegetableMapは「java.util.Map」
fruitMap.put("fruit", "strawberry");
fruitMap.put("price", 180);
vegetableMap.put("vegetable", "carrot");
vegetableMap.put("price", 120);
list.add(fruitMap);
list.add(vegetableMap);
// modelは「org.springframework.ui.Model」
model.addAttribute("list", list);
<!--/* list[n]でマップ変数を取得し、その変数のキー'fruit'、'vegetable'、'price'を使用してアクセス */-->
<p th:text="${list[0].fruit}"></p>
<p th:text="${list[0].price}"></p>
<p th:text="${list[1].vegetable}"></p>
<p th:text="${list[1].price}"></p>
〇結果
マップに格納されているリストの各要素
マップの中に格納されたリストの各要素にアクセスしたい場合、${変数名.キー[インデックス]}(もしくは${変数名[キー][インデックス]})と記載します。
Java側からmapにfruitListを格納し、modelに設定してhtmlに渡しています。
// fruitListは「java.util.List」
// mapは「java.util.Map」
fruitList.add("pineapple");
fruitList.add("mango");
map.put("list", fruitList);
// modelは「org.springframework.ui.Model」
model.addAttribute("map", map);
<!--/* map.listでリスト変数を取得し、その変数にインデックスを使用して各要素にアクセス */-->
<p th:text="${map.list[0]}"></p>
<p th:text="${map.list[1]}"></p>
〇結果
マップに格納されているマップの各値
マップの中に格納されたマップの各要素にアクセスしたい場合、${変数名.キー.キー}(もちろん、こちらも.キーを[キー]にしてもOK)と記載します。
Java側からmapにfruitMapを格納し、modelに設定してhtmlに渡しています。
// map、fruitMapは「java.util.Map」
fruitMap.put("fruit", "kiwi");
fruitMap.put("price", 200);
map.put("fruitMap", fruitMap);
// modelは「org.springframework.ui.Model」
model.addAttribute("map", map);
<!--/* map.fruitMapでマップ変数を取得し、その変数のキー'fruit'、'price'を使用してアクセスしている */-->
<p th:text="${map.fruitMap.fruit}"></p>
<p th:text="${map.fruitMap.price}"></p>
〇結果
オブジェクトのフィールド
オブジェクトのフィールドにアクセスする場合、そのオブジェクトに用意されたgetterメソッドを使用してアクセスします。ここで注意すべき点は${…}に記載する文字はフィールド名ではなくgetterメソッド名に合わせるということです。
以下のクラスFruitではフィールドにnameとpriceを用意していますが、フィールドとgetterメソッドどちらでアクセスしているかわかりやすいようにgetterメソッドの名前を少し変更しています。
またgetterメソッドを呼び出す際には、getterメソッド名から”get”を取り除いた文字列を使用します。
public class Fruit {
// フィールド
private String name;
private int price;
// nameのgetter
public String getFruitName() {
return name;
}
// nameのsetter
public void setFruitName(String name) {
this.name = name;
}
// priceのgetter
public int getFruitPrice() {
return price;
}
// priceのsetter
public void setFruitPrice(int price) {
this.price = price;
}
}
Fruit fruit = new Fruit();
fruit.setFruitName("lemon");
fruit.setFruitPrice(150);
model.addAttribute("fruit", fruit);
<!--/* getterメソッドの"get"を取り除いた文字列を使用する(最初の文字のみ小文字にしてもよい) */-->
<p th:text="${fruit.fruitName}"></p>
<p th:text="${fruit.fruitPrice}"></p>
(getterメソッド名と変数名が異なる場合に)変数名を記載したり、最初以外の文字列の小文字/大文字を変更してしまうとエラーとなってしまうため注意しましょう。
〇結果
インスタンスメソッドの戻り値
インスタンスメソッドの戻り値にアクセスする場合、そのメソッドをそのまま記載します。
public class Fruit {
// フィールド
private String name;
private int price;
// setter/getterは省略
public String getUpperCaseName() {
return name.toUpperCase();
}
public int calculatePrice(int num) {
return price * num;
}
}
Fruit fruit = new Fruit();
fruit.setFruitName("cherry");
fruit.setFruitPrice(200);
model.addAttribute("fruit", fruit);
<!--/* 引数なしのメソッド呼び出し */-->
<p th:text="${fruit.getUpperCaseName()}"></p>
<!--/* 引数ありのメソッド呼び出し */-->
<p th:text="${fruit.calculatePrice(3)}"></p>
○結果
まとめ
変数式は${…}を使用し、以下のルールで変数にアクセスします。
- 変数名を指定
- ${変数名}
- 変数名の後に[]を使用し、インデックスやキーを指定
- ${リスト[インデックス]}、${マップ[キー]}
- 変数名の後に.を使用し、マップのキーを指定
- ${マップ.キー}
- getterメソッド名を指定
- ${オブジェクト.getterメソッド名}※”get”の文字列を除いた文字列、
- インスタンスメソッドを指定
- ${オブジェクト.インスタンスメソッド()}
またマップやリストの複合型についてもアクセス方法を示しましたが、記載を細分化していけば上記のルールでアクセスが可能となります。
以上のことを踏まえれば、様々な構造の変数にアクセスが可能となります。動的なサイトを作成する際に非常に便利な機能なので、活用していきましょう!
コメント