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でのディスカッション
また、実装時は必ずテストを作成し、セキュリティ面にも配慮することを忘れずに。チーム開発では、ここで紹介した命名規則やコーディング規約を共有し、一貫性のある実装を心がけましょう。