HTL 使用の手引き getting-started-with-htl

HTML テンプレート言語(HTL)は、Adobe Experience Manager での HTML 用に、サーバーサイドで優先かつ推奨されるテンプレートシステムです。 すべての HTML サーバーサイドテンプレートシステムと同じように、HTL ファイルでも、HTML 本体、基本的なプレゼンテーションロジックおよび実行時に評価される変数を指定することで、ブラウザーに送信される出力を定義します。

このドキュメントでは、HTL の目的の概要と、言語の基本的な概念および構造について説明します。

TIP
このドキュメントでは、HTL の目的と、HTL の基本的な構造および概念の概要について説明します。特定構文の疑問については、HTL の仕様を参照してください。

HTL レイヤー layers

AEMでは、多数のレイヤーが HTL を定義します。

  1. HTL の仕様 - HTL は、プラットフォームに依存しないオープンソースの仕様で、誰でも自由に実装できます。
  2. Sling HTL スクリプティングエンジン - Sling プロジェクトは、AEM で使用される HTL の参照実装を作成しました。
  3. AEM拡張機能 - AEMは Sling HTL スクリプティングエンジン上に構築され、AEM固有の便利な機能を開発者に提供します。

この HTL ドキュメントでは、HTL を使用した AEM ソリューションの開発に焦点を当てています。そのため、必要に応じて外部リソースをリンクし、3 つのレイヤーすべてに接触します。

HTL の基本概念 fundamental-concepts-of-htl

HTML テンプレート言語は式言語を使用して、レンダリングされたマークアップに個々のコンテンツを挿入し、HTML5 データ属性を使用し、マークアップのブロックに対してステートメント(条件や繰り返しなど)を定義します。HTL がコンパイルされて Java サーブレットになると、式と HTL データ属性は両方とも完全にサーバー側で評価され、生成される HTML には何も表示されません。

TIP
このページで示す例の多くを実行するためには、Read Eval Print Loop というライブ実行環境を使用できます。

ブロックと式 blocks-and-expressions

次に示す例は、そのまま template.html ファイルに含めることができます。

<h1 data-sly-test="${properties.jcr:title}">
    ${properties.jcr:title}
</h1>

2 種類の構文は次のように区別できます。

  • ブロックステートメント - <h1>HTMLを条件付きで表示する場合は、data-sly-test element5 データ属性を使用します。 HTL ではこのような属性が複数提供され、これらを使用して HTML 要素に動作を関連付けることができます。すべての属性には data-sly というプレフィックスが付きます。
  • 式言語 - ${ 文字と } 文字は、HTL 式を区切ります。 実行時にこれらの式が評価され、出力 HTML ストリームに値が挿入されます。

両方の構文について詳しくは、HTL の仕様を参照してください。

SLY 要素 the-sly-element

HTL の中心となる概念は、既存のHTML要素を再利用してブロックステートメントを定義できるようにすることです。 これにより、文の開始位置と終了位置を定義する区切り文字を追加する必要がなくなります。 マークアップに注釈を付けることで、HTMLの有効性を損なうことなく静的HTMLを動的テンプレートに変換し、静的ファイルとしても適切に表示します。

ただし、ブロックステートメントを挿入する必要がある正確な場所に、既存の要素が存在しない可能性があります。このような場合は、特別な sly 要素を挿入できます。 この要素は、添付されたブロックステートメントを実行し、それに応じてコンテンツを表示しながら、出力から自動的に削除されます。

以下の例の内容は次のとおりです。

<sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
    <h1>${properties.jcr:title}</h1>
    <p>${properties.jcr:description}</p>
</sly>

次のようなHTMLが出力されるのは、jcr:title プロパティと jcr:description プロパティの両方が定義され、いずれも空でない場合のみです。

<h1>MY TITLE</h1>
<p>MY DESCRIPTION</p>

注意すべきことの 1 つは、既存の要素にブロックステートメントの注釈を付けることができなかった場合にのみ、sly 要素を使用することです。これは、静的なHTMLを動的 sly する際に、言語が提供する値を、静的な値を変えないように要素が抑止するためです。

例えば、前の例が既に div 要素にラップされている場合、追加された sly 要素は不正使用となります。

<div>
    <sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
        <h1>${properties.jcr:title}</h1>
        <p>${properties.jcr:description}</p>
    </sly>
