ShopifyのDawnテーマのお問合せフォームをブロックタイプで、任意の項目にカスタムできるようにするコード

今回は、ある案件で実装したコードを共有したいと思います！
社内で出た「お問合せページの項目を、テーマからカスタマイズできるといいよねー」の言葉から実装したものです。
コピペで簡単に出来るようにファイル内のコードを全て貼り付けます！
その後にブロックタイプについての解説をしていきます！

## contact-form.liquid
contact-form.liquidの中身を全て入れ替えるか、contact-custom-form.liquidを作成し下記のコードを貼り付けてください。
このフォームのカスタム機能を組み込んだDawnのバージョンは３.0.0です。
スタイルの調整は組み込まれてないので、任意で行ってください。

```
{{ 'section-contact-form.css' | asset_url | stylesheet_tag }}

{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px;
    padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px;
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
  }
{%- endstyle -%}

<div class="color-{{ section.settings.color_scheme }} gradient">
  <div class="contact page-width page-width--narrow section-{{ section.id }}-padding">

    {%- comment -%} タイトル {%- endcomment -%}
    <h2 class="title title-wrapper--no-top-margin">{{ section.settings.heading | escape }}</h2>


    {%- form 'contact', id: 'ContactForm', class: 'isolate custom-form' -%}

      {%- if form.posted_successfully? -%}
        {%- comment -%} 成功した時のコメント {%- endcomment -%}
        <div class="form-status form-status-list form__message" tabindex="-1" autofocus>{% render 'icon-success' %} {{ 'templates.contact.form.post_success' | t }}</div>
      {%- elsif form.errors -%}
        {%- comment -%} エラー表示 {%- endcomment -%}
        <div class="form__message">
          <h2 class="form-status caption-large text-body" role="alert" tabindex="-1" autofocus>{% render 'icon-error' %} {{ 'templates.contact.form.error_heading' | t }}</h2>
        </div>
        <ul class="form-status-list caption-large" role="list">
          <li>
            <a href="#ContactForm-email" class="link">
              {{ form.errors.translated_fields['email'] | capitalize }} {{ form.errors.messages['email'] }}
            </a>
          </li>
        </ul>
      {%- endif -%}

      {% for block in section.blocks %}

      {% case block.type %}

        {%- comment -%} Eメール {%- endcomment -%}
        {% when 'email' %}
          <div class="field field--with-error" {{ block.shopify_attributes }}>
            <input
              autocomplete="email"
              type="email"
              id="Form-{{ section.id }}-{{ forloop.index0 }}"
              class="field__input"
              name="contact[email]"
              spellcheck="false"
              autocorrect="off"
              autocapitalize="off"
              value="{% if form.email %}{{ form.email }}{% elsif customer %}{{ customer.email }}{% endif %}"
              required="true"
              {% if form.errors contains 'email' %}
                aria-invalid="true"
                aria-describedby="ContactForm-email-error"
              {% endif %}
              placeholder="{{ 'templates.contact.form.email' | t }}"
            >

            <label class="field__label" for="Form-{{ section.id }}-{{ forloop.index0 }}">
              {{ 'templates.contact.form.email' | t }}
              <span aria-hidden="true">*</span>
            </label>

            {%- if form.errors contains 'email' -%}
              <small class="contact__field-error" id="ContactForm-email-error">
                <span class="visually-hidden">{{ 'accessibility.error' | t }}</span>
                <span class="form__message">{% render 'icon-error' %}{{ form.errors.translated_fields['email'] | capitalize }} {{ form.errors.messages['email'] }}</span>
              </small>
            {%- endif -%}
          </div>

        {%- comment -%} 本文 {%- endcomment -%}
        {% when 'body' %}
        <div class="field" {{ block.shopify_attributes }}>
          <textarea
            rows="10"
            id="ContactForm-body"
            class="text-area field__input"
            required="true"
            name="contact[{{ 'templates.contact.form.comment' | t }}]"
            placeholder="{{ 'templates.contact.form.comment' | t }}"
          >
            {{- form.body -}}
          </textarea>
          <label class="form__label field__label" for="ContactForm-body">
            {{ 'templates.contact.form.comment' | t }}
            <span aria-hidden="true">*</span>
          </label>
        </div>


        {%- comment -%} 1行 テキスト{%- endcomment -%}
        {% when 'text' %}
          <div class="field" {{ block.shopify_attributes }}>
            <input class="field__input"
                    type="text"
                    id="Form-{{ section.id }}-{{ forloop.index0 }}"
                    name="contact[{{ block.settings.title }}]"
                    value=""
                    placeholder="{{ block.settings.title }}"
                    {% if block.settings.check %} required="true" {% endif %}
                    >

            <label class="field__label" for="Form-{{ section.id }}-{{ forloop.index0 }}">
              {{ block.settings.title }}
              {% if block.settings.check %}<span aria-hidden="true">*</span>{% endif %}
            </label>
          </div>


        {%- comment -%} テキストエリア {%- endcomment -%}
        {% when 'textarea' %}
          <div class="field" {{ block.shopify_attributes }}>
            <textarea
              rows="10"
              id="Form-{{ section.id }}-{{ forloop.index0 }}"
              class="text-area field__input"
              name="contact[{{ block.settings.title | handleize | default: 'textarea' }}]"
              placeholder="{{ block.settings.title }}"
              {% if block.settings.check %} required="true" {% endif %}
            >
              {{- form.body -}}
            </textarea>
            <label class="form__label field__label" for="Form-{{ section.id }}-{{ forloop.index0 }}">
              {{ block.settings.title }}
              {% if block.settings.check %}<span aria-hidden="true">*</span>{% endif %}
            </label>
          </div>


        {%- comment -%} 電話番号 {%- endcomment -%}
        {% when 'telephone' %}
          <div class="field" {{ block.shopify_attributes }}>
            <input type="tel"
                    id="ContactForm-phone" class="field__input"
                    autocomplete="tel"
                    name="contact[{{ block.settings.title }}]"
                    pattern="[0-9\-]*"
                    value=""
                    placeholder="{{ block.settings.title }}"
                    {% if block.settings.check %} required="true" {% endif %}
                    >

            <label class="field__label" for="ContactForm-phone">
              {{ block.settings.title }}
              {% if block.settings.check %}<span aria-hidden="true">*</span>{% endif %}
            </label>
          </div>


        {%- comment -%} 名前 {%- endcomment -%}
        {% when 'name' %}
          <div class="field" {{ block.shopify_attributes }}>
            <input class="field__input"
            autocomplete="name"
            type="text"
            id="ContactForm-name"
            required="true"
            name="contact[{{ 'templates.contact.form.name' | t }}]"
            value="{% if form.name %}{{ form.name }}{% elsif customer %}{{ customer.name }}{% endif %}"
            placeholder="{{ 'templates.contact.form.name' | t }}">

            <label class="field__label" for="ContactForm-name">
              {{ 'templates.contact.form.name' | t }}
              <span aria-hidden="true">*</span>
            </label>
          </div>


        {%- comment -%} 見出し {%- endcomment -%}
        {% when 'formHeading' %}
          <div class="custom-form__block custom-form__block--heading form-item" {{ block.shopify_attributes }}>
            <p class="strong">{{ block.settings.title }}</p>
          </div>


        {%- comment -%} 装飾 {%- endcomment -%}
        {% when 'spacer' %}
          {%- assign hr_class = 'hr--clear' -%}
          {%- if block.settings.line -%}
            {%- assign hr_class = '' -%}
          {%- endif -%}
          <div class="form-item" {{ block.shopify_attributes }}>
            <hr class="{{ hr_class }}">
          </div>

        {%- comment -%} チェックボックス {%- endcomment -%}
        {% when 'checkbox' %}
          <div class="field no-border" {{ block.shopify_attributes }}>
            <label for="Form-{{ section.id }}-{{ forloop.index0 }}">
              <input type="checkbox"
                      id="Form-{{ section.id }}-{{ forloop.index0 }}"
                      class="contactFormCheckbox"
                      name="contact[{{ block.settings.title | handleize | default: 'checkbox' }}]"
                      value="{{ block.settings.title }}"
                      {% if block.settings.check %} required="true" {% endif %}
                      >
              {{ block.settings.title }}
              {% if block.settings.check %}<span aria-hidden="true">*</span>{% endif %}
            </label>
          </div>


        {%- comment -%} ラジオボタン {%- endcomment -%}
        {% when 'radio' %}
          <div class="field no-border colum" {{ block.shopify_attributes }}>
            {% if block.settings.title != blank %}
              <legend class="custom-form__label">
                {{ block.settings.title }}
                {% if block.settings.check %}<span aria-hidden="true">*</span>{% endif %}
              </legend>
            {% endif %}
            {% if block.settings.label-one != blank %}
              <div>
                <input type="radio" id="Form-{{ section.id }}-{{ forloop.index0 }}-1" class="contactFormRadio" name="contact[{{ block.settings.title | handleize | default: 'radio' }}]" value="{{ block.settings.label-one }}"
                {% if block.settings.check %} required="true" {% endif %}>
                <label for="Form-{{ section.id }}-{{ forloop.index0 }}-1">{{ block.settings.label-one }}</label>
              </div>
            {% endif %}
            {% if block.settings.label-two != blank %}
              <div>
                <input type="radio" id="Form-{{ section.id }}-{{ forloop.index0 }}-2" class="contactFormRadio" name="contact[{{ block.settings.title | handleize | default: 'radio' }}]" value="{{ block.settings.label-two }}">
                <label for="Form-{{ section.id }}-{{ forloop.index0 }}-2">{{ block.settings.label-two }}</label>
              </div>
            {% endif %}
            {% if block.settings.label-three != blank %}
              <div>
                <input type="radio" id="Form-{{ section.id }}-{{ forloop.index0 }}-3" class="contactFormRadio" name="contact[{{ block.settings.title | handleize | default: 'radio' }}]" value="{{ block.settings.label-three }}">
                <label for="Form-{{ section.id }}-{{ forloop.index0 }}-3">{{ block.settings.label-three }}</label>
              </div>
            {% endif %}
            {% if block.settings.label-four != blank %}
              <div>
                <input type="radio" id="Form-{{ section.id }}-{{ forloop.index0 }}-4" class="contactFormRadio" name="contact[{{ block.settings.title | handleize | default: 'radio' }}]" value="{{ block.settings.label-four }}">
                <label for="Form-{{ section.id }}-{{ forloop.index0 }}-4">{{ block.settings.label-four }}</label>
              </div>
            {% endif %}
            {% if block.settings.label-five != blank %}
              <div>
                <input type="radio" id="Form-{{ section.id }}-{{ forloop.index0 }}-5" class="contactFormRadio" name="contact[{{ block.settings.title | handleize | default: 'radio' }}]" value="{{ block.settings.label-five }}">
                <label for="Form-{{ section.id }}-{{ forloop.index0 }}-5">{{ block.settings.label-five }}</label>
              </div>
            {% endif %}
            {% if block.settings.label-six != blank %}
              <div>
                <input type="radio" id="Form-{{ section.id }}-{{ forloop.index0 }}-6" class="contactFormRadio" name="contact[{{ block.settings.title | handleize | default: 'radio' }}]" value="{{ block.settings.label-six }}">
                <label for="Form-{{ section.id }}-{{ forloop.index0 }}-6">{{ block.settings.label-six }}</label>
              </div>
            {% endif %}
          </div>


        {%- comment -%} セレクトボックス {%- endcomment -%}
        {% when 'select' %}
          <div class="field no-border colum" {{ block.shopify_attributes }}>
            {% if block.settings.title != blank %}
              <label for="Form-{{ section.id }}-{{ forloop.index0 }}"
                      class="custom-form__label label--block">
                {{ block.settings.title }}
                {% if block.settings.check %}<span aria-hidden="true">*</span>{% endif %}
              </label>
            {% endif %}

            <select id="Form-{{ section.id }}-{{ forloop.index0 }}"
                    class="contactFormSelect"
                    name="contact[{{ block.settings.title | handleize | default: 'select' }}]"
                    {% if block.settings.check %} required="true" {% endif %}
                    >
              <option value="">未選択</option>
              {% if block.settings.label-one != blank %}
                <option value="{{ block.settings.label-one }}">{{ block.settings.label-one }}</option>
              {% endif %}
              {% if block.settings.label-two != blank %}
                <option value="{{ block.settings.label-two }}">{{ block.settings.label-two }}</option>
              {% endif %}
              {% if block.settings.label-three != blank %}
                <option value="{{ block.settings.label-three }}">{{ block.settings.label-three }}</option>
              {% endif %}
              {% if block.settings.label-four != blank %}
                <option value="{{ block.settings.label-four }}">{{ block.settings.label-four }}</option>
              {% endif %}
              {% if block.settings.label-five != blank %}
                <option value="{{ block.settings.label-five }}">{{ block.settings.label-five }}</option>
              {% endif %}
              {% if block.settings.label-six != blank %}
                <option value="{{ block.settings.label-six }}">{{ block.settings.label-six }}</option>
              {% endif %}
            </select>
          </div>

      {% endcase %}

    {% endfor %}

      {%- comment -%} フォームボタン {%- endcomment -%}
      <div class="contact__button">
        <button type="submit" class="button">
          {{ 'templates.contact.form.send' | t }}
        </button>
      </div>
    {%- endform -%}
  </div>
</div>

{% schema %}
{
  "name": "t:sections.contact-form.name",
  "tag": "section",
  "class": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "default": "Contact form",
      "label": "Heading"
    },
    {
      "type": "select",
      "id": "color_scheme",
      "options": [
        {
          "value": "accent-1",
          "label": "t:sections.all.colors.accent_1.label"
        },
        {
          "value": "accent-2",
          "label": "t:sections.all.colors.accent_2.label"
        },
        {
          "value": "background-1",
          "label": "t:sections.all.colors.background_1.label"
        },
        {
          "value": "background-2",
          "label": "t:sections.all.colors.background_2.label"
        },
        {
          "value": "inverse",
          "label": "t:sections.all.colors.inverse.label"
        }
      ],
      "default": "background-1",
      "label": "t:sections.all.colors.label"
    },
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 36
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 36
    }
  ],
  "blocks": [
      {
        "type": "name",
        "name": "お名前",
        "limit": 1,
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "お名前",
            "default": "フルネーム",
            "info": "例：フルネーム"
          }
        ]
      },
      {
        "type": "email",
        "name": "Eメール",
        "limit": 1,
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "ラベル",
            "default": "Eメール"
          }
        ]
      },
      {
        "type": "body",
        "name": "本文",
        "limit": 1,
        "settings": [
          {
            "type": "textarea",
            "id": "title",
            "label": "ラベル",
            "default": "本文"
          }
        ]
      },
      {
        "type": "text",
        "name": "1行テキスト",
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "ラベル",
            "default": "フルネーム",
            "info": "例：フルネーム"
          },
          {
            "type": "checkbox",
            "id": "check",
            "label": "必須にする",
            "default": false,
            "info": "チェックを入れると必須項目にできます。"
          }
        ]
      },
      {
        "type": "textarea",
        "name": "テキストエリア",
        "settings": [
          {
            "type": "textarea",
            "id": "title",
            "label": "ラベル",
            "default": "メッセージを入力してください",
            "info": "例：メッセージを入力してください"
          },
          {
            "type": "checkbox",
            "id": "check",
            "label": "必須にする",
            "default": false,
            "info": "チェックを入れると必須項目にできます。"
          }
        ]
      },
      {
        "type": "telephone",
        "name": "電話",
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "ラベル",
            "default": "電話番号",
            "info": "例：電話番号"
          },
          {
            "type": "checkbox",
            "id": "check",
            "label": "必須にする",
            "default": false,
            "info": "チェックを入れると必須項目にできます。"
          }
        ]
      },
      {
        "type": "formHeading",
        "name": "見出し",
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "見出し",
            "default": "見出し",
            "info": "より長いフォームを分割するために使用します"
          }
        ]
      },
      {
        "type": "spacer",
        "name": "装飾",
        "settings": [
          {
            "type": "checkbox",
            "id": "line",
            "label": "水平ライン",
            "default": false
          }
        ]
      },
      {
        "type": "checkbox",
        "name": "チェックボックス",
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "ラベル",
            "default": "チェックボックスオプション",
            "info": "例：ニュースレターを購読します"
          },
          {
            "type": "checkbox",
            "id": "check",
            "label": "必須にする",
            "default": false,
            "info": "チェックを入れると必須項目にできます。"
          }
        ]
      },
      {
        "type": "select",
        "name": "セレクト ドロップダウン",
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "Heading",
            "default": "選択肢一つを選択してください",
            "info": "例：配達場所を選択します"
          },
          {
            "type": "checkbox",
            "id": "check",
            "label": "必須にする",
            "default": false,
            "info": "チェックを入れると必須項目にできます。"
          },
          {
            "type": "header",
            "content": "最大6つのオプションを含めます",
            "info":  "未入力のテキストのオプションは表示されません。"
          },
          {
            "type": "text",
            "id": "label-one",
            "default": "オプション1",
            "label": "オプション1"
          },
          {
            "type": "text",
            "id": "label-two",
            "default": "オプション２",
            "label": "オプション２"
          },
          {
            "type": "text",
            "id": "label-three",
            "default": "オプション３",
            "label": "オプション３"
          },
          {
            "type": "text",
            "id": "label-four",
            "default": "オプション４",
            "label": "オプション４"
          },
          {
            "type": "text",
            "id": "label-five",
            "default": "オプション５",
            "label": "オプション５"
          },
          {
            "type": "text",
            "id": "label-six",
            "default": "オプション６",
            "label": "オプション６"
          }
        ]
      },
      {
        "type": "radio",
        "name": "ラジオボタン",
        "settings": [
          {
            "type": "text",
            "id": "title",
            "label": "タイトル",
            "default": "オプションを選択します"
          },
          {
            "type": "checkbox",
            "id": "check",
            "label": "必須にする",
            "default": false,
            "info": "チェックを入れると必須項目にできます。"
          },
          {
            "type": "header",
            "content": "最大6つのオプションを含めます",
            "info":  "除外するにはオプションテキストを未入力にします"
          },
          {
            "type": "text",
            "id": "label-one",
            "default": "オプション１",
            "label": "オプション１"
          },
          {
            "type": "text",
            "id": "label-two",
            "default": "オプション２",
            "label": "オプション２"
          },
          {
            "type": "text",
            "id": "label-three",
            "label": "オプション3"
          },
          {
            "type": "text",
            "id": "label-four",
            "label": "オプション4"
          },
          {
            "type": "text",
            "id": "label-five",
            "label": "オプション5"
          },
          {
            "type": "text",
            "id": "label-six",
            "label": "オプション6"
          }
        ]
      }
    ],
  "presets": [
    {
      "name": "t:sections.contact-form.presets.name"
    }
  ]
}
{% endschema %}

```

