テンプレート記法
パーツのHTMLに書く独自記法のリファレンスです。フィールド・選択肢・バリデーション・バックエンドデータ参照・z-* 制御属性を扱います。
テンプレート記法
ZeroCode.jsでは、カスタムHTMLテンプレート構文を使用して動的なコンテンツを定義します。
フィールド記法
テキストフィールド
{$fieldName:defaultValue}
単一行のテキスト入力フィールドとして表示されます。
<h1>{$title:タイトル}</h1>
デフォルト値は省略できます({$title})。URL
やメールアドレスのように、デフォルト値に「.」や「:」を含めることもできます({$url:https://example.com/contact})。型付きフィールドでもデフォルト値を省略でき({$content::rich})、バリデーション(:required
など)は型の前後どちらに書いても有効です。
テキストエリアフィールド
{$fieldName:defaultValue:textarea}
複数行のテキスト入力フィールドとして表示されます。
<p>{$description:説明文:textarea}</p>
リッチテキストフィールド
{$fieldName:defaultValue:rich}
リッチテキストエディター(TipTap)として表示されます。HTMLタグを含むテキストを編集できます。
<div>{$content:本文:rich}</div>
画像フィールド
{$fieldName:defaultValue:image}
画像選択フィールドとして表示されます。画像管理から画像を選択できます。
<img src="{$image:default.jpg:image}" alt="画像" />
グループ化されたフィールド
フィールド名にドット(.)を使用して、フィールドをグループ化できます。
{$fieldName.groupName:defaultValue}
例:
<div>
<h2>{$hero.title:ヒーロータイトル}</h2>
<p>{$hero.description:ヒーロー説明}</p>
</div>
グループ化されたフィールドは、編集パネルでグループとして表示され、整理された編集が可能です。
オプショナルフィールド(空入力制御)
フィールド名の後に?を追加することで、オプショナルフィールドとして定義できます。ユーザーが何も入力しなかった場合、フィールドの値はundefinedになります。
{$fieldName?:defaultValue}
{$fieldName?:defaultValue:rich}
{$fieldName?:defaultValue:image}
{$fieldName.groupName?:defaultValue}
例:
<div class="section">
<div class="section__title">{$title:タイトル(必須)}</div>
<div class="section__subtitle">{$subtitle?:サブタイトル(オプション)}</div>
<img src="{$optional_image?:default.jpg:image}" alt="{$optional_alt?:画像の説明}" />
</div>
動作:
-
ユーザーが何も入力しなかった場合、フィールドの値は
undefinedになります(デフォルト値は使用されません) - 編集パネルでは、オプショナルフィールドは空欄として表示されます
-
属性値がオプショナルフィールドのみで構成されていて、値が
undefined(または空)の場合、その属性自体がHTMLから削除されます -
基本動作では、オプショナルフィールドが空の場合でも親要素(タグ)は残ります(例:
<p></p>)。親要素ごと削除したい場合は、z-empty属性を使用してください(詳細は「条件分岐」セクションの「z-empty 属性」を参照)
例:
<!-- テンプレート -->
<img src="{$optional_image?:default.jpg:image}" alt="{$optional_alt?:画像の説明}" />
<!-- optional_imageとoptional_altが両方undefinedの場合 -->
<img />
<!-- optional_imageに値がある場合 -->
<img src="image.jpg" alt="画像の説明" />
親要素を削除したい場合:
オプショナルフィールドが空の場合、基本動作では親要素(タグ)が残ります(例:
<p></p>)。親要素ごと削除したい場合は、z-empty属性を使用してください。
<!-- 基本動作:空タグが残る -->
<p>{$subtitle?:サブタイトル}</p>
<!-- subtitleがundefinedの場合、<p></p>が残る -->
<!-- z-emptyを使用:親要素を削除 -->
<div z-empty="$subtitle">
<p>{$subtitle?:サブタイトル}</p>
</div>
<!-- subtitleがundefinedの場合、<div>要素ごと削除される -->
注意: 属性値全体がオプショナルフィールドのみで構成されている場合のみ属性が削除されます。他のテキストが含まれている場合は削除されません。
補足:
z-empty属性の詳細については、「条件分岐」セクションの「z-empty
属性」を参照してください。
バリデーション記法
フィールドにバリデーションルールを追加できます。フロントエンドでの同期バリデーションと、バックエンドでの非同期バリデーションの両方をサポートしています。
{$fieldName:defaultValue:required}
{$fieldName:defaultValue:max=100}
{$fieldName:defaultValue:required:max=50}
{$fieldName:defaultValue:readonly}
{$fieldName:defaultValue:disabled}
例:
<div class="form">
<input type="text" name="title" value="{$title:タイトル:required:max=100}" />
<input type="email" name="email" value="{$email:メールアドレス:required}" />
<textarea name="description">{$description:説明:max=500}</textarea>
<input type="text" name="readonly_field" value="{$readonly_field:読み取り専用:readonly}" />
<input type="text" name="disabled_field" value="{$disabled_field:無効化:disabled}" />
</div>
バリデーションルール:
-
:required- 必須フィールド。空の場合はエラーメッセージが表示されます。 -
:max=N- 最大文字数制限(Nは数値)。指定した文字数を超えるとエラーメッセージが表示されます。 -
:readonly- 読み取り専用フィールド。編集できませんが、値は送信されます。 :disabled- 無効化フィールド。編集できず、値も送信されません。
動作:
-
フロントエンドバリデーション(同期):
requiredとmaxは、ユーザーが入力中にリアルタイムでチェックされます。エラーがある場合は、フィールドの下にエラーメッセージが表示されます。 -
バックエンドバリデーション(非同期):
保存ボタンをクリックすると、
save-requestイベントが発火します。バックエンドでバリデーションを行い、エラーがある場合はsave-resultイベントでエラーを返してください。詳細は「保存リクエスト」セクションを参照してください。 - バリデーションエラーは、編集パネルが開いている場合は該当フィールドに表示され、編集パネルが閉じている場合は画面上部にエラーバナーが表示されます。
注意:
複雑なバリデーション(メール形式チェック、重複チェックなど)は、バックエンドで実施することを推奨します。フロントエンドでは、requiredとmaxのみをサポートしています。
バックエンドデータの参照
バックエンドから渡されたデータをテンプレート内で参照できます。動的URLや共通で使用するデータ(店舗ID、ユーザー情報など)を表示する際に便利です。
{@fieldName}
{@fieldName:defaultValue}
{@items[0].name}
{@items.length}
/shop/{shop_id}/products
例:
<div class="shop-header">
<h1>{@title:店舗名未設定}</h1>
<a href="{@url:/}">店舗詳細</a>
<p>アイテム数: {@items.length}</p>
<p>最初のアイテム: {@items[0].name:名称未設定}</p>
<a href="/shop/{shop_id}/products">商品一覧</a>
</div>
動作:
- 基本的なデータ参照:
{@fieldName}でバックエンドデータを参照 -
デフォルト付き参照:
{@fieldName:defaultValue}で、データ未取得・パス不存在・null/undefined/空文字のときdefaultValueを表示(backend-data未指定時もデフォルト値を使用) - ネストされたオブジェクト:
{@user.name}のようにドット記法で参照 - 配列の参照:
{@items[0]}で配列の要素を参照 - 配列のlength:
{@items.length}で配列の長さを取得 - URL内プレースホルダー:
/shop/{shop_id}/のようにURL内に直接記述可能
使用例:
<!-- Web Componentにバックエンドデータを渡す -->
<zcode-cms
backend-data='{"title":"A店舗","url":"/shop/123/","shop_id":"123","items":[{"name":"商品1"}]}'
>
<!-- テンプレート内で {@title}, {@url}, {@items[0].name} などが使用可能 -->
</zcode-cms>
注意:
バックエンドデータは信頼できるソースからのみ使用してください。{@fieldName}
で存在しないパスを参照した場合は空文字列が返されます。{@fieldName:defaultValue}
を使うと、その場合にデフォルト値を表示できます。
z-for ループ記法
バックエンドデータの配列をループ表示するには、z-for属性を使用します。現在はバックエンドデータの配列のみをサポートしています。
<div z-for="item in {@items}">
<!-- ループ内のコンテンツ -->
</div>
構文:
z-for="変数名 in {@配列パス}"
例:
<!-- 店舗一覧をループ表示 -->
<div class="shop-list">
<div z-for="shop in {@shops}" class="shop-item">
<h2>{shop.name}</h2>
<p>{shop.description}</p>
<a href="{shop.url}">詳細を見る</a>
</div>
</div>
<!-- 商品一覧をループ表示 -->
<div class="product-list">
<div z-for="product in {@products}" class="product-item">
<h3>{product.name}</h3>
<p>価格: {product.price}円</p>
<p>カテゴリ: {product.category}</p>
</div>
</div>
ループ変数の参照:
-
{変数名.プロパティ名}: ループ変数のプロパティを参照(例:{shop.name}) -
{変数名.ネストされた.プロパティ}: ネストされたプロパティを参照(例:{shop.address.city}) -
{変数名}: ループ変数自体を参照(オブジェクトの場合はJSON文字列として表示)
ループ内での他のテンプレート構文:
ループ内では、通常のテンプレート構文({$field}、{@data}など)も使用できます。
<div z-for="shop in {@shops}" class="shop-item">
<h2>{shop.name}</h2>
<p>{$description:説明文}</p>
<a href="/shop/{@shop_id}/{shop.id}/">詳細</a>
</div>
制限事項:
- 現在はバックエンドデータの配列のみをサポート(コンポーネントデータの配列は非対応)
- ネストループは非対応
-
インデックス変数は非対応(
z-for="(item, index) in {@items}"のような構文は使用不可) - 空の配列の場合は、ループ要素全体が削除されます
完全な例:
<!-- HTML -->
<zcode-cms id="cms" backend-data='{"shops": [{"id": "1", "name": "A店舗", "url": "/shop/1/"}, {"id": "2", "name": "B店舗", "url": "/shop/2/"}]}'>
<link slot="css" rel="stylesheet" href="/css/common.css" />
</zcode-cms>
<!-- パーツテンプレート -->
<div class="section">
<div class="section__head">
<div class="section__title">{$title:店舗一覧}</div>
</div>
<div class="section__contents">
<div class="section__items">
<div z-for="shop in {@shops}" class="shop-item">
<h2>{shop.name}</h2>
<a href="{shop.url}">詳細を見る</a>
</div>
</div>
</div>
</div>
注意: z-forループ記法は、バックエンドデータの配列を表示するためのシンプルな実装です。将来的には、コンポーネントデータの配列やネストループなどの機能が追加される予定です。
選択肢記法
ラジオボタン(単一選択)
($fieldName:option1|option2|option3)
パイプ(|)で区切られたオプションから1つを選択します。
<div>($color:red|blue|green)</div>
チェックボックス(複数選択)
($fieldName:option1,option2,option3)
カンマ(,)で区切られたオプションから複数を選択できます。
<div>($tags:tag1,tag2,tag3)</div>
セレクトボックス(単一選択)
($fieldName@:option1|option2|option3)
アットマーク(@)とパイプ(|)で区切られたオプションから1つを選択します。
<div>($size@:S|M|L)</div>
セレクトボックス(複数選択)
($fieldName@:option1,option2,option3)
アットマーク(@)とカンマ(,)で区切られたオプションから複数を選択できます。
<div>($categories@:cat1,cat2,cat3)</div>
条件分岐
z-if は表示するかしないかを決めます。fieldName に紐づく形ではありません(fieldName に紐づくのは z-empty)。属性値に指定したキーの真偽で、要素を表示または削除します。
<element z-if="showContent">
<!-- 条件が真の場合に表示 -->
</element>
<div z-if="showContent">
<p>コンテンツが表示されます</p>
</div>
動作:
- 指定キーの値が真(
true、非空文字列など)の場合、要素を表示 -
指定キーの値が偽(
false、null、空文字列、0)の場合は要素を削除 - 指定キーが存在しない(
undefined)場合は表示として扱う
z-tag 属性(タグ名の動的変更)
<element z-tag="$tagName:h1|h2|h3">
<!-- タグ名を動的に変更 -->
</element>
HTMLタグ名を動的に変更できます。テンプレートで書いたタグ名がデフォルト値として使用されます。
<!-- 見出しタグを動的に変更(h2をデフォルト) -->
<h2 z-tag="$headingTag:h1|h2|h3" class="title">{$title:タイトル}</h2>
<!-- headingTagが"h1"の場合 → <h1 class="title">タイトル</h1> -->
<!-- headingTagが"h2"の場合 → <h2 class="title">タイトル</h2> -->
<!-- 選択肢を指定しない場合(全量表示) -->
<div z-tag="$containerTag" class="container">
{$content:コンテンツ}
</div>
動作:
-
デフォルト値: テンプレートで書いたタグ名(例:
<h2>)がデフォルト値として使用されます -
選択肢の指定:
z-tag="$tagName:h1|h2|h3"のように、パイプ(|)で区切って選択肢を指定できます - 全量表示: 選択肢を指定しない場合、すべての有効なタグが選択肢として表示されます
-
属性の保持:
タグ名が変更されても、
class、idなどの属性は保持されます - 子要素の保持: タグ名が変更されても、子要素は保持されます
対応タグ:
-
見出し:
h1,h2,h3,h4,h5,h6 - コンテナ:
div,p,span - リスト:
li,ul,ol -
セマンティック:
section,article,aside,nav,header,footer,main -
その他:
figure,figcaption,blockquote,pre,code -
テーブル:
table,thead,tbody,tr,th,td
使用例:
<!-- 見出しタグを動的に変更 -->
<h2 z-tag="$headingTag:h1|h2|h3" class="title">{$title:タイトル}</h2>
<!-- リストアイテムのタグを変更 -->
<li z-tag="$itemTag:li|div|span" class="list-item">{$item:項目}</li>
<!-- コンテナタグを変更 -->
<div z-tag="$containerTag:div|section|article" class="container">
{$content:コンテンツ}
</div>
注意: テンプレートで書いたタグ名が選択肢に含まれていない場合、開発環境では警告が表示され、選択肢の最初の値がデフォルト値として使用されます。
z-empty 属性(fieldName に紐づく・空なら親要素削除)
<element z-empty="$fieldName">
<!-- フィールドが空の場合、要素ごと削除 -->
</element>
z-empty は
$fieldName
で指定したフィールドに紐づきます。そのフィールドが空(undefined、null、空文字列、または実質的に空のrichテキスト)の場合、親要素を削除します。基本動作では空タグが残るため、親ごと消したいときだけ
z-empty を使ってください。
<!-- 基本動作:空タグが残る -->
<p>{$subtitle?:サブタイトル}</p>
<!-- subtitleがundefinedの場合、<p></p>が残る -->
<!-- z-empty:親要素を削除 -->
<div z-empty="$subtitle">
<p>{$subtitle?:サブタイトル}</p>
</div>
<!-- subtitleがundefinedの場合、<div>要素ごと削除される -->
動作:
- フィールドが
undefined、null、空文字列の場合、要素を削除 -
richテキストが実質的に空(
<p></p>、<p> </p>など)の場合も要素を削除 - フィールドに値がある場合は、要素を表示
使用例:
<!-- オプショナルフィールドで親要素を削除したい場合 -->
<div z-empty="$subtitle">
<p>{$subtitle?:サブタイトル}</p>
</div>
<!-- richテキストが空の場合も削除 -->
<div z-empty="$content">
<div>{$content?:デフォルトコンテンツ:rich}</div>
</div>
<!-- textareaフィールドでも使用可能 -->
<div z-empty="$description">
<div>{$description?:説明文:textarea}</div>
</div>
注意:
fieldName に紐づくのは z-empty です。z-if は表示 on/off
のみで、field には紐づきません。
スロット(ネスト構造)
<element z-slot="slotName">
<!-- スロットコンテンツ -->
</element>
スロットを使用して、パーツ内に他のパーツをネストできます。
<div class="features">
<div z-slot="items">
<!-- ここに子パーツが追加されます -->
</div>
</div>
スロットの制限
PartDataのslotsプロパティでallowedPartsを指定することで、スロットに追加可能なパーツを制限できます。
{
"id": "feature-list",
"title": "機能一覧",
"body": "<div z-slot=\"items\"></div>",
"slots": {
"items": {
"allowedParts": ["feature-item-1", "feature-item-2"]
}
}
}
セキュリティ注意: テンプレート構文で属性値にユーザー入力を設定する場合、基本的なエスケープ処理とURL検証が適用されますが、サーバー側での検証を必ず実装してください。