Thymeleafのth:attrappendの使い方と応用例を徹底解説|Springで動的属性を扱う基本
新人
「先輩、Thymeleafで属性を動的に追加したいんですが、th:attrappendってどう使うんですか?」
先輩
「良い質問だね。Thymeleafには属性を操作するための便利な構文がいくつかあるけど、th:attrappendはその中でも“既存の属性に値を追加する”ための機能なんだ。」
新人
「属性を追加するというのは、HTMLタグに新しいクラスや値を足していく感じですか?」
先輩
「そうそう。例えばボタンに動的なクラスを追加したり、data属性を結合するのにも使えるよ。では、まずThymeleaf自体の基本からおさらいしておこう。」
1. Thymeleafとは何か(テンプレートエンジンの概要)
Thymeleaf(タイムリーフ)は、Spring Frameworkでよく使われるテンプレートエンジンです。テンプレートエンジンとは、HTMLファイルの中に変数や条件分岐を埋め込み、サーバー側でデータを反映させて動的なページを生成する仕組みのことです。
例えば、コントローラで渡したデータをHTML内に埋め込みたい場合、Thymeleafのth:textやth:eachなどを使ってデータを表示できます。SpringとThymeleafを組み合わせることで、サーバーサイドレンダリング(SSR)による動的なWebページを簡単に構築できるのです。
開発環境の前提としては、pleiades + Gradle構成でSpringプロジェクトを作成し、@Controllerアノテーションを使ってコントローラを定義します。ここでは@RestControllerではなく、HTMLビューを返す形で進めます。
2. th:attrappendとは?(基本的な役割と使い方の概要)
th:attrappendは、Thymeleafの属性処理構文のひとつで、既に存在するHTML属性に対して値を追加(append)するために使います。
例えば、HTMLタグにすでにclass属性が指定されている場合、th:attrappendを使うと元の値を壊さずに新しいクラスを追加できます。これにより、動的にスタイルや状態を変更することが可能になります。
構文は次のようになります。
<button class="btn" th:attrappend="class=${active} ? ' btn-active' : ''">ボタン</button>
この例では、もし変数activeがtrueなら、既存のbtnにbtn-activeが追加されます。結果的に、出力HTMLでは次のようになります。
<button class="btn btn-active">ボタン</button>
このように、条件に応じて属性を動的に変更できるのがth:attrappendの大きな特徴です。
3. 基本的な構文の例(属性を追加・結合する仕組みの説明)
それでは、Springプロジェクトで実際にth:attrappendを使う例を見てみましょう。まず、コントローラで状態を渡します。
@Controller
public class SampleController {
@GetMapping("/sample")
public String show(Model model) {
model.addAttribute("active", true);
model.addAttribute("userRole", "admin");
return "sample";
}
}
そして、HTMLテンプレートsample.htmlでは次のように記述します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf th:attrappendの使い方</title>
</head>
<body>
<button class="btn" th:attrappend="class=${active} ? ' btn-active' : ''">状態ボタン</button>
<div class="role" th:attrappend="data-role=${userRole}">ユーザー情報</div>
</body>
</html>
このテンプレートをブラウザで確認すると、次のような出力が得られます。
<button class="btn btn-active">状態ボタン</button>
<div class="role" data-role="admin">ユーザー情報</div>
上記のように、th:attrappendを使うことで既存の属性を上書きせずに拡張できます。もしth:attrを使うと属性全体を置き換えてしまうため、既存の値を維持したい場合にはth:attrappendが最適です。
複数属性を同時に追加する
th:attrappendは、カンマ区切りで複数の属性を一度に操作できます。例えば、クラスとデータ属性を同時に追加することも可能です。
<a class="link" data-type="basic"
th:attrappend="class=' highlight', data-type='-user'">リンク</a>
出力結果は以下のようになります。
<a class="link highlight" data-type="basic-user">リンク</a>
このように、属性の値を結合してより複雑な出力を実現することができます。Springのプロジェクトで状態管理や動的なUIを実装するときには非常に便利です。
th:attrappendの応用例
例えば、管理者ユーザーだけに特定のCSSクラスを付与する場合や、複数の条件でボタンのスタイルを変えたい場合にも応用できます。
<button class="btn"
th:attrappend="class=${userRole == 'admin'} ? ' btn-danger' : ' btn-secondary'">操作</button>
上記の例では、変数userRoleがadminの場合、クラスにbtn-dangerが追加されます。これにより、Springアプリケーションで柔軟にUIの状態を制御できます。
4. th:attrappendの基本的な利用例(class属性・style属性などの動的結合)
ここからは、Thymeleafのth:attrappendを実際に使った基本的な利用例を紹介します。特に、class属性やstyle属性など、HTMLでよく使う要素に動的な値を結合するケースを見ていきましょう。
まずは、ボタンの状態によってクラスを変えるサンプルです。ボタンが「有効」かどうかでクラスを動的に付け替える場合にth:attrappendを使います。
<button class="btn" th:attrappend="class=${enabled} ? ' btn-success' : ' btn-disabled'">送信</button>
上記では、変数enabledがtrueであればbtn-successが追加され、falseの場合はbtn-disabledが付与されます。このようにして、ボタンの見た目を状態に応じて動的に変更することができます。
次に、style属性を動的に結合する例を見てみましょう。HTMLのインラインスタイルを条件に応じて変化させることも可能です。
<div style="background-color: lightgray;"
th:attrappend="style=${highlighted} ? ' border: 2px solid orange;' : ''">
強調表示エリア
</div>
このコードでは、highlightedがtrueのときに枠線スタイルを追加します。つまり、もともと指定されている背景色は維持されたまま、枠線のスタイルだけが後から結合される仕組みです。これにより、既存のスタイルを壊さずに新しい見た目を付与できるという点がth:attrappendの大きな利点です。
また、複数の条件でクラスとスタイルを同時に変えたい場合も、カンマで区切って書けます。
<div class="box" style="color: black;"
th:attrappend="class=${status == 'error'} ? ' text-danger' : '',
style=${status == 'error'} ? ' background-color: #ffeeee;' : ''">
メッセージ領域
</div>
このように書くことで、状態に応じた色変更や装飾を柔軟に実現できます。Spring MVCとThymeleafを組み合わせれば、動的なUI表現が容易になります。
5. th:attrappendを使う際の注意点(上書きとの違いや動的レンダリングの挙動)
th:attrappendを使う際には、いくつかの注意点を理解しておくことが大切です。特に、似た機能を持つth:attrとの違いを意識しましょう。
th:attrは、指定した属性の値を完全に上書きします。つまり、既存の値を保持せず新しい値だけに置き換える動作です。一方、th:attrappendは、既存の値の後ろに新しい文字列を「結合」します。
上書きと追加の違い
<!-- th:attrを使う場合(上書き) -->
<button class="btn" th:attr="class='btn-warning'">警告</button>
<!-- th:attrappendを使う場合(追加) -->
<button class="btn" th:attrappend="class=' btn-warning'">警告</button>
上記の例では、th:attrを使うと元々のclass="btn"が上書きされ、最終的に「btn-warning」だけが残ります。しかしth:attrappendなら、結果として「btn btn-warning」となり、元のクラスが維持されます。
もう一つの注意点は、ThymeleafがHTMLをサーバーサイドでレンダリングするタイミングです。Spring MVCでコントローラから値を渡す場合、Thymeleafはページ生成時に変数を評価してHTMLを組み立てます。そのため、フロントエンドでのリアルタイム操作(JavaScriptによる動的変更)とは異なり、サーバーで確定した値が出力される点を意識しましょう。
条件付き追加の罠
条件分岐において、空文字を返すケースではスペースの扱いにも注意が必要です。例えば、クラスの後ろに不要なスペースが残るとCSS指定が正しく動作しない場合があります。
<button class="btn" th:attrappend="class=${active} ? ' btn-on' : ''">ON/OFF</button>
この書き方では問題ありませんが、もし空文字の代わりにスペースや不正な文字が返ると、意図しないクラス名が適用される恐れがあります。テンプレートを書く際には、不要な空白を避けるようにしましょう。
最後に、th:attrappendは「追加する」という性質上、すでに同じ属性値が存在すると重複する可能性があります。たとえば、同じクラス名を複数回追加してしまうとCSSが二重指定されることがあるため、変数の管理にも気をつけてください。
6. @Controllerとの連携例(Springでのモデル値を使った属性追加)
ここからは、Spring MVCの@ControllerとThymeleafを連携させる実践例を紹介します。実際のアプリケーション開発では、サーバー側で条件を判定し、その値をテンプレートに渡してth:attrappendで属性を操作することがよくあります。
まず、Gradle+pleiades構成で作成したSpringプロジェクト内に、次のようなコントローラクラスを用意します。
@Controller
public class StatusController {
@GetMapping("/status")
public String showStatus(Model model) {
model.addAttribute("online", true);
model.addAttribute("theme", "dark");
model.addAttribute("alertLevel", "warning");
return "status";
}
}
このコントローラでは、3つの変数をモデルに追加しています。onlineはユーザーがオンライン状態かどうか、themeはサイトテーマ(darkやlight)、alertLevelは警告レベルを表します。
次に、対応するThymeleafテンプレートstatus.htmlを見てみましょう。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf th:attrappend 連携例</title>
</head>
<body>
<div class="user-status"
th:attrappend="class=${online} ? ' online' : ' offline'">
現在の接続状態
</div>
<div class="container"
th:attrappend="class=${theme == 'dark'} ? ' theme-dark' : ' theme-light'">
テーマ切り替え領域
</div>
<div class="alert-box"
th:attrappend="class=' alert-' + ${alertLevel}">
警告メッセージ
</div>
</body>
</html>
この例を実行すると、ブラウザには次のようなHTMLが出力されます。
<div class="user-status online">現在の接続状態</div>
<div class="container theme-dark">テーマ切り替え領域</div>
<div class="alert-box alert-warning">警告メッセージ</div>
このように、Spring MVCで渡したモデルの値をth:attrappendにバインドすることで、HTMLの属性を柔軟に制御できます。テンプレートエンジンとしてのThymeleafの強みは、Java側の状態をそのままHTMLに反映できる点にあります。
また、この仕組みを利用すれば、ユーザーの状態やテーマ設定に応じてスタイルや表示を動的に変更できるため、画面の再利用性が高まります。SpringとThymeleafを組み合わせることで、バックエンドのロジックとフロントエンドの表現を自然に連携させることができます。
特に、pleiadesを使った開発環境では、テンプレートのプレビューや自動補完機能がサポートされているため、初心者でも直感的に動作を確認しながら学習できます。Gradleでのビルドも軽量で扱いやすいため、Thymeleafの構文を実験的に試すには最適な構成です。
次のステップでは、これらの基本的な構文を発展させて、複雑なフォーム入力やリスト表示の中でth:attrappendを活用する方法へとつなげていくことができます。
7. th:attrappendの応用例(複数クラス・条件分岐・他属性との組み合わせ)
ここからは、Thymeleafのth:attrappendをより実践的に活用する応用例を見ていきます。単純なクラス追加だけでなく、条件分岐や複数の属性を組み合わせることで、柔軟な画面表現が可能になります。
新人
「先輩、ボタンの色や状態を複数条件で変えたい場合にもth:attrappendが使えるんですか?」
先輩
「もちろんだよ。複数の条件を組み合わせれば、状態に応じてスタイルを細かく制御できる。例えば管理者や一般ユーザー、ゲストなど、ユーザーの種類ごとにクラスを追加することもできるんだ。」
次のようなHTMLを考えてみましょう。
<div class="user-box"
th:attrappend="class=${userRole == 'admin'} ? ' role-admin' : '',
class=${userRole == 'guest'} ? ' role-guest' : '',
data-level=${loginCount > 10} ? ' advanced' : ' basic'">
ユーザー情報
</div>
この例では、userRoleがadminならrole-adminクラスを、guestならrole-guestクラスを追加します。また、loginCountの値によってデータ属性も変化します。
出力結果は次のようになります。
<div class="user-box role-admin" data-level="advanced">ユーザー情報</div>
このように複数条件を組み合わせると、ユーザーの状態や操作履歴に応じてUIを柔軟に変えることができます。Springのモデル値を利用すれば、サーバー側のロジックと連動して直感的に表現できるのがThymeleafの魅力です。
また、クラス属性だけでなく、titleやaria-labelなどの補助属性に情報を追加することも可能です。
<button class="info-btn" title="情報"
th:attrappend="title=${userRole == 'admin'} ? '(管理者用)' : '(閲覧専用)'">
詳細を表示
</button>
結果として、管理者の場合は「情報(管理者用)」というタイトルが表示され、アクセシビリティにも配慮したUIを簡単に構築できます。
8. よくある間違いとデバッグ方法(属性の上書き・意図しない結合など)
th:attrappendを使う際には、慣れないうちはいくつかの典型的な間違いをしやすいです。ここでは、初心者がつまずきやすいポイントと、その確認方法(デバッグ手法)を紹介します。
新人
「動的にクラスを追加したのに、ブラウザで見ると反映されていません。何が悪いんでしょうか?」
先輩
「ありがちなパターンだね。もしかするとth:attrとth:attrappendを混同してるかもしれない。あるいは、変数名のスペルミスかも。」
① th:attrとの混在
th:attrを同時に使うと、th:attrappendで追加した内容が上書きされることがあります。両方を同じ属性で使用しないように注意しましょう。
<!-- NG例 -->
<button th:attr="class='btn'" th:attrappend="class=' btn-warning'">警告</button>
このような書き方では、最終的にbtn-warningが消えてしまうことがあります。属性操作は1つの方法に統一するのが安全です。
② 空白の扱い
属性値にスペースを忘れると、クラス名が意図せず結合されることがあります。
<!-- NG例 -->
<button class="btn" th:attrappend="class='btn-warning'">警告</button>
<!-- 出力結果: class="btnbtn-warning" -->
このようにスペースを忘れると、クラスが1つの単語として扱われてしまいます。正しくは次のように書きます。
<!-- OK例 -->
<button class="btn" th:attrappend="class=' btn-warning'">警告</button>
③ デバッグのコツ
もし思った通りの結果が出ない場合は、ブラウザの「開発者ツール」で生成後のHTMLを確認しましょう。SpringとThymeleafはサーバーサイドでHTMLを生成するため、実際に出力されたHTMLを見ることで原因を特定できます。
また、Thymeleafはテンプレート構文のエラーをコンソールに出力するので、Eclipse(pleiades)のコンソールを確認するのも有効です。エラーメッセージに「EvaluationException」などが表示されている場合は、変数の評価に失敗している可能性があります。
9. 実践的なサンプル:フォーム入力値を使って属性を動的変更する例
最後に、th:attrappendをフォームの入力値と組み合わせる実践的な例を見てみましょう。ユーザーが入力したデータに応じて、ボタンのクラスやメッセージの色を変えるような場面で役立ちます。
新人
「入力内容によってボタンの色を変えるってどうやるんですか?」
先輩
「それなら、Springの@Controllerでフォーム入力を受け取り、その値をモデルに渡してth:attrappendで反映させればできるよ。」
Controllerの実装
@Controller
public class FormController {
@GetMapping("/form")
public String showForm(Model model) {
model.addAttribute("messageType", "");
return "form";
}
@PostMapping("/form")
public String submit(@RequestParam String messageType, Model model) {
model.addAttribute("messageType", messageType);
return "form";
}
}
HTMLテンプレート(form.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf th:attrappend フォーム連携例</title>
</head>
<body>
<form th:action="@{/form}" method="post">
<label>メッセージタイプを選択:</label>
<select name="messageType">
<option value="success">成功</option>
<option value="error">失敗</option>
<option value="info">情報</option>
</select>
<button type="submit">送信</button>
</form>
<div class="alert-box"
th:attrappend="class=${messageType == 'success'} ? ' alert-success' : '',
class=${messageType == 'error'} ? ' alert-danger' : '',
class=${messageType == 'info'} ? ' alert-info' : ''">
<span th:text="'現在の状態:' + ${messageType}"></span>
</div>
</body>
</html>
ユーザーが「成功」を選択して送信すると、class="alert-box alert-success"が生成されます。入力に応じてクラスを追加できるので、メッセージの色やデザインを自由に切り替えることが可能です。
このように、Spring MVCとThymeleafを組み合わせることで、サーバー側の状態をHTMLに反映しつつ、フォーム入力に基づいた動的なスタイル変更を簡単に実現できます。th:attrappendは静的HTMLを拡張して動的Webアプリを作るための強力なツールです。
新人
「なるほど!これならユーザーの操作に合わせてリアルタイムに見た目を変えられますね。」
先輩
「そうだね。これがThymeleafのth:attrappendを使う最大の魅力さ。Springとの相性も抜群だから、動的な属性制御をマスターすればWeb開発の幅が一気に広がるよ。」