## 設定内容とブロックについて
### 設定内容
本文やEmailの箇所には、limitを入れることで、一度しか項目を追加できなくし、デフォルトで必須項目にしています。
limitを外すことで、Emailの複数取得も可能になります。
　
### block.typeについて
Block機能自体、複数の項目を追加できるので大変便利なのですが、blockのタイプを判定して組み込みをすることで、よりカスタマイズのバリエーションを増やすことが可能です。（ブロックの使い方に慣れるのも大変なのですが…）
通常ブロックは、１セクションファイルに１ブロックなのですが、擬似的に２つや３つのように表示することも可能かと思います。
説明すると、
```
<section>
  <div>
    <p>エリアA</p>
    {% for block in section.blocks %}
      {% case block.type %}

      {% when "type1" %}
        タイプ１

      {% when "type2" %}
        タイプ２

      {% when "type3" %}
        タイプ３

      {% endcase %}
    {% endfor %}
  </div>
</section>

```
を
```
<section>
    <div>
      <p>エリアA</p>
      {% for block in section.blocks %}
        {% if block.type == "type1" %}
          タイプ１
        {% endif %}
      {% endfor %}
    </div>

    <div>
      <p>エリアB</p>
      {% for block in section.blocks %}
        {% if block.type == "type2" %}
          タイプ２
        {% endif %}
      {% endfor %}
    </div>

    <div>
      <p>エリアC</p>
      {% for block in section.blocks %}
        {% if block.type == "type3" %}
          タイプ３
        {% endif %}
      {% endfor %}
    </div>
  </div>
</section>
```
のようにすることで、セクションファイルが１つしか組み込めないような箇所でも、複数組み込んだように追加することも可能です。（検証済み）
ただ、テーマのカスタマイズのブロックを追加する箇所は、管理が大変そうですが…。
　
## まとめ
今回はお問合せを例にしましたが、商品詳細で表示項目をカスタマイズなどにも応用することができます。
ブロックタイプを利用し、テーマカスタマイズの幅を広げることで、マーチャントの希望やショップを利用される方が使いやすいテーマ開発をやっていきましょう！

あと、
ARCHETYPでは、Shopifyのテーマ開発やアプリ開発をやりたいエンジニアを募集しております！
気になった方はお気軽にエントリーをお待ちしております！(業務委託もOK）
https://www.archetyp.jp/recruit/
noteでブログもやってるので良かったらチェックしてください！
https://note.com/archetyp