Thymeleafのif条件分岐とは?基礎から解説
th:ifの基本的な書き方と動作原理
Thymeleafのth:if属性は、HTML要素の表示/非表示を条件によって制御する機能です。この属性は、Spring Bootアプリケーションでの画面開発において最も頻繁に使用される機能の1つです。
基本構文
<div th:if="${条件式}">
条件が真の場合に表示される内容
</div>
評価ルール
Thymeleafは以下の値を「偽(false)」として評価します:
false(ブール値)- 数値の
0 null- 空文字列
"" - 空配列
- 空のコレクション
それ以外の値は全て「真(true)」として評価されます。
実践的な使用例
<!-- 基本的な変数の判定 -->
<div th:if="${user.isActive}">
このユーザーはアクティブです
</div>
<!-- null チェック -->
<div th:if="${user != null}">
ユーザー情報が存在します
</div>
<!-- 数値の比較 -->
<div th:if="${product.stock > 0}">
在庫があります
</div>
<!-- 文字列の比較 -->
<div th:if="${user.role == 'ADMIN'}">
管理者向けメニュー
</div>
th:unlessで否定条件を簡潔に書く方法
th:unlessはth:ifの否定版で、条件が偽の場合に要素を表示します。コードの可読性を高めたい場合に特に有用です。
th:unlessの基本構文
<div th:unless="${条件式}">
条件が偽の場合に表示される内容
</div>
th:if vs th:unlessの使い分け
以下の例で、同じ条件をそれぞれの方法で表現する方法を見てみましょう:
<!-- th:ifを使用した場合 -->
<div th:if="${product.stock == 0}">
在庫切れです
</div>
<!-- th:unlessを使用した場合 -->
<div th:unless="${product.stock > 0}">
在庫切れです
</div>
Switch文との使い分けポイント
条件分岐にはth:ifの他にth:switch/th:caseも使用できます。それぞれの特徴を理解し、適切に使い分けることが重要です。
Switch文の基本構文
<div th:switch="${user.role}">
<p th:case="'ADMIN'">管理者です</p>
<p th:case="'USER'">一般ユーザーです</p>
<p th:case="*">権限がありません</p>
</div>
使い分けの指針
- th:ifを使用するケース:
- 条件が独立している場合
- 真偽の判定が主な目的の場合
- 複雑な条件式を使用する場合
- th:switchを使用するケース:
- 1つの変数に対して複数の値をチェックする場合
- 相互排他的な条件分岐の場合
- コードの見通しをよくしたい場合
実践的な使い分け例
<!-- th:ifの適切な使用例 -->
<div th:if="${user.isAdmin()}">
<button>ユーザー管理</button>
</div>
<div th:if="${user.canEditPosts()}">
<button>投稿編集</button>
</div>
<!-- th:switchの適切な使用例 -->
<div th:switch="${order.status}">
<p th:case="'PENDING'">処理待ち</p>
<p th:case="'PROCESSING'">処理中</p>
<p th:case="'COMPLETED'">完了</p>
<p th:case="'CANCELLED'">キャンセル</p>
<p th:case="*">不明なステータス</p>
</div>
このように、使用シーンに応じて適切な条件分岐方法を選択することで、メンテナンス性の高いコードを実現できます。
実践で使える!Thymeleafのif条件分岐15のテクニック
複数条件を組み合わせた高度な分岐処理
1. AND条件の組み合わせ
<!-- 複数条件のAND結合 -->
<div th:if="${user.isActive} and ${user.hasPermission('ADMIN')}">
管理者向けコンテンツ
</div>
<!-- ネストされた条件チェック -->
<div th:if="${order != null and order.status == 'SHIPPED' and order.trackingNumber != null}">
配送状況を確認する
</div>
2. OR条件の活用
<!-- 複数条件のOR結合 -->
<div th:if="${user.isAdmin} or ${user.isModerator}">
コンテンツ管理メニュー
</div>
<!-- 複雑なOR条件の組み合わせ -->
<div th:if="${item.isDiscounted} or (${item.stock < 5} and ${item.isPopular})">
注目商品!
</div>
3. 複合条件式の最適化
<!-- 条件式を変数として定義 -->
<div th:with="isEligible=${user.age >= 20 and user.hasValidId}">
<div th:if="${isEligible}">
サービスを利用できます
</div>
</div>
ElvisオペレータとNullチェックの組み合わせ技
4. Nullセーフな条件分岐
<!-- Elvisオペレータを使用したnull安全な表示 -->
<div th:if="${user?.premium} ?: false">
プレミアム会員向けコンテンツ
</div>
<!-- ネストされたプロパティのnullチェック -->
<div th:if="${order?.customer?.address} != null">
配送先住所が登録されています
</div>
5. デフォルト値を使用した条件分岐
<!-- デフォルト値との組み合わせ -->
<div th:with="status=${order?.status} ?: 'PENDING'">
<div th:if="${status == 'COMPLETED'}">
注文完了
</div>
</div>
リストや配列の要素チェックテクニック
6. コレクションの空チェック
<!-- リストの存在・空チェック -->
<div th:if="${not #lists.isEmpty(items)}">
<ul>
<li th:each="item : ${items}" th:text="${item.name}">商品名</li>
</ul>
</div>
<!-- 配列の長さチェック -->
<div th:if="${#arrays.length(selectedOptions) > 0}">
選択されたオプション一覧
</div>
7. 特定要素の存在チェック
<!-- リスト内の特定要素チェック -->
<div th:if="${#lists.contains(user.roles, 'ADMIN')}">
管理者権限があります
</div>
セッション・リクエストパラメータでの条件分岐
8. セッション変数のチェック
<!-- セッション変数の存在チェック -->
<div th:if="${session.containsKey('userId')}">
ログイン中です
</div>
<!-- セッション属性を使用した条件分岐 -->
<div th:if="${session.preferences?.darkMode} ?: false">
ダークモード有効
</div>
9. リクエストパラメータの活用
<!-- パラメータの存在チェック -->
<div th:if="${param.containsKey('error')}">
<div class="alert alert-danger">
ログインに失敗しました
</div>
</div>
<!-- パラメータ値による分岐 -->
<div th:if="${param.view != null and param.view[0] == 'detailed'}">
詳細ビュー
</div>
Spring Securityと組み合わせた権限制御
10. 認証状態のチェック
<!-- 認証済みユーザーのチェック -->
<div th:if="${#authentication.isAuthenticated()}">
ログイン中のユーザー: <span th:text="${#authentication.name}">username</span>
</div>
11. ロールベースの表示制御
<!-- 特定ロールの確認 -->
<div th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}">
<a href="/admin/dashboard">管理画面へ</a>
</div>
<!-- 複数ロールの組み合わせ -->
<div th:if="${#authorization.expression('hasAnyRole(''ROLE_ADMIN'', ''ROLE_MANAGER'')')}">
<button>高度な設定</button>
</div>
12. カスタム権限のチェック
<!-- カスタムセキュリティ式の使用 -->
<div th:if="${#authorization.expression('hasPermission(#id, ''Product'', ''EDIT'')')}">
<button>商品を編集</button>
</div>
13. ユーザー情報に基づく細かな制御
<!-- プリンシパル情報を使用した条件分岐 -->
<div th:if="${#authentication.principal.department == 'SALES'}">
<div>売上レポート</div>
</div>
14. セキュリティコンテキストの高度な活用
<!-- セキュリティコンテキストと業務ロジックの組み合わせ -->
<div th:with="hasAccess=${#authorization.expression('hasRole(''ADMIN'')') or
(#authorization.expression('hasRole(''USER'')') and ${item.creator.id == #authentication.principal.id})}">
<div th:if="${hasAccess}">
<button>コンテンツを編集</button>
</div>
</div>
15. エラーハンドリングとの組み合わせ
<!-- 権限エラーの適切な表示 -->
<div th:if="${#authorization.expression('!hasRole(''ADMIN'')')}">
<div class="alert alert-warning">
この操作には管理者権限が必要です
</div>
</div>
これらのテクニックを適切に組み合わせることで、より柔軟で保守性の高い条件分岐処理を実現できます。
現場で役立つ!if条件分岐のベストプラクティス
可読性を高めるリファクタリングテクニック
1. 条件式の抽出と再利用
<!-- Before: 複雑な条件式を直接記述 -->
<div th:if="${user.age >= 20 and user.hasValidId and user.subscriptionStatus == 'ACTIVE'}">
プレミアムコンテンツ
</div>
<!-- After: th:withを使用して条件を抽出 -->
<div th:with="canAccessPremium=${user.age >= 20 and user.hasValidId and user.subscriptionStatus == 'ACTIVE'}">
<div th:if="${canAccessPremium}">
プレミアムコンテンツ
</div>
</div>
2. Helper関数の活用
// Controllerまたはユーティリティクラスで定義
@Component
public class UserPermissionUtils {
public boolean canAccessPremiumContent(User user) {
return user.getAge() >= 20
&& user.hasValidId()
&& "ACTIVE".equals(user.getSubscriptionStatus());
}
}
<!-- Helperメソッドを使用した簡潔な条件式 -->
<div th:if="${@userPermissionUtils.canAccessPremiumContent(user)}">
プレミアムコンテンツ
</div>
3. 意図が明確な命名規則
<!-- Bad: 意図が不明確な命名 -->
<div th:if="${flag && status == 1}">
<!-- Good: 意図が明確な命名 -->
<div th:if="${isUserVerified && orderStatus == 'COMPLETED'}">
パフォーマンスを考慮した条件式の書き方
1. 条件式の評価順序の最適化
<!-- Bad: 重い処理が先に評価される -->
<div th:if="${heavyDatabaseOperation() and simpleCheck}">
<!-- Good: 軽い処理を先に評価 -->
<div th:if="${simpleCheck and heavyDatabaseOperation()}">
2. キャッシュの活用
// Controllerで条件をキャッシュ
@ModelAttribute("commonConditions")
public Map<String, Boolean> prepareCommonConditions() {
Map<String, Boolean> conditions = new HashMap<>();
conditions.put("isSystemAvailable", systemService.checkAvailability());
conditions.put("isMaintenanceMode", configService.isMaintenanceMode());
return conditions;
}
<!-- キャッシュされた条件を使用 -->
<div th:if="${commonConditions.isSystemAvailable}">
システムは利用可能です
</div>
3. 不要な条件評価の回避
<!-- Bad: ループ内で毎回条件を評価 -->
<tr th:each="item : ${items}">
<td th:if="${#authorization.expression('hasRole(''ADMIN'')')}">
管理者操作
</td>
</tr>
<!-- Good: ループの外で一度だけ評価 -->
<div th:with="isAdmin=${#authorization.expression('hasRole(''ADMIN'')')}">
<tr th:each="item : ${items}">
<td th:if="${isAdmin}">
管理者操作
</td>
</tr>
</div>
保守性を高めるための設計パターン
1. 責務の分離
// ビジネスロジックをサービス層に分離
@Service
public class ContentVisibilityService {
public boolean shouldShowPremiumContent(User user) {
return user.isPremiumMember()
&& !user.isSubscriptionExpired()
&& user.hasAccessToFeature("premium_content");
}
}
<!-- テンプレートではシンプルな呼び出しのみ -->
<div th:if="${@contentVisibilityService.shouldShowPremiumContent(user)}">
プレミアムコンテンツ
</div>
2. 条件のカプセル化
// 条件をenumで管理
public enum DisplayCondition {
SHOW_PREMIUM_CONTENT {
@Override
public boolean evaluate(User user) {
return user.isPremiumMember();
}
},
SHOW_ADMIN_PANEL {
@Override
public boolean evaluate(User user) {
return user.hasRole("ADMIN");
}
};
public abstract boolean evaluate(User user);
}
<!-- カプセル化された条件を使用 -->
<div th:if="${@displayConditionEvaluator.evaluate(DisplayCondition.SHOW_PREMIUM_CONTENT, user)}">
プレミアムコンテンツ
</div>
3. テスト容易性の確保
// テスト可能な設計
@Component
public class FeatureToggleService {
private final ConfigurationService configService;
public boolean isFeatureEnabled(String featureName, User user) {
return configService.isFeatureActive(featureName)
&& user.hasAccessTo(featureName);
}
}
<!-- テスト可能な条件分岐 -->
<div th:if="${@featureToggleService.isFeatureEnabled('new_dashboard', user)}">
新しいダッシュボード
</div>
これらのベストプラクティスを適用することで、より保守性が高く、パフォーマンスの良い条件分岐処理を実装できます。また、チーム開発においても一貫性のあるコードベースを維持することが可能になります。
初心者がハマりやすい!if条件分岐の注意点と対策
よくあるエラーとその解決方法
1. NullPointerException関連のエラー
よくある問題
<!-- Bad: Nullチェックなしで直接プロパティにアクセス -->
<div th:if="${user.premium}">
プレミアム会員です
</div>
解決策
<!-- Good: Safe Navigation演算子を使用 -->
<div th:if="${user?.premium}">
プレミアム会員です
</div>
<!-- Better: 完全なNull対策 -->
<div th:if="${user != null and user.premium}">
プレミアム会員です
</div>
2. 比較演算子の誤用
よくある問題
<!-- Bad: 等価比較の誤った使用 -->
<div th:if="${status = 'ACTIVE'}"> <!-- 代入演算子を使用してしまっている -->
アクティブユーザー
</div>
解決策
<!-- Good: 正しい等価比較 -->
<div th:if="${status == 'ACTIVE'}">
アクティブユーザー
</div>
<!-- Better: 文字列比較の場合はequalsを使用 -->
<div th:if="${#strings.equals(status, 'ACTIVE')}">
アクティブユーザー
</div>
3. スコープの誤認識
よくある問題
<!-- Bad: スコープを明示していない -->
<div th:if="${username}">
ようこそ、<span th:text="${username}">ゲスト</span>さん
</div>
解決策
<!-- Good: スコープを明示的に指定 -->
<div th:if="${session.username != null}">
ようこそ、<span th:text="${session.username}">ゲスト</span>さん
</div>
デバッグのコツと効率的なトラブルシューティング
1. デバッグ情報の表示
<!-- 変数の中身を確認 -->
<div th:text="${#vars}"></div>
<!-- 特定の変数の型と値を確認 -->
<div th:text="${#objects.nullSafe(user, 'null')}"></div>
<div th:text="${#objects.nullSafe(user?.getClass(), 'null')}"></div>
2. 条件式の段階的な検証
<!-- 複雑な条件を分解して確認 -->
<div th:with="condition1=${user != null},
condition2=${user.age >= 20},
condition3=${user.hasValidSubscription()}">
<div>条件1: <span th:text="${condition1}">false</span></div>
<div>条件2: <span th:text="${condition2}">false</span></div>
<div>条件3: <span th:text="${condition3}">false</span></div>
<div th:if="${condition1 and condition2 and condition3}">
全ての条件を満たしています
</div>
</div>
3. デバッグモードの活用
# application.properties spring.thymeleaf.cache=false logging.level.org.thymeleaf=TRACE
テストコードでの条件分岐の検証方法
1. ユニットテストの作成
@Test
public void testUserAccessConditions() {
User user = new User();
user.setAge(25);
user.setSubscriptionStatus("ACTIVE");
ModelAndView modelAndView = new ModelAndView("user/profile");
modelAndView.addObject("user", user);
String rendered = thymeleafEngine.process("user/profile", context);
assertTrue(rendered.contains("プレミアムコンテンツ"));
}
2. テストケースの網羅
@TestFactory
Stream<DynamicTest> testVariousConditions() {
return Stream.of(
// 正常系テスト
TestCase.of("通常ユーザー", createNormalUser(), true),
TestCase.of("管理者ユーザー", createAdminUser(), true),
// エッジケース
TestCase.of("null ユーザー", null, false),
TestCase.of("無効化ユーザー", createDisabledUser(), false)
).map(testCase -> DynamicTest.dynamicTest(
testCase.name,
() -> assertCondition(testCase.user, testCase.expected)
));
}
3. 条件分岐のモック化
@Test
public void testComplexCondition(@Mock UserService userService) {
when(userService.isUserEligible(any())).thenReturn(true);
// テンプレートのレンダリング
Context context = new Context();
context.setVariable("userService", userService);
String result = templateEngine.process("template", context);
assertTrue(result.contains("条件を満たしています"));
}
デバッグ時の主なチェックポイント
- 変数の存在確認
- セッション変数、リクエストパラメータ、モデル属性が期待通りに存在するか
- スコープは正しく設定されているか
- 型の一致確認
- 比較対象の型は一致しているか
- 暗黙の型変換が期待通りに行われているか
- 条件式の評価順序
- 複合条件の場合、各部分が期待通りに評価されているか
- Short-circuit評価が意図通りに機能しているか
- 文字列比較の方法
==とequalsの使い分けは適切か- 大文字小文字の区別は意図通りか
これらの注意点を意識し、適切なデバッグとテストを行うことで、より信頼性の高い条件分岐処理を実装できます。
実践的なサンプルコードで学ぶ応用テクニック
ユーザー管理画面での活用例
1. ユーザー一覧画面の実装
<!-- users/list.html -->
<div class="user-management">
<!-- 管理者向け操作メニュー -->
<div th:if="${#authorization.expression('hasRole(''ADMIN'')')}" class="admin-controls">
<button class="btn btn-primary" onclick="location.href='/users/new'">新規ユーザー登録</button>
<button class="btn btn-secondary" onclick="exportUserList()">ユーザー一覧出力</button>
</div>
<!-- ユーザー一覧テーブル -->
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>ステータス</th>
<th th:if="${#authorization.expression('hasAnyRole(''ADMIN'', ''MANAGER'')')}">操作</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}">1</td>
<td>
<!-- プレミアムユーザーの場合、アイコンを表示 -->
<i th:if="${user.isPremium}" class="fas fa-crown" title="プレミアム会員"></i>
<span th:text="${user.name}">山田太郎</span>
</td>
<td>
<!-- ステータスに応じて表示を変更 -->
<span th:if="${user.status == 'ACTIVE'}" class="badge badge-success">有効</span>
<span th:if="${user.status == 'SUSPENDED'}" class="badge badge-warning">停止中</span>
<span th:if="${user.status == 'DELETED'}" class="badge badge-danger">削除済</span>
</td>
<td th:if="${#authorization.expression('hasAnyRole(''ADMIN'', ''MANAGER'')')}">
<!-- ユーザーステータスに応じた操作ボタン -->
<div class="btn-group">
<button th:if="${user.status != 'DELETED'}"
th:onclick="'editUser(' + ${user.id} + ')'"
class="btn btn-sm btn-info">編集</button>
<button th:if="${user.status == 'ACTIVE'}"
th:onclick="'suspendUser(' + ${user.id} + ')'"
class="btn btn-sm btn-warning">停止</button>
<button th:if="${user.status == 'SUSPENDED'}"
th:onclick="'reactivateUser(' + ${user.id} + ')'"
class="btn btn-sm btn-success">再開</button>
</div>
</td>
</tr>
<!-- データが存在しない場合のメッセージ -->
<tr th:if="${#lists.isEmpty(users)}">
<td colspan="4" class="text-center">ユーザーが存在しません</td>
</tr>
</tbody>
</table>
</div>
2. ユーザー詳細画面の実装
<!-- users/detail.html -->
<div class="user-detail">
<!-- ユーザーが存在しない場合のエラー表示 -->
<div th:if="${user == null}" class="alert alert-danger">
指定されたユーザーは存在しません。
</div>
<!-- ユーザー情報の表示 -->
<div th:if="${user != null}" class="user-info">
<h2>ユーザー詳細情報</h2>
<!-- プロフィール情報 -->
<div class="profile-section">
<div class="profile-header">
<!-- プレミアムステータスバッジ -->
<div th:if="${user.isPremium}" class="premium-badge">
<span class="badge badge-gold">Premium</span>
<small th:text="${'有効期限: ' + #dates.format(user.premiumExpireDate, 'yyyy/MM/dd')}">
2024/12/31
</small>
</div>
<!-- アカウントステータス -->
<div th:switch="${user.status}" class="status-indicator">
<span th:case="'ACTIVE'" class="text-success">アクティブ</span>
<span th:case="'PENDING'" class="text-warning">認証待ち</span>
<span th:case="'LOCKED'" class="text-danger">ロック中</span>
</div>
</div>
<!-- 管理者向け操作パネル -->
<div th:if="${#authorization.expression('hasRole(''ADMIN'')')}" class="admin-panel">
<div th:if="${user.status == 'LOCKED'}" class="alert alert-warning">
<p>アカウントがロックされています。</p>
<p th:text="${'ロック理由: ' + user.lockReason}">不正アクセス試行</p>
<button class="btn btn-success" th:onclick="'unlockAccount(' + ${user.id} + ')'">
ロック解除
</button>
</div>
<div th:if="${user.warningCount > 0}" class="alert alert-info">
<p th:text="${'警告回数: ' + user.warningCount + '回'}">警告回数: 2回</p>
</div>
</div>
</div>
</div>
</div>
商品一覧・詳細画面での実装例
1. 商品一覧画面
<!-- products/list.html -->
<div class="product-listing">
<!-- 商品フィルターセクション -->
<div class="filter-section" th:with="hasFilters=${param.category != null or param.price != null}">
<div th:if="${hasFilters}" class="active-filters">
<span>適用中のフィルター:</span>
<span th:if="${param.category}" th:text="${param.category[0]}">カテゴリー</span>
<span th:if="${param.price}" th:text="${'¥' + param.price[0] + '以下'}">価格</span>
<a href="/products" class="clear-filters">クリア</a>
</div>
</div>
<!-- 商品グリッド -->
<div class="product-grid">
<div th:each="product : ${products}" class="product-card">
<!-- 在庫状況に応じたバッジ -->
<div class="stock-status">
<span th:if="${product.stock == 0}" class="badge badge-danger">在庫切れ</span>
<span th:if="${product.stock > 0 and product.stock < 5}" class="badge badge-warning"
th:text="${'残り' + product.stock + '個'}">残り3個</span>
</div>
<!-- セール商品の表示 -->
<div th:if="${product.isOnSale}" class="sale-badge">
<span th:text="${product.discountRate + '%OFF'}">30%OFF</span>
</div>
<!-- 商品情報 -->
<img th:src="${product.imageUrl}" alt="商品画像" class="product-image">
<div class="product-info">
<h3 th:text="${product.name}">商品名</h3>
<div class="price-section">
<span th:if="${product.isOnSale}" class="original-price"
th:text="${'¥' + product.originalPrice}">¥1,000</span>
<span class="current-price"
th:text="${'¥' + product.currentPrice}">¥700</span>
</div>
</div>
</div>
</div>
</div>
2. 商品詳細画面
<!-- products/detail.html -->
<div class="product-detail">
<!-- 商品が存在しない場合 -->
<div th:if="${product == null}" class="alert alert-danger">
商品が見つかりませんでした。
</div>
<!-- 商品情報表示 -->
<div th:if="${product != null}" class="product-container">
<!-- 在庫状況による購入ボタンの制御 -->
<div class="purchase-section">
<div th:if="${product.stock > 0}">
<button class="btn btn-primary"
th:onclick="'addToCart(' + ${product.id} + ')'">
カートに追加
</button>
<!-- 残り僅かな場合の警告 -->
<div th:if="${product.stock < 5}" class="stock-warning">
残り在庫僅か!
</div>
</div>
<div th:unless="${product.stock > 0}" class="out-of-stock">
<button class="btn btn-secondary" disabled>在庫切れ</button>
<!-- 再入荷通知ボタン -->
<button th:if="${user != null}"
th:onclick="'notifyRestock(' + ${product.id} + ')'"
class="btn btn-outline-primary">
再入荷通知を受け取る
</button>
</div>
</div>
<!-- セール情報の表示 -->
<div th:if="${product.isOnSale}" class="sale-info">
<div class="countdown" th:with="remaining=${product.saleEndTime - currentTime}">
<span>セール終了まで:</span>
<span th:text="${remaining + '時間'}">24時間</span>
</div>
</div>
</div>
</div>
フォーム入力画面での条件分岐活用法
1. ユーザー登録フォーム
<!-- users/register.html -->
<form th:action="@{/users/register}" method="post" class="registration-form">
<!-- フォームの状態に応じたメッセージ -->
<div th:if="${param.error}" class="alert alert-danger">
入力内容にエラーがあります。
</div>
<div th:if="${param.emailTaken}" class="alert alert-warning">
このメールアドレスは既に使用されています。
</div>
<!-- 入力フィールド -->
<div class="form-group">
<label for="userType">ユーザータイプ</label>
<select id="userType" name="userType" class="form-control"
th:with="selectedType=${param.userType}">
<option value="INDIVIDUAL">個人</option>
<option value="CORPORATE">法人</option>
</select>
</div>
<!-- 法人ユーザー向け追加フィールド -->
<div th:if="${param.userType == 'CORPORATE'}" class="corporate-fields">
<div class="form-group">
<label for="companyName">会社名</label>
<input type="text" id="companyName" name="companyName"
class="form-control" required>
</div>
<div class="form-group">
<label for="department">部署名</label>
<input type="text" id="department" name="department"
class="form-control">
</div>
</div>
<!-- プレミアム会員オプション -->
<div class="premium-option">
<div class="form-check">
<input type="checkbox" id="premium" name="premium"
class="form-check-input">
<label class="form-check-label" for="premium">
プレミアム会員に登録する
</label>
</div>
<!-- プレミアム特典の表示 -->
<div th:if="${param.premium}" class="premium-benefits">
<h4>プレミアム特典</h4>
<ul>
<li>配送料無料</li>
<li>会員限定セール参加可能</li>
<li>ポイント2倍</li>
</ul>
</div>
</div>
</form>
これらの実装例は、実際のプロジェクトですぐに活用できる形で提供されています。条件分岐を効果的に使用することで、ユーザーエクスペリエンスを向上させ、より直感的なインターフェースを実現できます。
まとめ:Thymeleafのif条件分岐を使いこなすために
本記事のポイント整理
1. 基本的な使い方
th:if属性を使用した基本的な条件分岐th:unlessによる否定条件の表現th:switch/th:caseとの使い分け
2. 実践的なテクニック
- 複数条件の組み合わせ方
- Nullセーフな条件分岐の実装
- Spring Securityとの連携による権限制御
3. パフォーマンスと保守性
<!-- パフォーマンスを考慮した実装例 -->
<div th:with="isAdmin=${#authorization.expression('hasRole(''ADMIN'')')}">
<div th:if="${isAdmin}">
<!-- 管理者向けコンテンツ -->
</div>
</div>
実装時のベストプラクティス
- 可読性を重視した実装
- 複雑な条件はヘルパーメソッドに切り出す
- 意図が明確な命名を心がける
- コメントで条件の意図を説明する
- 保守性の高いコード設計
- ビジネスロジックはサービス層に分離
- 共通の条件はユーティリティクラスに集約
- テストコードでの検証を忘れずに
- セキュリティへの配慮
- 権限チェックは必ず行う
- センシティブな情報の表示制御
- XSS対策の実施
こんなときはこう使う!実践ガイド
| ユースケース | 推奨される実装方法 |
|---|---|
| 単純な真偽判定 | th:if="${flag}" |
| Null考慮が必要 | th:if="${object?.property}" |
| 複数条件の組み合わせ | th:if="${condition1 and condition2}" |
| 権限による表示制御 | th:if="${#authorization.expression('hasRole(...)')}" |
| 否定条件 | th:unless="${condition}" |
次のステップ
- スキルアップの方向性
- Thymeleafの他の機能との組み合わせ
- Spring SecurityのSpEL式の習得
- パフォーマンスチューニングの実践
- 実践的な学習方法
- 小規模なプロジェクトでの実装練習
- 既存コードのリファクタリング
- ユニットテストの作成
- よくある問題への対処
- デバッグツールの活用
- エラーログの解析
- パフォーマンスモニタリング
最後に
Thymeleafの条件分岐機能は、適切に使用することで保守性が高く、セキュアで効率的なWebアプリケーションの実装を可能にします。本記事で紹介した基礎知識とベストプラクティスを活用し、実際のプロジェクトで実践してください。
困ったときは以下のリソースを参考にしてください:
- Thymeleaf公式ドキュメント
- Spring Framework公式ガイド
- Stack Overflowでのディスカッション
また、実装時は必ずテストを作成し、セキュリティ面にも配慮することを忘れずに。チーム開発では、ここで紹介した命名規則やコーディング規約を共有し、一貫性のある実装を心がけましょう。