波括弧を展開しない (@verbatim)

a-blog cms のテンプレートエンジンでは変数に波括弧を使用しているため、JavaScriptなどの波括弧とぶつかってしまい、そのままでは正常にJavaScriptが動作しません。 JavaScriptを正常に動作させるためには、波括弧をバックスラッシュでエスケープする必要があります。

<div id="app">
  \{\{ message \}\}
</div>

@verbatimブロック

テンプレートの広い箇所でJavaScript変数を表示する場合は、 HTMLを @verbatim ブロックで囲めば、波括弧を1つ1つをバックスラッシュでエスケープする必要がなくなります。

Ver. 2.10.8 で追加

@verbatim
<div id="app">
{{ message }}
</div>
@endverbatim

複数のエリアを同時に更新する hx-swap-oob

a-blog cmspost include で出来なかった事で htmx を採用する事でできるようになる大きなところとして「複数のエリアを同時に更新」が可能になったところではないでしょうか。


上記は htmx を活用したサンプルサイトです。 合わせてご覧ください。


hx-swap-oob 属性とは

要素を置換する場所を指定するには、hx-target属性を使用します。一方で、hx-swap-oob(Out-Of-Band Swapの略)属性を使うことで、hx-targetで指定された要素以外にも、同時に他の部分を置換することができます。

更新前のページで事前準備

更新をかけたい <div>要素について名前をつけておく必要がありますので、id="main-contents"id="topicpath" という属性を設定します。


一覧ページから詳細ページへのリンク

一覧ページから詳細ページへのリンクとしては以下のように記述します。複数箇所の更新の際にも呼び出し側の記述は特に変化はありません。通常の htmxhx-get などの記述になります。

<a href="{url}" 
  hx-get="{url}/tpl/include/htmx/realestate-body.html"
  hx-push-url="{url}"
  hx-target="#main-contents"
  hx-swap="innerHTML"
  hx-ext="ajax-header" 
class="card-link">

呼び出されるテンプレート側の記述

呼ばれるテンプレートには、以下のような記述をします。hx-swap-oob="true" と一緒に書かれている id属性 の部分が置き換わる事になります。

<div id="main-contents" hx-swap-oob="true">
<!-- BEGIN_MODULE Entry_Body -->
(略)
<!-- END_MODULE Entry_Body -->
</div>

<div id="topicpath" hx-swap-oob="true">
<!-- BEGIN_MODULE Topicpath -->
(略)
<!-- END_MODULE Topicpath -->
</div>

<!-- BEGIN_MODULE Ogp -->
<title>htmx:{title}</title>
<!-- END_MODULE Ogp -->

おまけ:タイトルタグの更新

上記では、id="main-contents"id="topicpath" の更新をするように書いていましたが、おまけで <title> の更新もできるようにします。 <title> については、今回の hx-swap-oob とも関係なく読み込んだファイルに <title>タグがあれば上書きをする仕様とのことです。

a-blog cms の場合 Ogp モジュールで <title>を組み立てる処理がありますので、そちらを利用します。 また、テストで更新できたことを分かりやすいようにテスト用のサイトではタイトルタグに htmx: を前につけて htmx でコンテンツを更新したことが分かりやすいようにしています。

htmxで browser history API を活用する方法と a-blog cms 実装のポイント

htmx は標準で browser history API をサポートしており、属性に hx-push-url="true" を追加することで、これを簡単に利用できます。a-blog cms をバックエンドで使用する際の注意点をいくつか紹介します。

※(例)と書かれている部分は実際には動作しません。

hx-push-url 属性を設定して正しく動作する GET

記事詳細(例)

<a>タグでの GETリクエストに対しては、htmx のドキュメントに従い、hx-get属性と hx-push-url属性を追加することを推奨します。

<a href="entry-1.html" hx-get="entry-1.html" hx-push-url="true">記事詳細</a>

hx-push-url 属性を設定しても正しく動作しない POST

(例)
この部分に結果が表示されます

POSTリクエストのケースでは、例えば a-blog cms でカスタムフィールド station を選択するための <select> を含むフォームを使用し、結果を表示するテンプレートとして include/htmx/result.html を指定する方法があります。

<form hx-post="" hx-push-url="true" hx-trigger="submit" hx-target="#search-result" hx-ext="ajax-header">
 <select name="station"> 
  <option value="Tokyo">Tokyo</option>
  <option value="Osaka">Osaka</option>
  <option value="Nagoya">Nagoya</option>
 </select>
 <input type="hidden" name="field[]" value="station">
  <input type="hidden" name="tpl" value="include/htmx/result.html">
<input type="submit" name="ACMS_POST_2GET" value="検索" >
</form>
<div id="search-result">この部分に結果が表示されます</div>

a-blog cms の仕様として、<form> の POST 時 name="ACMS_POST_2GET" が送られてくると、検索条件( station = Nagoya )から URL を組み立て、その後リダイレクトされ GET に変換され、以下のような URL でアクセスした結果を取得できます。

https://example.com/htmx/result.html/field/station/Nagoya/

ここで問題が発生します。 上記の URL がブラウザに表示され履歴にも登録されますが、この URL にアクセスすると Ajax で追加される部分的な HTML になってしまいます。この検索結果で履歴に登録されて欲しい URLは、テンプレートファイル include/htmx/result.html のパスが無い以下の URL であって欲しいのです。

https://example.com/field/station/Nagoya/

hx-push-url 属性を設定しても正しく動作する POST 解決編

ここまでで a-blog cms POSTリクエスト後に URL を組み立てる ACMS_POST_2GET の処理が、htmxhx-push-url と相性が悪い状況を解決する方法を考えてみました。

以下に示す JavaScriptコードを追加することで、hx-push-url 属性の動作を維持しつつ、希望する URL形式を browser history API に正しく渡すことができるようになります。

<script>
  addEventListener('htmx:beforeHistoryUpdate', function (event) {
    const proposedUrl = event.detail.history.path;
    const customUrl = proposedUrl.replace(/\/include\/htmx\/.*\.html/, '');
    event.detail.history.path = customUrl;
  });
</script>

処理としては htmx で History を更新する前の path を取得し、/include/htmx/ 〜 .html 部分を削除するという処理を追加しています。

一般的に a-blog cms の部分的なファイルは include ディレクトリに整理して保存するというルールが定着していますので、htmx で読み込むテンプレートファイルは、その include ディレクトリ内に htmx を用意し、その htmx ディレクトリ内で管理というルールでテーマを構築というルールにする事で上記の JavaScript が有効的に動作させることができます。