</div>

そして div 要素には、次の条件で注釈が付いている場合があります。

<div data-sly-test="${properties.jcr:title && properties.jcr:description}">
    <h1>${properties.jcr:title}</h1>
    <p>${properties.jcr:description}</p>
</div>

HTL コメント htl-comments

次の例では、1 行目に HTL コメント、2 行目に HTML コメントを表示しています。

<!--/* An HTL Comment */-->
<!-- An HTML Comment -->

HTL のコメントは、HTML コメントに JavaScript 形式の構文が加えられたものです。プロセッサーは、HTL コメント全体とその中の内容を完全に無視し、出力から削除します。

ただし、標準HTMLのコメントの内容は渡され、コメント内の式が評価されます。

HTML コメントに HTL コメントを含めることも、HTL コメントに HTML コメントを含めることもできません。

特別なコンテキスト special-contexts

HTL を最大限に活用するためには、HTL がHTML構文に基づいているという関係をよく理解することが重要です。

詳しくは、HTL の仕様のコンテキストセクションを表示を参照してください。

要素名と属性名 element-and-attribute-names

式は HTML テキストまたは属性値でのみ指定できます。要素名や属性名の内部には指定できません。指定した場合、有効な HTML として認識されません。要素名を動的に設定するには、対象の要素について data-sly-element ステートメントを使用できます。また、属性名を動的に設定するには、data-sly-attribute ステートメントを使用できます。複数の属性を一度に設定することもできます。

<h1 data-sly-element="${myElementName}" data-sly-attribute="${myAttributeMap}">...</h1>

ブロックステートメントなしのコンテキスト contexts-without-block-statements

HTL ではブロックステートメントの定義にデータ属性を使用するので、次のコンテキスト内ではそのようなブロックステートメントを定義できません。ここで使用できるのは式のみです。

  • HTML コメント
  • script 要素
  • style 要素

理由は、これらのコンテキストの内容がテキストであり HTML ではないことです。含まれる HTML 要素は単純な文字データとみなされます。したがって、実際のHTML要素がないと、実行される属性 data-sly 存在できません。

このアプローチは、大きな制約と思われるかもしれません。 ただし、HTMLテンプレートHTMLでは有効な言語の出力のみを生成する必要があるので、この方法をお勧めします。 この後のロジックにアクセスするための Use-API では、追加ロジックをテンプレートから呼び出す方法を説明しています。これは、このようなコンテキストで複雑な出力を用意する必要があるときに使用できます。バックエンドからフロントエンドスクリプトにデータを送信するには、コンポーネントのロジックを含んだ JSON 文字列を生成し、単純な HTL 式を使用して data 属性に配置します。

次の例は、HTMLのコメントの動作を示していますが、スクリプト要素またはスタイル要素では同じ動作が見られます。

<!--
    The title is: ${properties.jcr:title}
    <h1 data-sly-test="${properties.jcr:title}">${properties.jcr:title}</h1>
-->

次のようなHTMLを出力します。

<!--
    The title is: MY TITLE
    <h1 data-sly-test="MY TITLE">MY TITLE</h1>
-->

明示的なコンテキストが必要 explicit-contexts-required

この後の自動コンテキスト認識エスケープで説明するように、HTL の目的の 1 つは、コンテキスト認識エスケープをすべての式に自動的に適用して、クロスサイトスクリプティング(XSS)によって脆弱性がもたらされるリスクを低減することです。HTL はHTMLのマークアップで式のコンテキストを検出しますが、インライン JavaScriptや CSS は分析しないので、開発者はこれらの式の正確なコンテキストを指定する必要があります。

正しいエスケープを適用しないと XSS の脆弱性が引き起こされるので、HTL は、script と style のコンテキストが宣言されていないとき、それらのコンテキストにあるすべての式の出力を削除します。

次の例では、script と style の内部に指定された式のコンテキストを設定する方法を示します。

<script> var trackingID = "${myTrackingID @ context='scriptString'}"; </script>
<style> a { font-family: "${myFont @ context='styleString'}"; } </style>

エスケープの制御方法について詳しくは、HTL の仕様の式言語の表示コンテキストの節を参照してください。

HTL の一般的な機能 general-capabilities-of-htl

