Liquid Snippets by ALSEL
🔌 API連携/JSON出力/JS統合上級

クイズ結果用の商品データとフィルタ条件を JSON 出力

選択したコレクションの全商品を JSON 形式で抽出し、クイズの回答ごとに設定されたフィルタ条件(価格・タグ・タイプ・ベンダー・オプション)と紐づけることで、JavaScript 側でリアルタイムに商品をマッチングできるようにする。

用途
クイズセクション内で、ユーザーの回答に基づいてフィルタされた商品を動的に表示するとき。商品データとフィルタルールを JavaScript に渡し、クライアント側で即座にマッチング処理を実行できる。
設置場所
snippets/quiz-data.liquid に配置し、クイズセクション(sections/quiz.liquid など)内で `{% render 'quiz-data' %}` で呼び出す。出力された JSON は <script> タグで window グローバル変数として JavaScript に渡し、フィルタロジック実装で参照する。
注意点
コレクションが指定されていない場合や空の場合、products_data は空配列 `[]` で出力されるため、JavaScript 側でハンドリングが必要。price_filter が未入力のブロックはフィルタオブジェクトから除外されるが、tags_filter / type_filter / vendor_filter / option_filter は空文字列でも配列として出力されるため、JavaScript で空要素を事前に削除するとよい。商品点数が 250 件を超える場合は paginate で分割されるため、複数ページの集約が必要になったら Liquid ロジックを拡張する。
タグ:jsonquizproduct-filterjavascript-integrationcollection

コード

93 行 / liquid
<!-- snippets/quiz-data.liquid -->
{%- assign collection = collections[section.settings.filter_collection] -%}

{%- capture 'products_data' -%}
{% paginate collection.products by 250 %}
[
{%- for product in collection.products -%}
  {%- capture 'product_html' -%}
  <div class="quiz-product">
    <a href="{{ product.url }}">
      <picture class="result-picture">
        {%- assign img = product.featured_image-%}
        <source media="(max-width: 375px)" srcset="{{ img | img_url: '375x' }}, {{ img | img_url: '375x', scale: 2 }} 2x">
        <source media="(max-width: 480px)" srcset="{{ img | img_url: '480x' }}, {{ img | img_url: '480x', scale: 2 }} 2x">
        <source media="(max-width: 640px)" srcset="{{ img | img_url: '640x' }}, {{ img | img_url: '640x', scale: 2 }} 2x">
        <img src="{{ img | img_url: image_max_width }}" srcset="{{ img | img_url: image_max_width }} 1x, {{ img | img_url: image_max_width, scale: 2 }}  2x" alt="{{ img.alt | escape }}">
      </picture>
      {{ product.title }}
    </a>
  </div>
  {%- endcapture -%}
  {
    "available": {{ product.available | json }},
    "title": {{ product.title | json }},
    "type": {{ product.type | downcase | remove: ' ' | split: ',' | json }},
    "tags": {{ product.tags | json | downcase | remove: ' ' }},
    "vendor": {{ product.vendor | json | downcase }},
    "options": {{ product.options_with_values | json | downcase }},
    "price": {{ product.price | divided_by: 100.0 | json }},
    "featured_image": {{ product.featured_image | json }},
    "url": {{ product.url | json }},
    "html": {{ product_html | json }}
  }{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
{% endpaginate %}
{%- endcapture -%}

{%- capture 'filters_data' -%}
{
  {%- for block in section.blocks -%}
    {%- if block.type == 'Answer' -%}
    "answer-{{ block.id }}": [
      {%- if block.settings.price_filter != blank -%}
      { 
        "args": [ {{ block.settings.price_filter | times: 1.0 }}, {{ block.settings.price_filter_operator | json }} ],
        "type": "price"
      }
      {%- endif -%}
      { 
        "args": {{ block.settings.tags_filter | split: ',' | json }},
        "type": "tag"
      }
      { 
        "args": {{ block.settings.type_filter | split: ',' | json }},
        "type": "type"
      }
      { 
        "args": {{ block.settings.vendor_filter | split: ',' | json }},
        "type": "vendor"
      }
      { 
        "args": [ {{ block.settings.option_filter | json }}, {{ block.settings.option_filter_values | split: ',' | json }} ],
        "type": "option"
      }
    ],
    {%- endif -%}
  {%- endfor -%}
}
{%- endcapture -%}
{%- assign filters_data = filters_data | downcase | strip_newlines | remove: ' ' | replace: '}{', '},{' | replace: ',}', '}' -%}

{%- capture 'blocks_data' -%}
{
{%- for block in section.blocks -%}
  {%- if block.type == 'Answer'-%}
    {%- assign index = block.settings.title | handle | split: '-' | last -%}
  {%- else -%}
    {%- assign index = block.type | split: '_' | last -%}
  {%- endif -%}
    "{{ block.id }}" : {{ index | minus: 1 }}
  {%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
}
{%- endcapture -%}

{%- assign question_total = 0 -%}
{%- for block in section.blocks -%}
  {%- if block.type != 'Answer' and block.settings.question_title != blank -%}
    {%- assign question_total = question_total | plus: 1 -%}
  {%- endif -%}
{%- endfor -%}

出典・ライセンス

License:
MIT

このコードは mirceapiturca 著作の MIT ライセンスソースです。 原本の著作権は mirceapiturca が保有します。日本語訳は ALSEL によるものです。

関連項目

🔌 API連携/JSON出力/JS統合中級

定期販売プランのデータ属性生成

商品バリアントと定期販売プランの ID を HTML data 属性として動的に生成するスニペット。ループで複数プランの ID を data-sellingId-1、data-sellingId-2 のように番号付けして出力する。

📁 theme-tools·MIT·9
🔌 API連携/JSON出力/JS統合中級

カスタム要素の親コンポーネント

Web Components の親要素(`<parent-element>`)でラップし、子スニペットを描画するコンポーネント。カスタム要素に依存した構造を実装する際のテンプレート。

📁 theme-tools·MIT·10
🔌 API連携/JSON出力/JS統合上級

javascript タグ内の JS コード整形

Liquid の `{% javascript %}` タグ内に記述した JavaScript コードを Prettier で自動整形する。純粋な JS であれば完全整形、Liquid 変数を含む場合はインデント調整のみで対応する。

📁 theme-tools·MIT·30
🔌 API連携/JSON出力/JS統合上級

JavaScriptタグ内のコード整形テスト

Liquid の {% javascript %} タグ内に埋め込まれた JavaScript コードを Prettier で整形するテストケース。純粋な JS、Liquid 混在、タブ設定、シングルクォート対応など複数のシナリオをカバーしている。

📁 theme-tools·MIT·38
🔌 API連携/JSON出力/JS統合上級

Vue コンポーネントのデモ

Vue コンポーネント、スロット、プロップ、Vuex ストア、グローバルミックス、カスタムディレクティブを一堂に展示するセクション。Shopify Theme Lab フレームワークの主要機能をインタラクティブに試せる。

📁 shopify-theme-lab·MIT·162
🔌 API連携/JSON出力/JS統合上級

サードパーティ製アプリの遅延読み込み最適化

Shopify に統合されたサードパーティ製アプリスクリプトの読み込み方式を管理画面から制御し、ページ表示速度を改善するセクション。スクロール、ユーザーインタラクション、または即座読み込みのいずれかに切り替えられる。

📁 Sections·MIT·769