如何在Shopify中用Alpine.js实现点击按钮单独渲染对应集合
问题:点击菜单按钮时所有商品集合同步显示/隐藏,无法单独控制
我在Shopify后台通过遍历区块生成菜单按钮,想用Alpine.js控制对应商品集合的显示/隐藏,但现在点击任意按钮,所有集合都会同时切换显示状态。问题出在布尔值状态不具备唯一性,尝试用Alpine的getter重构后还是没解决——循环里的集合名称能正确渲染,但没法和状态关联实现单独控制。
原始代码
<div x-data="{ selected: false }" class="tw-container tw-mx-auto tw-pt-20" x-cloak> <div class="tw-flex lg:tw-justify-center tw-space-x-4 tw-overflow-x-auto"> {% for block in section.blocks %} <button type="button" @click="selected = !selected" class="tw-cursor-pointer" > // 渲染按钮图片与标题 </button> {% endfor %} </div> {% for block in section.blocks %} <div class="tw-container"> <div x-show="selected" > <div class="tw-mt-12 tw-grid tw-grid-cols-1 md:tw-grid-cols-3 tw-gap-6"> {% assign coll_name = block.settings['menu-collection-name'] | handleize %} {%- if coll_name != '' -%} // 渲染对应集合的商品,现在会随selected同步切换 {% for product in collections[coll_name].products %} {%- render 'restaurant-menu-item', item: product -%} {% endfor %} {%- endif -%} </div> </div> </div> {% endfor %} </div> {% schema %} { "name": "Restaurant Menu", "settings": [ { "type": "image_picker", "id": "image", "label": "Upload Menu Image" }, { "type": "text", "id": "title", "label": "Title", "default": "Menu" } ], "blocks": [ { "type": "menu-link", "name": " Menu-link", "settings": [ { "type": "image_picker", "id": "link-image", "label": "Link Image" }, { "type": "text", "id": "link-title", "label": "Link Title", "default": "Menu Title" }, { "type": "text", "id": "menu-collection-name", "label": "Menu Collection Name (required)", "info": "It should match name of collection used for menu items. Use name when collection is first set" } ] } ] } {% endschema %}
重构后的尝试代码(仍未解决)
<div x-data=" {open: false, get isOpen() { return this.open }, toggle() { this.open = ! this.open }, } " class="tw-container tw-mx-auto tw-pt-20" x-cloak > <div class="tw-flex lg:tw-justify-center tw-space-x-4 tw-overflow-x-auto"> {% for block in section.blocks %} {%- comment -%} 这里能正确输出对应集合名称,但赋值或捕获后只会渲染一次,无法动态变化 {%- endcomment -%} {{ block.settings['menu-collection-name'] }} <button type="button" @click="toggle()" class="tw-cursor-pointer" > <div class="tw-min-w-[300px] sm:tw-max-w-sm tw-rounded-lg tw-p-6 tw-mb-6 tw-overflow-hidden tw-shadow"> 渲染可点击的图片按钮 </div> </button> {% endfor %} </div> <div class="tw-container"> {% for block in section.blocks %} {%- comment -%} 这里能正确输出对应集合名称,但赋值或捕获后只会渲染一次,无法动态变化 {%- endcomment -%} {{ block.settings['menu-collection-name'] }} <div x-show="isOpen" > <div class="tw-mt-12 tw-grid tw-grid-cols-1 md:tw-grid-cols-3 tw-gap-6"> {% assign coll_name = block.settings['menu-collection-name'] | handleize %} {%- if coll_name != '' -%} {% for product in collections[coll_name].products %} {%- render 'restaurant-menu-item', item: product -%} {% endfor %} {%- else -%} <p>No menu here check the name of collection</p> {%- endif -%} </div> </div> {% endfor %} </div> </div> {% schema %} { "name": "Restaurant Menu", "settings": [ { "type": "image_picker", "id": "image", "label": "Upload Menu Image" }, { "type": "text", "id": "title", "label": "Title", "default": "Menu" } ], "blocks": [ { "type": "menu-link", "name": " Menu-link", "settings": [ { "type": "image_picker", "id": "link-image", "label": "Link Image" }, { "type": "text", "id": "title", "label": "Link Title", "default": "Menu Title" }, { "type": "text", "id": "menu-collection-name", "label": "Menu Collection Name (required)", "info": "It should match name of collection used for menu items. Use name when collection is first set" } ] } ] } {% endschema %}
解决方案:用唯一标识替代全局布尔值
核心思路是把Alpine的全局布尔状态改成存储当前选中区块的唯一ID(或集合Handle),这样每个按钮和对应的集合都能通过这个唯一标识关联:
- Alpine状态定义为
selectedBlockId: null,用来存储当前选中的区块ID - 每个按钮点击时,把自身对应的
block.id赋值给selectedBlockId - 每个集合的
x-show判断当前selectedBlockId是否等于自身区块的block.id
修改后的完整代码
<div x-data="{ selectedBlockId: null }" class="tw-container tw-mx-auto tw-pt-20" x-cloak> <div class="tw-flex lg:tw-justify-center tw-space-x-4 tw-overflow-x-auto"> {% for block in section.blocks %} <button type="button" @click="selectedBlockId = selectedBlockId === '{{ block.id }}' ? null : '{{ block.id }}'" class="tw-cursor-pointer" > <!-- 渲染按钮图片与标题 --> {% if block.settings['link-image'] %} <img src="{{ block.settings['link-image'] | img_url: '300x300' }}" alt="{{ block.settings['link-title'] }}"> {% endif %} <span>{{ block.settings['link-title'] }}</span> </button> {% endfor %} </div> {% for block in section.blocks %} <div class="tw-container"> <div x-show="selectedBlockId === '{{ block.id }}'"> <div class="tw-mt-12 tw-grid tw-grid-cols-1 md:tw-grid-cols-3 tw-gap-6"> {% assign coll_name = block.settings['menu-collection-name'] | handleize %} {%- if coll_name != '' -%} {% for product in collections[coll_name].products %} {%- render 'restaurant-menu-item', item: product -%} {% endfor %} {%- else -%} <p>暂无菜单,请检查集合名称是否正确</p> {%- endif -%} </div> </div> </div> {% endfor %} </div> {% schema %} { "name": "Restaurant Menu", "settings": [ { "type": "image_picker", "id": "image", "label": "上传菜单主图" }, { "type": "text", "id": "title", "label": "标题", "default": "菜单" } ], "blocks": [ { "type": "menu-link", "name": "菜单链接", "settings": [ { "type": "image_picker", "id": "link-image", "label": "按钮图片" }, { "type": "text", "id": "link-title", "label": "按钮标题", "default": "菜单分类" }, { "type": "text", "id": "menu-collection-name", "label": "菜单集合名称(必填)", "info": "需与商品集合的名称完全匹配,使用集合创建时的原始名称" } ] } ] } {% endschema %}
关键修改点说明
x-data改为{ selectedBlockId: null }:用null表示未选中任何区块- 按钮点击事件:
selectedBlockId = selectedBlockId === '{{ block.id }}' ? null : '{{ block.id }}'——点击已选中的按钮会取消选中,点击未选中的会切换到该区块 - 集合显示判断:
x-show="selectedBlockId === '{{ block.id }}'"——只有当前选中的区块对应的集合才会显示
这样就实现了点击单个按钮,只显示对应商品集合的效果,不会出现所有集合同步切换的问题。
内容的提问来源于stack exchange,提问作者Mike Haslam
相关产品推荐
相关产品推荐

