他のフレームワークには用意されていたりする
テンプレートにおける表示用の加工処理は大体 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 側から定数としてアクセスしたい場合も自然だ。