Thymeleaf javascript 値渡しを簡単にするコツ|初心者でもできる動的連携
新人
「先輩、ThymeleafでJavaScriptに値を渡す方法がよくわからないんです。コントローラで用意したデータをスクリプトに渡すにはどうすればいいんですか?」
先輩
「いい質問だね。ThymeleafはサーバーサイドでHTMLを生成するテンプレートエンジンだから、JavaScriptとの連携も少し工夫が必要なんだ。今回はその仕組みとコツを説明しよう。」
新人
「お願いします!JavaScriptでサーバーの値を使えるようになると、動的なページが作れそうです!」
先輩
「そのとおり。Thymeleafでの値渡しを理解すれば、Springの@ControllerとJavaScriptを連携させて、よりリッチなWebアプリケーションが作れるようになるよ。」
1. Thymeleafとは?
Thymeleaf(タイムリーフ)は、Spring Frameworkでよく使われるテンプレートエンジンのひとつです。HTMLにサーバーのデータを埋め込むことができるため、Javaのコントローラから送られた値を動的に表示することができます。特に@Controllerを使うSpring MVCプロジェクトでのビュー層において、HTMLファイルをそのままブラウザで確認できる「自然テンプレート」として人気があります。
このテンプレートエンジンの大きな特徴は、「HTMLを壊さずに動的なデータを扱える」という点です。たとえば、ユーザー名やリストデータをページ内に表示する処理を簡単に記述できます。
Spring Bootではspring-boot-starter-thymeleafを依存関係に追加するだけで簡単に利用でき、pleiades環境でもチェックを入れるだけで自動的に設定されます。MavenではなくGradleで開発している場合でも、同様に依存関係を追加することで動作します。
2. JavaScriptとの連携が必要になるシーン
ThymeleafでHTMLを作成するだけでなく、JavaScriptと連携することで「動的な値渡し」が可能になります。たとえば、サーバーから渡されたリストデータをJavaScriptで処理して、グラフを描画したり、動的なDOM操作を行ったりすることができます。
しかし、初心者がよくつまずくのが「Thymeleaf変数をJavaScript内で使う方法」です。単純に${変数}と書いても、スクリプト内では正しく動作しないことがあります。これは、JavaScriptの文字列として解釈される前にThymeleafがサーバーサイドで置き換えを行うため、適切な書き方が必要になるからです。
Thymeleafでは、JavaScript内で値を扱うためにth:inline="javascript"という属性を使用します。この指定を行うことで、JavaScriptコード内でもサーバー側の変数を安全に展開できるようになります。
3. コントローラでの値の受け渡しとThymeleafでの表示
それでは、Springの@ControllerクラスからThymeleafへ値を渡す基本的な流れを見てみましょう。ここでは、pleiades+Gradle構成を前提としています。
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SampleController {
@GetMapping("/sample")
public String showPage(Model model) {
model.addAttribute("message", "ThymeleafとJavaScriptの連携成功!");
model.addAttribute("count", 5);
return "sample";
}
}
上記のようにModelオブジェクトに値を追加すると、Thymeleaf側で${message}や${count}として利用できるようになります。
次に、Thymeleafテンプレートでこれらの値をJavaScriptに渡す方法を見てみましょう。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf JavaScript 値渡し</title>
</head>
<body>
<h3 th:text="${message}">メッセージ表示</h3>
<script th:inline="javascript">
/*<![CDATA[*/
let message = [[${message}]];
let count = [[${count}]];
console.log("サーバーから受け取ったメッセージ:", message);
console.log("サーバーから受け取った数値:", count);
/*]]>*/
</script>
</body>
</html>
このように、[[${変数}]]という形式を使うことで、JavaScriptコードの中でもThymeleafの値を安全に展開できます。実際にブラウザの開発者ツール(コンソール)を開くと、サーバーから渡された値が確認できます。
サーバーから受け取ったメッセージ: ThymeleafとJavaScriptの連携成功!
サーバーから受け取った数値: 5
このように、コントローラで渡した値をJavaScriptへ渡すことで、フロントエンドでの動的な処理が可能になります。例えば、ユーザーの入力に応じて画面の内容を切り替えたり、非同期通信を行う前にサーバー側の設定値をJavaScriptに渡しておくこともできます。
次のステップでは、この仕組みを使ってリストデータやオブジェクトを渡す応用例について学んでいきましょう。
4. ThymeleafからJavaScriptへ値を渡す基本構文(th:inlineの使い方)
前回の例では、th:inline="javascript"を使って、サーバーサイドの変数をJavaScriptに渡しました。ここでは、その仕組みをもう少し詳しく理解していきましょう。Thymeleafでは通常のHTMLと同じように記述できますが、スクリプトの中でサーバーの値を扱う場合は特別な構文が必要です。
th:inline="javascript"を指定すると、ThymeleafがJavaScript向けの文法で値を出力できるようになります。これを設定しないと、JavaScript内で${変数}をそのまま書いても正しく展開されません。
たとえば、次のようなサンプルコードを見てみましょう。
<script th:inline="javascript">
/*<![CDATA[*/
let username = [[${userName}]];
let userAge = [[${userAge}]];
console.log("ユーザー名:", username);
console.log("年齢:", userAge);
/*]]>*/
</script>
このように、[[${...}]]を使うことで、JavaScript変数に値を安全に代入できます。文字列の場合は自動的にダブルクォーテーションで囲まれるため、特別な処理は不要です。
一方、HTML側で同じ値を出力する場合はth:textを使いますが、JavaScript内では必ずth:inline属性が必要になります。これにより、テンプレート内のスクリプトもThymeleafが正しく解釈してくれるようになります。
5. オブジェクトやリストをJavaScriptへ渡す実例
次に、少し発展的な内容として、オブジェクトやリストをJavaScriptへ渡す方法を紹介します。サーバーから単一の値だけでなく、複数のデータをまとめたリストやマップを渡すことも可能です。
たとえば、Springの@Controllerでユーザー情報を複数人分送るケースを考えます。
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
import java.util.Map;
@Controller
public class UserController {
@GetMapping("/users")
public String showUsers(Model model) {
List<Map<String, Object>> userList = List.of(
Map.of("name", "田中太郎", "age", 28),
Map.of("name", "佐藤花子", "age", 32),
Map.of("name", "鈴木一郎", "age", 24)
);
model.addAttribute("users", userList);
return "userList";
}
}
上記のコントローラでは、List<Map<String, Object>>型のリストをThymeleafに渡しています。これをJavaScript側で使う場合、次のように記述します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ユーザー一覧</title>
</head>
<body>
<script th:inline="javascript">
/*<![CDATA[*/
let users = [[${users}]];
console.log(users);
/*]]>*/
</script>
</body>
</html>
このコードを実行すると、JavaScriptの配列としてサーバーから渡されたデータがそのまま扱えます。ブラウザのコンソールでは次のように表示されます。
[
{name: "田中太郎", age: 28},
{name: "佐藤花子", age: 32},
{name: "鈴木一郎", age: 24}
]
このように、Thymeleafはリストやマップを自動的にJSON形式に変換して出力してくれるため、JavaScriptで直接扱えるのが大きな利点です。テンプレート内で複雑なデータを扱いたいときにも非常に便利です。
特に、サーバーで生成したデータをそのままJavaScriptで利用したい場合には、余計なJSON変換処理を書かずに済むのがThymeleafの魅力です。
6. JavaScript側で受け取った値を使って動的に表示を変える方法
最後に、JavaScriptで受け取ったデータを使ってHTMLの表示を動的に変更する方法を見ていきましょう。サーバーから渡された配列データを使って、画面上にユーザー情報を自動で一覧表示する例です。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf 値渡し 実践</title>
</head>
<body>
<h3>ユーザー一覧</h3>
<ul id="userList"></ul>
<script th:inline="javascript">
/*<![CDATA[*/
let users = [[${users}]];
/*]]>*/
let listElement = document.getElementById("userList");
users.forEach(user => {
let li = document.createElement("li");
li.textContent = user.name + "(" + user.age + "歳)";
listElement.appendChild(li);
});
</script>
</body>
</html>
この例では、usersという変数にサーバー側のデータを展開し、JavaScriptでそれをループ処理しています。DOM操作によってリスト要素を生成することで、ユーザー一覧を動的に描画する仕組みになっています。
Thymeleafを使うことで、バックエンドで生成されたデータをテンプレートに安全に埋め込み、フロントエンドのJavaScriptで扱えるようになります。特に、データの加工や描画ロジックをクライアント側で実装したい場合に非常に有効です。
また、変数のスコープにも注意が必要です。th:inline="javascript"のスクリプトブロック内で宣言された変数は、他のスクリプトタグからも参照できるため、複数の機能で共有して使うこともできます。
もし変数が展開されない場合は、Thymeleafのテンプレート構文がコメントアウトされていないか、またはth:inline属性を忘れていないかを確認しましょう。
これで、ThymeleafとJavaScriptの連携による「値渡し」の基本的な構造と実践方法が理解できたと思います。次のステップでは、データの加工やイベント処理と組み合わせて、より動的な画面を作る応用方法に進むことができます。
7. コントローラから渡す値を安全に扱うためのポイント(スクリプトインジェクション対策)
ここまでで、Thymeleafを使ってサーバー側の値をJavaScriptへ渡す方法を学びました。しかし、実際のアプリケーションでは「安全に値を扱う」ことも重要なポイントになります。特に、ユーザー入力をそのままスクリプト内に展開してしまうと、悪意あるコードが実行される危険があります。これをスクリプトインジェクションと呼びます。
たとえば、ユーザーが入力フォームに<script>alert('攻撃');</script>という文字列を入力した場合、それをサーバーが無加工でThymeleafへ渡すと、JavaScript内でそのスクリプトが実行されてしまいます。これはセキュリティ上の大きな問題です。
このような事態を防ぐには、Thymeleafが提供する自動エスケープ機能を正しく利用することが大切です。Thymeleafでは、[[${変数}]]構文を使うと自動的にエスケープ処理が行われ、特殊文字が安全に変換されます。そのため、HTMLやJavaScriptの文法を壊すことなく値を展開できます。
また、文字列をそのままJavaScriptに渡す際は、Thymeleafが自動でダブルクォーテーションを付与するため、自分で囲む必要はありません。逆に、明示的に囲んでしまうと二重引用符になり、予期しないエラーにつながることがあります。
もうひとつの安全対策として、JavaScript内で文字列を扱う際には、常に出力内容を検証することが推奨されます。必要に応じて、サーバー側で入力値をバリデーションし、不正なスクリプトが埋め込まれないようにしておきましょう。ThymeleafはサーバーでHTMLを生成するため、フロント側のセキュリティ対策と連携することが重要です。
このように、ThymeleafとJavaScriptの「値渡し」は便利な仕組みですが、安全に実装することが何よりも大切です。特に公開システムやログイン後の画面では、データをそのまま出力しないという意識を持っておきましょう。
8. 動的な値連携をより便利にする工夫(th:attrやdata属性の活用)
次に、ThymeleafとJavaScriptをさらに柔軟に連携させる方法として、「th:attr」や「data属性」を使った工夫を紹介します。これらを使うことで、HTML要素の中に動的な値を埋め込み、JavaScriptから簡単にアクセスできるようになります。
まず、th:attrを使うと、HTMLタグの属性にサーバー側の値を直接設定できます。たとえば、ユーザーIDをボタンに埋め込む場合、次のように書きます。
<button th:attr="data-userid=${user.id}" onclick="showUserInfo(this)">ユーザー情報を見る</button>
このように書くと、生成されたHTMLは次のようになります。
<button data-userid="101" onclick="showUserInfo(this)">ユーザー情報を見る</button>
そして、JavaScript側では次のようにして値を取得できます。
function showUserInfo(element) {
let userId = element.getAttribute("data-userid");
console.log("選択されたユーザーID:", userId);
}
この方法を使うと、ThymeleafとJavaScriptの間で自然に値を受け渡しできます。特にイベント処理やクリック操作などの動的な画面操作では非常に有効です。
さらに応用すると、複数の属性を一括で設定することも可能です。
<div th:attr="data-name=${user.name}, data-age=${user.age}">ユーザー情報</div>
このように書けば、JavaScriptからdatasetを使ってアクセスできます。
let userDiv = document.querySelector("div");
console.log(userDiv.dataset.name);
console.log(userDiv.dataset.age);
このようなdata属性の活用は、テンプレート内で複数の値を安全に扱う上でとても便利です。JavaScriptとの「動的連携」をより自然に実現できるため、スクリプト内での複雑な変数定義を減らすことができます。
また、デザインやHTML構造を壊さずに値を保持できる点も大きな利点です。HTML側に値を埋め込み、JavaScriptで参照するという流れは、Thymeleafを使ったフロントエンド開発の定番手法と言えるでしょう。
9. 実際のサンプルアプリでの流れまとめ(Controller → Thymeleaf → JavaScript)
最後に、これまでの内容を踏まえて、SpringのコントローラからThymeleaf、そしてJavaScriptへ値を渡す一連の流れをまとめてみましょう。この流れを理解すれば、どのように「動的連携」が行われているのかが明確になります。
まず、サーバー側ではSpringの@Controllerを使ってデータを用意します。たとえば、ログイン中のユーザー情報をモデルに追加してビューへ渡す場合、次のように記述します。
@Controller
public class AccountController {
@GetMapping("/account")
public String showAccount(Model model) {
model.addAttribute("userName", "田中太郎");
model.addAttribute("userLevel", "ゴールド");
return "account";
}
}
このデータをThymeleafテンプレートで受け取り、JavaScriptに連携させる例を見てみましょう。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>アカウント情報</title>
</head>
<body>
<h3 th:text="${userName} + 'さんのアカウント情報'"></h3>
<div th:attr="data-level=${userLevel}" id="userData"></div>
<script th:inline="javascript">
/*<![CDATA[*/
let name = [[${userName}]];
let level = document.getElementById("userData").dataset.level;
console.log(name + "さんのランクは" + level + "です。");
/*]]>*/
</script>
</body>
</html>
このように、サーバーで用意した値がThymeleafによってHTMLに展開され、そのままJavaScriptから参照できるようになります。変数を[[${...}]]で直接渡すだけでなく、data属性に埋め込むことで、安全かつ柔軟に値を取り扱うことができます。
実際の画面では、ユーザー名やステータスを動的に変更したり、クリックイベントを通じてサーバーの情報を反映させることも容易です。このような構成により、バックエンドのSpringとフロントエンドのJavaScriptが自然に連携し、動的で反応の良いアプリケーションを構築できます。
また、Thymeleafはテンプレートの段階でサーバー変数を展開するため、JavaScriptフレームワークを使わずにシンプルな構造で実装できます。これは、pleiades+Gradle構成での軽量開発に非常に適しています。
つまり、ControllerからThymeleafを経由してJavaScriptに値を渡す流れは次のようになります。
1. ControllerでModelにデータを追加する。
2. Thymeleafテンプレートで[[${変数}]]を展開する。
3. JavaScriptで変数やdata属性を利用して動的に処理する。
この流れを意識することで、初心者でも安全かつ柔軟にThymeleafとJavaScriptを組み合わせた「動的連携」を実現できます。複雑なフレームワークを使わなくても、Spring標準機能だけで十分に豊かな画面表現が可能です。
これで「Thymeleaf javascript 値渡しを簡単にするコツ」の記事は完結です。最初は少し難しく感じるかもしれませんが、仕組みを理解すれば非常に強力なテンプレート連携ができるようになります。
まとめ
ここまで、Spring Bootの開発現場で欠かせないThymeleaf(タイムリーフ)とJavaScriptの連携について詳しく解説してきました。サーバーサイドで生成したデータをフロントエンドへ受け渡す「値渡し」は、動的なWebアプリケーションを構築する上で避けては通れないステップです。特に、Springのコントローラから渡されたModelの値を、いかに安全かつスマートにJavaScriptで扱うかが、コードの可読性や保守性を大きく左右します。
Thymeleaf連携の重要ポイントのおさらい
今回の内容で最も重要なのは、th:inline="javascript"の活用です。これを使用することで、ThymeleafはJavaScriptの文脈を理解し、サーバーサイドの変数を適切な形式(文字列なら引用符付き、オブジェクトならJSON形式)で展開してくれます。また、[[${変数名}]]というインライン表記を用いることで、従来の煩雑な記述を簡略化できる点も大きなメリットです。
さらに、セキュリティ面についても触れました。[[...]]による自動エスケープは、XSS(クロスサイトスクリプティング)などの脆弱性を防ぐための基本的な防衛策です。ユーザーが入力した情報をそのまま画面に出力する際は、必ずThymeleafの標準機能を活用するようにしましょう。
より高度な動的連携のために
また、data-属性(カスタムデータ属性)を利用した手法も紹介しました。JavaScriptの中に直接値を書き込むのではなく、HTML要素に属性としてデータを持たせることで、DOM操作と連動した柔軟な処理が可能になります。この方法は、特に複数のボタンに異なるIDを持たせて、クリック時にそれぞれの詳細情報を表示させるといったUIの実装に非常に適しています。
最後に、これまでの知識を総動員した実践的なサンプルコードを掲載します。コントローラから受け取った複雑なオブジェクトをJavaScriptの配列として扱い、それを加工して画面に反映させる一連の流れを確認してみましょう。
// Controller側の実装例
@GetMapping("/dashboard")
public String showDashboard(Model model) {
List<Product> products = productService.findAll();
model.addAttribute("productList", products);
model.addAttribute("isAvailable", true);
return "dashboard";
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>商品管理ダッシュボード</title>
</head>
<body>
<div id="status-badge" th:attr="data-status=${isAvailable}">状態確認</div>
<ul id="product-container">
</ul>
<script th:inline="javascript">
/*<![CDATA[*/
// 1. リストデータをJSON形式で安全に取得
const products = [[${productList}]];
// 2. DOMの取得
const container = document.getElementById('product-container');
const badge = document.getElementById('status-badge');
// 3. データに基づいた動的描画
if (products && products.length > 0) {
products.forEach(p => {
const li = document.createElement('li');
li.className = "list-item";
li.innerHTML = `<strong>${p.name}</strong>: ¥${p.price}`;
container.appendChild(li);
});
}
// 4. data属性の活用
if (badge.dataset.status === 'true') {
badge.style.color = 'green';
badge.textContent = 'サービス稼働中';
}
/*]]>*/
</script>
</body>
</html>
このように、Thymeleafの「サーバーサイドでの静的な描画能力」と、JavaScriptの「クライアントサイドでの動的な操作能力」を適切に組み合わせることが、モダンなWeb開発の第一歩です。pleiadesやGradleを用いた環境でも、この基本原則は変わりません。
生徒
「先生、今回のまとめでThymeleafとJavaScriptの関係がすごくスッキリ整理できました!特に[[${...}]]を使うだけで、自動的にJSONっぽく扱えるのが便利ですね。」
先生
「そうだね。昔はわざわざ隠しフィールド(hidden)に値を埋め込んで、それをJavaScriptで取得するなんて面倒なこともしていたけれど、今のThymeleafならスマートに書けるんだよ。」
生徒
「セキュリティの話も勉強になりました。エスケープを意識しないと、知らぬ間に脆弱性を作ってしまう可能性があるんですね。th:inline="javascript"は魔法の呪文みたいです。」
先生
「いい表現だね(笑)。でも、過信は禁物だよ。大量のデータを渡すとHTMLのソースコードが肥大化して、ページの読み込み速度に影響することもある。そんなときはWeb APIを作って非同期通信(Ajax)を検討するのも一つの手だ。」
生徒
「なるほど!まずはこの『値渡し』を完璧にして、次はもっと複雑な動的連携にも挑戦してみたいと思います。ありがとうございました!」