テンプレート記法

パーツの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>

動作:

例:

<!-- テンプレート -->
<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のみをサポートしています。

バックエンドデータの参照

バックエンドから渡されたデータをテンプレート内で参照できます。動的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>

動作:

使用例:

<!-- 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>

ループ変数の参照:

ループ内での他のテンプレート構文:

ループ内では、通常のテンプレート構文({$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>

制限事項:

完全な例:

<!-- 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>

動作:

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="$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>要素ごと削除される -->

動作:

使用例:

<!-- オプショナルフィールドで親要素を削除したい場合 -->
<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検証が適用されますが、サーバー側での検証を必ず実装してください。