ここでは、HTML テンプレート言語の一般的な機能を簡単に説明します。

ロジックにアクセスするための Use-API use-api-for-accessing-logic

HTML テンプレート言語(HTL)Java Use-API を使用すると、HTL ファイルから data-sly-use を介してカスタム Java クラスのヘルパーメソッドへのアクセスが可能になります。このプロセスにより、複雑なビジネスロジックをすべて Java コードでカプセル化し、HTL コードではマークアップの直接作成処理のみを行うことができます。

詳しくは、HTL Java Use-API を参照してください。

自動コンテキスト認識エスケープ automatic-context-aware-escaping

次の例をご覧ください。

<p data-sly-use.logic="logic.js">
    <a href="${logic.link}" title="${logic.title}">
        ${logic.text}
    </a>
</p>

ほとんどのテンプレート言語では、この例によってクロスサイトスクリプティング(XSS)の脆弱性が生じる可能性があります。すべての変数が自動的に HTML エスケープされた場合でも、href 属性を特に URL エスケープする必要があるからです。これは忘れやすく、自動的に検出するのも難しいので、最も一般的なエラーの 1 つです。

これをサポートするため、HTMLテンプレート言語では、配置されたコンテキストに応じて各変数を自動的にエスケープします。 このアプローチが可能なのは、HTL がHTMLの構文を認識しているためです。

次の logic.js ファイルがあるとします。

use(function () {
    return {
        link:  "#my link's safe",
        title: "my title's safe",
        text:  "my text's safe"
    };
});

最初の例の結果は、次のようになります。

<p>
    <a href="#my%20link%27s%20safe" title="my title&#39;s safe">
        my text&#39;s safe
    </a>
</p>

HTL は href 属性と src 属性を URI コンテキストでエスケープする必要があることを認識しているため、2 つの属性のエスケープ方法が異なることに注意してください。また、URI の先頭に「javascript:」が付いている場合は、コンテキストが明示的に別のものに変更されない限り、その属性全体が削除されます。

エスケープを制御する方法について詳しくは、HTL の仕様の式言語の表示コンテキストの節を参照してください。

空の属性の自動削除 automatic-removal-of-empty-attributes

次の例をご覧ください。

<p class="${properties.class}">some text</p>

class プロパティの値が空の場合、HTMLテンプレート言語は、出力から class 属性全体を自動的に削除します。

ここでも、HTL がHTMLの構文を認識し、動的な値を持つ属性を値が空でない場合のみ条件によって表示できるので、このプロセスが可能です。 この理由は、マークアップを無効にして判読不能にする属性の周囲に条件ブロックを追加するのを回避できるので、非常に便利です。

また、式に含まれる変数の型も重要です。

  • String:

    • not empty: 文字列を属性値として設定します。
    • 空の場合: ​属性全体を削除します。
  • 数値: 値を属性値として設定します。

  • Boolean:

    • true: ​属性を値なしで表示します(ブール値の HTML 属性として)。
    • false: ​属性全体を削除します。

次の例は、ブール式を使用してブール値の HTML 属性を制御する方法を示します。

<input type="checkbox" checked="${properties.isChecked}"/>

属性を設定する場合は、data-sly-attribute ステートメントも使用できます。

HTL の一般的な使用方法 common-patterns-with-htl

ここでは、一般的なシナリオをいくつか紹介します。 ここでは、HTMLテンプレート言語を使用して、これらのシナリオを解決する最適な方法について説明します。

クライアントライブラリの読み込み loading-client-libraries

HTL では、クライアントライブラリは AEM 提供のヘルパーテンプレートを介して読み込まれます。テンプレートには data-sly-use を使用してアクセスできます。このファイルには 3 つのテンプレートが含まれ、data-sly-call で呼び出すことができます。

  • css - 参照されるクライアントライブラリの CSS ファイルのみを読み込みます。
  • js - 参照されるクライアントライブラリの JavaScript ファイルのみを読み込みます。
  • all - 参照されるクライアントライブラリのすべてのファイル(CSS と JavaScript 両方)を読み込みます。

各ヘルパーテンプレートには、必要なクライアントライブラリを参照するための categories オプションを指定できます。このオプションには、文字列値の配列またはコンマ区切り値のリストを含む文字列を指定できます。

