他のフレームワークには用意されていたりする
テンプレートにおける表示用の加工処理は大体 Thymeleaf が標準で備えているが、例えば CakePHP における Helper
だったり Django における独自のテンプレートタグの作成のようにテンプレートの機能だけでは賄いきれない、多くはプレゼンテーション層における HTML への変換のためのロジックを使いたい時がある。
こういう時に Spring Boot における Thymeleaf 上ではどうすればいいのだろうか、というのが今回のテーマである。
勿論 Controller
や Model
(Form) に対応する HTML タグへの変換処理を書いたりすることはできるが MVC において本来コントローラやモデルでプレゼンテーション層の処理を書くのは好ましくないので避けたいところだ。
いろいろ試行錯誤してみた結果、プレゼンテーション層のヘルパクラスをコンポーネントとして登録して Thymeleaf 側から呼び出すのが一番シンプルな気がした。
ヘルパークラス定義
今回は Markdown で書かれたテキストを HTML に変換したいとする。 以下のようなヘルパークラスを任意のパッケージに定義する:
/**
* Thymeleaf テンプレート上で使用するヘルパ.
*/
@Component
class Helper {
/**
* Markdown を HTML に変換して返す.
*
* @param markdown Markdown
* @return HTML に変換された Markdown
*/
fun toHtml(markdown: String): String {
val (parser, renderer) = Parser.builder().build() to HtmlRenderer.builder().build()
return renderer.render(parser.parse(markdown))
}
}
@Component
アノテーションを付与することにより Spring 管理下のコンポーネントとして機能する。
これを Thymeleaf 上で使用するには以下のように ${@helper.toHtml(xxx)}
といった @
を頭につけた記法となる:
<!-- HTML エスケープされないように th:utext を使用する -->
<div class="post-content" th:utext="${@helper.toHtml(post.markdown)}"></div>
後はこういう要件が出てくる度にこの Helper
クラスにメソッドを追加していけばよい。
定数の参照はどうする
同じような悩みとして Thymeleaf 上から Kotlin の定数を参照したいというのがある。
一応 Thymeleaf 上で ${T(パッケージ.クラス名).static フィールド名}
という記法で任意の static フィールドやメソッドを呼び出すことはできる。
ただ、例えば Kotlin で object
を使用して static を表現したとする:
object Consts {
val DAYS = arrayListOf('月', '火', '水', '木', '金', '土', '日')
}
上記の定数を Thymeleaf 側で参照するには ${T(com.kojion.etc.Consts).INSTANCE.DAYS}
のようにしなければならない。
Kotlin の object
が Java コード側から見るとシングルトンな INSTANCE
という static フィールドを介してアクセスするようになっているので INSTANCE
といちいち付けなければならず、あまり綺麗とは言えない。
この場合あえて Kotlin でなく Java で書いてみる:
public class Consts {
public static final List<String> DAYS = Arrays.asList("月", "火", "水", "木", "金", "土", "日");
}
これで ${T(com.kojion.etc.Consts).DAYS}
とアクセスできるので少しシンプルになった。
Thymeleaf 側からは Kotlin でなく Java として見なければならないのが少し辛いところだ。
パッケージ名を書くのも気になる場合は、先程のヘルパークラスと同様にコンポーネントとして登録して定数定義するのがいいのかもしれない:
@Component
object Consts {
val DAYS = arrayListOf("月", "火", "水", "木", "金", "土", "日")
}
これで Thymeleaf 側から ${@consts.DAYS}
でアクセスできるようになった。
object
で定義しているので Kotlin 側から定数としてアクセスしたい場合も自然だ。