要旨
Thymeleafで改行コードを含む文字列を出力してもHTMLソース内で改行されるだけで表示上は改行されない。
改行コードを改行タグに変換して文字列を出力する機能を持つ独自ダイアレクトを作成しth:text
の代替ダイアレクトとして使用する。
なお、Thymeleaf2と3では実装が異なるが本稿ではThymeleaf3を扱う。
環境
- Java SE 8 (jdk-8u181)
- Maven 3.5.4
- Spring Boot 2.0.5
- Thymeleaf 3.0.9
概要
- Processorクラス(操作を実際に記述するクラス)を実装する
- Dialectクラス(Processorをまとめてダイアレクトとして定義するクラス)を実装する
- Beanに設定する
- テンプレートで使用する
詳細手順
Processorクラス(操作を実際に記述するクラス)を実装する
クラス定義
Thymeleafが提供しているProcessor系の抽象クラスからやりたいことに近いものを選んでそれを継承するクラスを作成する。
今回はStandard Dialectであるth:text
に類似したものを作成するのでAbstractStandardExpressionAttributeTagProcessor
を継承する。
public class BreakLineProcessor extends AbstractStandardExpressionAttributeTagProcessor {
...
}
コンストラクタ
必ずコンストラクタを作成し、当該Processorがどのように使われるかを定義する。
private static final String ATTR_NAME = "brtext";
public BreakLineProcessor(String dialectPrefix, int precedence) {
super(TemplateMode.HTML, dialectPrefix, ATTR_NAME, precedence, true);
}
なお、継承元クラスであるAbstractStandardExpressionAttributeTagProcessor
のコンストラクタの各引数は以下のとおりである。
変数名 | クラス | 役割 | |
---|---|---|---|
1 | templateMode | TemplateMode(enum) | テンプレートの種類(HTMLなど) |
2 | dialectPrefix | String | ダイアレクトのプレフィックス(コロンの左側) |
3 | attrName | String | ダイアレクトの属性名(コロンの右側) |
4 | precedence | int | Processor処理実行の優先順位(値が小さいほうが優先) |
5 | removeAttribute | boolean | Processor処理後に当該ダイアレクト属性を除去するか(trueなら除去する) |
今回はプレフィックスと優先順位のみ外から受け取り、残りは固定値とした。
処理実行メソッド
抽象メソッドであるdoProcess
をオーバーライドして処理を記述する。
AbstractStandardExpressionAttributeTagProcessor
のdoProcess
の場合はEL式を解釈した結果が引数のexpressionResult
に格納されるので、これを改行コードごとに分割し、改行タグをはさみながら出力すればよい。
全体の処理の概要は以下の通り。
- 引数の
context
からIModelFactory
インスタンスを取得する。 - 取得した
IModelFactory
インスタンスからIModel
インスタンスを作成する。 - 作成した
IModel
インスタンスにHTMLに反映する要素(文字列やタグ)を順番に追加する。
なお、追加するインスタンスもIModelFactory
インスタンスから作成する。 - 引数の
structureHandler
にIModel
インスタンスを適用する。
@Override
protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName,
String attributeValue, Object expressionResult, IElementTagStructureHandler structureHandler) {
// nullの場合は何も出力せず処理を終了
if (expressionResult == null) return;
IModelFactory factory = context.getModelFactory();
IModel model = factory.createModel();
ITemplateEvent brTag = factory.createOpenElementTag("br");
// 改行コードで分割
String[] lines = expressionResult.toString().split("\r\n|\r|\n", -1);
for (String line : lines) {
// <br>+1行分の文字列をmodelに追加
model.add(brTag);
model.add(factory.createText(line));
}
// 先頭の<br>タグを除去
model.remove(0);
structureHandler.setBody(model, false);
Dialectクラス(Processorをまとめてダイアレクトとして定義するクラス)を実装する
クラス定義
Thymeleafが提供しているDialect系の抽象クラスやインターフェースから作成したダイアレクト処理に合致するもの選んでそれを継承(実装)するクラスを作成する。
今回はProcessorにより本処理を実装したので抽象クラスAbstractProcessorDialect
を継承する。
public class KurukuruzDialect extends AbstractProcessorDialect {
...
}
コンストラクタ
必ずコンストラクタを作成し、当該Dialectがどのように使われるかを定義する。
private static final String NAME = "kurukuruz original dialect";
private static final String PREFIX = "krz";
public KurukuruzDialect() {
super(NAME, PREFIX, StandardDialect.PROCESSOR_PRECEDENCE);
}
なお、継承元クラスであるAbstractProcessorDialect
のコンストラクタの各引数は以下のとおりである。
変数名 | クラス | 役割 | |
---|---|---|---|
1 | name | String | ダイアレクトの名前(?), 特に使うことはない |
2 | prefix | String | ダイアレクトのプレフィックス, 後述のProcessor登録メソッドの引数になる |
3 | processorPrecedence | int | Processor処理実行の優先順位に使用する数値 |
Processor登録
AbstractProcessorDialect
の抽象メソッドであるgetProcessors
内でダイアレクトに登録する各ProcessorのインスタンスをSetとしてまとめ、返却する。
@Override
public Set<IProcessor> getProcessors(String dialectPrefix) {
Set<IProcessor> processors = new HashSet<>();
processors.add(new BreakLineProcessor(dialectPrefix, getDialectProcessorPrecedence()));
return processors;
}
Beanに設定する
@Bean
アノテーションが有効な適当なクラスで以下のようにBean定義メソッドを作成し、Dialectインスタンスを登録する。
@Bean
KurukuruzDialect kurukuruzDialect() {
return new KurukuruzDialect();
}
実行結果
- HTMLテンプレート
<body>
<div krz:brtext="${text1}"></div>
<div krz:brtext="${text2}"></div>
<div krz:brtext="${text3}"></div>
<div krz:brtext="${text4}"></div>
<div krz:brtext="${text5}"></div>
</body>
- 表示対象のModel
model.addAttribute("text1", "改行無し");
model.addAttribute("text2", "CRLF改行\r\nあり\r\n\r\n↑2回連続改行");
model.addAttribute("text3", "CR改行\rあり");
model.addAttribute("text4", "LF改行\nあり");
model.addAttribute("text5", null);
↓↓↓↓↓
- 出力結果(html)
<body>
<div>改行無し</div>
<div>CRLF改行<br>あり<br><br>↑2回連続改行</div>
<div>CR改行<br>あり</div>
<div>LF改行<br>あり</div>
<div></div>
</body>
- 出力結果(画面表示)
参考サイト
Thymeleafで改行コードをhtmlの改行タグに変換して出力する - Qiita
Thymeleaf3で独自のDialectを作る - Qiita
thymeleaf3でDialectを実装する - abcdefg.....