次に 2 つの短い例を示します。

複数のクライアントライブラリをすべて一度に読み込む loading-multiple-client-libraries-fully-at-once

<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
     data-sly-call="${clientlib.all @ categories=['myCategory1', 'myCategory2']}"/>

ページの異なるセクションでクライアントライブラリを参照する referencing-a-client-library-in-different-sections-of-a-page

<!doctype html>
<html data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
    <head>
        <!-- HTML meta-data -->
        <sly data-sly-call="${clientlib.css @ categories='myCategory'}"/>
    </head>
    <body>
        <!-- page content -->
        <sly data-sly-call="${clientlib.js @ categories='myCategory'}"/>
    </body>
</html>

この例では、HTMLheadbody 要素が別々のファイルに存在する場合、clientlib.html テンプレートは、テンプレートが必要な各ファイルに読み込まれる必要があります。

HTL の仕様の template および call ステートメントに関する節では、テンプレートの宣言と呼び出しの仕組みについて詳しく記載しています。

データをクライアントに渡す passing-data-to-the-client

一般的にデータをクライアントに渡すための最適かつ洗練された方法は、HTL では特に、data 属性を使用することです。

次の例は、オブジェクトを JSON にシリアル化してクライアントに渡す方法を示しています(Java でも可能)。 その後、data 属性に簡単に配置できます。

<!--/* template.html file: */-->
<div data-sly-use.logic="logic.js" data-json="${logic.json}">...</div>
/* logic.js file: */
use(function () {
    var myData = {
        str: "foo",
        arr: [1, 2, 3]
    };

    return {
        json: JSON.stringify(myData)
    };
});

その後、クライアント側の JavaScript がどのようにその属性にアクセスして再び JSON を解析するかは簡単に推測できます。このアプローチは、対応するJavaScriptをクライアントライブラリに配置します。例えば、次のようになります。

var elements = document.querySelectorAll("[data-json]");
for (var i = 0; i < elements.length; i++) {
    var obj = JSON.parse(elements[i].dataset.json);
    //console.log(obj);
}

クライアントサイドテンプレートの使用 working-with-client-side-templates

特別なコンテキストの制約解除の節で説明した方法が有効に使用できる特別なケースの 1 つは、scrip 要素に配置するクライアント側のテンプレート(例えば Handlebars)を記述する場合です。このケースでこの方法を安全に使用できる理由は、想定とは異なり script 要素に JavaScript は含まれず HTML 要素が含まれるからです。その仕組みを次の例で示します。

<!--/* template.html file: */-->
<script id="entry-section" type="text/template"
    data-sly-include="entry-section.html"></script>

<!--/* entry-section.html file: */-->
<div class="entry-section">
    <h2 data-sly-test="${properties.entrySectionTitle}">
        ${properties.entrySectionTitle}
    </h2>
    {{#each entry}}<h3><a href="{{link}}">{{title}}</a></h3>{{/each}}
</div>

Handlebars テンプレートのコンテンツは独自のファイルに分離されているので、script 要素のマークアップには明示的なコンテキストのない HTL ブロックステートメントを含めることができます。 また、この例に示すように、サーバー側で実行される HTL (h2 要素)を、Handlebars のようなクライアント側で実行されるテンプレート言語(h3 要素)と混在させることができます。

ただし、最新の方法では HTML template 要素が代わりに使用されます。この方法には、テンプレートの内容を個別のファイルに分離する必要がないというメリットがあります。

特別なコンテキストの制約解除 lifting-limitations-of-special-contexts

特殊なケースですが、script、style、コメントのコンテキストの制約を回避する必要がある場合には、それらの内容を別の HTL ファイルに分離することができます。HTL は、独自のファイル内のすべてを標準のHTMLフラグメントとして解釈し、インクルード元の限定コンテキストを無視します。

この例は、このページの後半にあるクライアント側テンプレートの使用を参照してください。

CAUTION
この方法では、クロスサイトスクリプティング(XSS)の脆弱性が発生する可能性があります。 このアプローチを使用する場合は、セキュリティの側面を慎重に検討する必要があります。 通常は、このやり方を使用しなくても同じ処理を実装するさらに適切な方法があります。
recommendation-more-help
86859df1-0285-4512-b293-0ef9cbea5ee8