FreeMarkerの一般的な操作

1. 前書き

FreeMarkerは、Javaで記述され、Apache Foundationによって管理されているテンプレートエンジンです。 FTLとも呼ばれるFreeMarkerテンプレート言語を使用して、Webページ、電子メール、XMLファイルなどの多くのテキストベースの形式を生成できます。
このチュートリアルでは、FreeMarkerですぐにできることを確認しますが、https://freemarker.apache.org/docs/api/freemarker/template/Configuration.html [quiteさらに、https://www.baeldung.com/freemarker-in-spring-mvc-tutorial [Springとうまく統合]もできます。
始めましょう!

2. クイック概要

ページに動的コンテンツを挿入するには、* FreeMarkerが理解できる構文を使用する必要があります*:
  • テンプレートの_ $ \ {…} は、生成された出力で次のように置き換えられます
    中括弧内の式の実際の値–この_interpolation –_と呼びます。いくつかの例は
    $ \ {1 + 2_}および_ $ \ {variableName} _です

  • FTLタグはHTMLタグに似ています(ただし、#_または @ _を含む)およびFreeMarker
    _ <#if…> </#if> _など、それらを解釈します

  • FreeMarkerのコメントは_ <#–で始まり、–> _で終わります

3. インクルードタグ

FTL _include_ディレクティブは、アプリケーションでDRY原則に従う方法です。 ファイル内の反復コンテンツを定義し、単一の_include_タグを使用して、異なるFreeMarkerテンプレートで再利用します。
そのような使用例の1つは、多くのページ内にメニューセクションを含める場合です。 最初に、ファイル内にメニューセクションを定義します。これを_menu.ftl_と呼び、次のコンテンツを使用します。
<a href="#dashboard">Dashboard</a>
<a href="#newEndpoint">Add new endpoint</a>
HTMLページに、create menu.ftlを含めます。
<!DOCTYPE html>
<html>
<body>
<#include 'fragments/menu.ftl'>
    <h6>Dashboard page</h6>
</body>
</html>
また、フラグメントにFTLを含めることもできます。これは素晴らしいことです。

4. 価値の存在の取り扱い

FTLは、_null_値を欠損値と見なします。 したがって、テンプレートに_null_ *を処理するロジックを追加する必要があります。
_ ?? _演算子を使用して、属性またはネストされたプロパティが存在するかどうかを確認できます。 結果はブール値です:
${attribute??}
そのため、_null、_の属性をテストしましたが、それだけでは十分ではありません。 この欠損値のフォールバックとしてデフォルト値を定義しましょう。 これを行うには、変数名の後に_!_演算子を配置する必要があります。
${attribute!'default value'}
丸括弧を使用して、多くのネストされた属性をラップできます。
たとえば、属性が存在し、ネストされたプロパティと別のネストされたプロパティがあるかどうかを確認するには、すべてをラップします。
${(attribute.nestedProperty.nestedProperty)??}
最後に、すべてをまとめると、これらを静的コンテンツに埋め込むことができます。
<p>Testing is student property exists: ${student???c}</p>
<p>Using default value for missing student: ${student!'John Doe'}</p>
<p>Wrapping student nested properties: ${(student.address.street)???c}</p>
そして、_student_が_null_だった場合、次のように表示されます。
<p>Testing is student property exists: false</p>
<p>Using default value for missing student: John Doe</p>
<p>Wrapping student nested properties: false</p>
  • _ ?? _ *の後に使用される追加の_?c_ディレクティブに注意してください。 ブール値を人間が読める文字列に変換するために行いました。

5. If-Elseタグ

コントロール構造はFreeMarkerに存在し、従来のif-elseはおそらくおなじみです:
<#if condition>
    <!-- block to execute if condition is true -->
<#elseif condition2>
    <!-- block to execute if condition2 is the first true condition -->
<#elseif condition3>
    <!-- block to execute if condition3 is the first true condition -->
<#else>
    <!-- block to execute if no condition is true -->
</#if>
_elseif_および_else_ブランチはオプションですが、条件はブール値に解決する必要があります。
評価を支援するために、次のいずれかを使用します。

*
* x_が_y_と異なる場合にのみ_true_を返す_x!= y
* x lt y_は、_x_が_y_より厳密に小さくなければならないことを意味します。
_lt_の代わりに
<_も使用します
* x gtが_true_と評価されるのは、_x_が厳密に_y_よりも大きい場合のみです
– _gt_の代わりに
> を使用できます
* _x lte y_は、_x_が_y_以下であるかどうかをテストします–代替
_lte_は
⇐ _です
* _x gte y_は、_x_が_y_以上かどうかをテストします–
_gte_の代替は> =
* _x?の存在を確認するには_x ?? _
* _sequence?seqContains(x)_は、_x_の存在を検証します
シーケンス

  • FreeMarkerは、> =と>をFTLタグの終了文字と見なすことに注意することが非常に重要です。

    次のテンプレートのために、それをまとめる:
<#if status??>
    <p>${status.reason}</p>
<#else>
    <p>Missing status!</p>
</#if>
結果のHTMLコードになります。
 <!-- When status attribute exists -->
<p>404 Not Found</p>

<!-- When status attribute is missing -->
<p>Missing status!</p>

6. サブ変数のコンテナー

FreeMarkerには、サブ変数用の3種類のコンテナがあります。
  • _Hashes_はキーと値のペアのシーケンスです-キーは一意である必要があります
    ハッシュの内部には順序がありません

  • _Sequences_は、それぞれに関連付けられたインデックスがあるリストです
    値-注目に値する事実は、サブ変数が異なるタイプになりうることです

  • _Collections_は、アクセスできないシーケンスの特殊なケースです
    サイズまたはインデックスによる値の取得-それでも_list_タグを使用して値を繰り返すことができます!

6.1. アイテムの繰り返し

2つの基本的な方法でコンテナを反復処理できます。 1つ目は、各値を反復処理し、それぞれの値に対してロジックが発生する場所です。
<#list sequence as item>
    <!-- do something with ${item} -->
</#list>
または、_Hash_を繰り返し、キーと値の両方にアクセスする場合:
<#list hash as key, value>
    <!-- do something with ${key} and ${value} -->
</#list>
2番目の形式は、反復のさまざまなステップで発生するロジックを定義できるため、より強力です。
<#list sequence>
    <!-- one-time logic if the sequence is not empty -->
    <#items as item>
        <!-- logic repeated for every item in sequence -->
    </#items>
    <!-- one-time logic if the sequence is not empty -->
<#else>
    <!-- one-time logic if the sequence is empty -->
</#list>
_item_はループ変数の名前を表しますが、必要に応じて名前を変更できます。 _else_ブランチはオプションです。
実践的な例として、いくつかのステータスをリストするテンプレートを定義します。
<#list statuses>
    <ul>
    <#items as status>
        <li>${status}</li>
    </#items>
    </ul>
<#else>
    <p>No statuses available</p>
</#list>
これにより、コンテナが_ [「200 OK」、「404 Not Found」、「500 Internal Server Error」] _の場合に次のHTMLが返されます。
<ul>
<li>200 OK</li>
<li>404 Not Found</li>
<li>500 Internal Server Error</li>
</ul>

6.2. アイテムの取り扱い

ハッシュを使用すると、含まれるキーのみを取得する_keys_と、値のみを取得する_values_という2つの単純な関数を使用できます。
シーケンスはより複雑です。最も便利な機能をグループ化できます。
  • _chunk_および_join_は、サブシーケンスを取得するか、2つのシーケンスを結合します

  • reverse _、 sort、_、および_sortBy_は要素の順序を変更します

  • _first_および_last_は、最初または最後の要素を取得します。
    それぞれ

  • _size_は、シーケンス内の要素の数を表します

  • seqContains _、 seqIndexOf_、または_seqLastIndexOf_は、
    素子

7. タイプ処理

FreeMarkerには、オブジェクトの操作に使用できるhttps://freemarker.apache.org/docs/ref_builtins.html [さまざまな機能](組み込み)が付属しています。 よく使用される関数を見てみましょう。

7.1. 文字列処理

  • _url_および_urlPath_は、例外を除き、文字列をURLエスケープします
    _urlPath_はスラッシュをエスケープしません/

  • jString _、 jsString、_、および_jsonString_はエスケープルールを適用します
    それぞれJava、Javascript、JSONの場合

  • capFirst _、 uncapFirst upperCase lowerCase_、および_capitalize_
    名前からわかるように、文字列の大文字と小文字を変更するのに便利です

  • boolean _、 date time datetime_および_number_は、
    文字列から他の型への変換

    これらの関数のいくつかを使用してみましょう。
<p>${'http://myurl.com/?search=Hello World'?urlPath}</p>
<p>${'Using " in text'?jsString}</p>
<p>${'my value?upperCase}</p>
<p>${'2019-01-12'?date('yyyy-MM-dd')}</p>
上記のテンプレートの出力は次のようになります。
<p>http%3A//myurl.com/%3Fsearch%3DHello%20World</p>
<p>MY VALUE</p>
<p>Using \" in text</p>
<p>12.01.2019</p>
_date_関数を使用する場合、Stringオブジェクトの解析に使用するパターンも渡しました。 * FreeMarkerは、特に指定のない限り、ローカル形式を使用します*。たとえば、日付オブジェクトに使用できる_string_関数で使用します。

7.2. 番号の取り扱い

  • round _、 floor_、および_ceiling_は数値の丸めに役立ちます

  • _abs_は数値の絶対値を返します

  • string_は、数値を文字列に変換します。 4つ渡すこともできます
    事前定義された数値形式:_computer _、
    currency number_、または_percent_または_ [“0。###â€] _などの独自の形式を定義します

    いくつかの数学演算のチェーンをやってみましょう:
<p>${(7.3?round + 3.4?ceiling + 0.1234)?string('0.##')}</p>
<!-- (7 + 4 + 0.1234) with 2 decimals -->
予想どおり、結果の値は_11.12._です

7.3. 日付処理

  • _.now_は現在の日時を表します

  • date _、 time_および_datetime_は、
    日時オブジェクト

  • _string_は日時を文字列に変換します。
    希望の形式、または事前定義された形式を使用

    現在の時刻を取得し、出力を時間と分のみを含む文字列にフォーマットします。
<p>${.now?time?string('HH:mm')}</p>
結果のHTMLは次のようになります。
<p>15:39</p>

8. 例外処理

FreeMarkerテンプレートの例外を処理する2つの方法があります。
最初の方法は、_attempt-recover_タグを使用して、何を実行しようとし、エラーが発生した場合に実行するコードブロックを定義することです。
構文は次のとおりです。
<#attempt>
    <!-- block to try -->
<#recover>
    <!-- block to execute in case of exception -->
</#attempt>
_attempt_タグと_recover_タグは両方とも必須です。 *エラーの場合、試行されたブロックをロールバックし、_recover_セクションのコードのみを実行します*。
この構文を念頭に置いて、テンプレートを次のように定義しましょう。
<p>Preparing to evaluate</p>
<#attempt>
    <p>Attribute is ${attributeWithPossibleValue??}</p>
<#recover>
    <p>Attribute is missing</p>
</#attempt>
<p>Done with the evaluation</p>
_attributeWithPossibleValue_が欠落している場合、以下が表示されます。
<p>Preparing to evaluate</p>
    <p>Attribute is missing</p>
<p>Done with the evaluation</p>
_attributeWithPossibleValue_が存在する場合の出力は次のとおりです。
<p>Preparing to evaluate</p>
    <p>Attribute is 200 OK</p>
<p>Done with the evaluation</p>
2番目の方法は、例外の場合に何をすべきかをFreeMarkerに設定することです。
Spring Bootでは、プロパティファイルを使用してこれを簡単に構成できます。利用可能な構成は次のとおりです。
  • spring.freemarker.setting.template_exception_handler = rethrow
    例外を再スローします

  • _spring.freemarker.setting.template_exception_handler = debug_の出力
    スタックトレース情報をクライアントに送信してから、例外を再スローします。

  • spring.freemarker.setting.template_exception_handler = html_debug
    スタックトレース情報をクライアントに出力し、通常はブラウザで読みやすいようにフォーマットしてから、例外を再スローします。

  • spring.freemarker.setting.template_exception_handler = ignore skips
    失敗した指示、テンプレートの実行を継続させる。

  • spring.freemarker.setting.template_exception_handler = default

9. メソッドの呼び出し

FreeMarkerテンプレートからJavaメソッドを呼び出したい場合があります。 次に、その方法を説明します。

9.1. 静的メンバー

静的メンバーへのアクセスを開始するには、グローバルFreeMarker構成を更新するか、モデルのS__taticModels__型属性を_statics_属性名の下に追加します。
model.addAttribute("statics", new DefaultObjectWrapperBuilder(new Version("2.3.28"))
    .build().getStaticModels());
静的要素へのアクセスは簡単です。
最初に、assignタグを使用してクラスの静的要素をインポートし、次に名前を決定し、最後にJavaクラスパスを決定します。
テンプレートに_Math_クラスをインポートし、静的_PI_フィールドの値を表示して、静的_pow_メソッドを使用する方法を次に示します。
<#assign MathUtils=statics['java.lang.Math']>
<p>PI value: ${MathUtils.PI}</p>
<p>2*10 is: ${MathUtils.pow(2, 10)}</p>
結果のHTMLは次のとおりです。
<p>PI value: 3.142</p>
<p>2*10 is: 1,024</p>

9.2. Beanメンバー

Beanメンバーは非常に簡単にアクセスできます。*ドット(。)*を使用すると、それだけです!
次の例では、モデルに_Random_オブジェクトを追加します。
model.addAttribute("random", new Random());
FreeMarkerテンプレートで、乱数を生成しましょう:
<p>Random value: ${random.nextInt()}</p>
これにより、次のような出力が生成されます。
<p>Random value: 1,329,970,768</p>

9.3. カスタムメソッド

カスタムメソッドを追加する最初の手順は、FreeMarkerの_TemplateMethodModelEx_インターフェイスを実装し、_exec_メソッド内でロジックを定義するクラスを作成することです。
public class LastCharMethod implements TemplateMethodModelEx {
    public Object exec(List arguments) throws TemplateModelException {
        if (arguments.size() != 1 || StringUtils.isEmpty(arguments.get(0)))
            throw new TemplateModelException("Wrong arguments!");
        String argument = arguments.get(0).toString();
        return argument.charAt(argument.length() - 1);
    }
}
モデルの属性として新しいクラスのインスタンスを追加します。
model.addAttribute("lastChar", new LastCharMethod());
次のステップは、テンプレート内で新しいメソッドを使用することです。
<p>Last char example: ${lastChar('mystring')}</p>
最後に、結果の出力は次のとおりです。
<p>Last char example: g</p>

10. 結論

この記事では、プロジェクト内でFreeMarkerテンプレートエンジンを使用する方法を見てきました。 共通の操作、さまざまなオブジェクトの操作方法、およびいくつかのより高度なトピックに焦点を当ててきました。
これらすべてのスニペットの実装は、https://github.com/eugenp/tutorials/tree/master/spring-freemarker [GitHubで]で入手できます。