Svelte模态框组件仅首次点击可打开问题求助
问题描述
在Svelte中开发了商品展示模态框组件,首次点击商品时模态框正常弹出,但关闭后再次点击任意商品,模态框都无法再次显示。
组件代码(ProductModal.svelte):
<script> export let open = false; export let data = {}; export let modalID = undefined; const closeModal = () => { console.log("closing modal...") open = false; // reset all fields data = {}; } $: console.log(open) </script> {#if open} <div class="modal" id="product-modal-{modalID}" class:modal-open={open}> <div class="modal-box relative w-56"> <label for="product-modal-{modalID}" class="btn btn-sm btn-circle absolute right-2 top-2" on:click={closeModal}>✕</label> <div class="bg-white"> <div class="flex items-center justify-center"> <img class="object-scale-down h-40 w-40" src="https://media.ldlc.com/r1600/ld/products/00/05/82/02/LD0005820208_1.jpg" alt="" /> </div> <div class="py-2 px-2 bg-white"> <p class="text-md font-semibold block text-gray-600"> { data.name } </p> <div class="flex flex-row py-4"> <div class="pr-8 pt-2 flex justify-between"> <span class="font-semibold">-</span> <input type="text" class="focus:outline-none bg-gray-100 border h-6 w-8 rounded text-sm px-2 mx-2" value="1"> <span class="font-semibold">+</span> </div> <button class="btn btn-primary btn-sm">Add</button> </div> </div> </div> </div> </div> {/if}
页面调用代码:
<script> import { productSearch } from "../../lib/api/products.js"; import ProductModal from "../../lib/components/ProductModal.svelte"; let openModal = false; let productData = {} const openProductModal = (data) => { console.log(data); productData = data; openModal = !openModal; console.log(openModal) } </script> {#if searchResults} {#each searchResults as result (result.searchable.id)} <div class="w-64 rounded-md overflow-hidden shadow-lg hover:scale-105 transition duration-500 bg-white"> <div class="flex items-center justify-center"> <img class="object-scale-down h-40 w-40" src="https://media.ldlc.com/r1600/ld/products/00/05/82/02/LD0005820208_1.jpg" alt="" /> </div> <div class="py-4 px-4 bg-white"> <p class="text-md font-semibold block text-gray-600 line-clamp-3 h-20 max-h-20"> { result.searchable.name } </p> <div class="flex flex-row justify-between"> <p class="text-lg font-semibold text-black cursor-auto my-3">${ result.searchable.retail_price }</p> <a href="#" on:click={openProductModal(result.searchable)} class="btn btn-circle btn-outline"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 10.5V6a3.75 3.75 0 10-7.5 0v4.5m11.356-1.993l1.263 12c.07.665-.45 1.243-1.119 1.243H4.25a1.125 1.125 0 01-1.12-1.243l1.264-12A1.125 1.125 0 015.513 7.5h12.974c.576 0 1.059.435 1.119 1.007zM8.625 10.5a.375.375 0 11-.75 0 .375.375 0 01.75 0zm7.5 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" /> </svg> </a> </div> </div> </div> {/each} {/if} {#if openModal} <ProductModal open={openModal} data={productData} modalID={productData.id} /> {/if}
问题原因
- 子组件直接修改props导致状态不同步:Svelte中props是单向绑定的,子组件
ProductModal的closeModal函数里直接修改open = false,不会同步到父组件的openModal状态。关闭模态框后,父组件的openModal依然是true,后续点击商品执行openModal = !openModal会把状态改成false,无法触发模态框渲染。 - 点击事件绑定错误:页面中
on:click={openProductModal(result.searchable)}的写法会在组件初始化时立即执行openProductModal,而非点击时触发,导致首次加载就触发模态框,后续点击逻辑完全异常。
修复方案
1. 修改子组件(ProductModal.svelte)
子组件不直接修改props,而是通过自定义事件通知父组件更新状态:
<script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); export let open = false; export let data = {}; export let modalID = undefined; const closeModal = () => { console.log("closing modal...") // 触发自定义事件让父组件处理状态变更 dispatch('close'); data = {}; } $: console.log(open) </script> {#if open} <div class="modal" id="product-modal-{modalID}" class:modal-open={open}> <div class="modal-box relative w-56"> <label for="product-modal-{modalID}" class="btn btn-sm btn-circle absolute right-2 top-2" on:click={closeModal}>✕</label> <div class="bg-white"> <div class="flex items-center justify-center"> <img class="object-scale-down h-40 w-40" src="https://media.ldlc.com/r1600/ld/products/00/05/82/02/LD0005820208_1.jpg" alt="" /> </div> <div class="py-2 px-2 bg-white"> <p class="text-md font-semibold block text-gray-600"> { data.name } </p> <div class="flex flex-row py-4"> <div class="pr-8 pt-2 flex justify-between"> <span class="font-semibold">-</span> <input type="text" class="focus:outline-none bg-gray-100 border h-6 w-8 rounded text-sm px-2 mx-2" value="1"> <span class="font-semibold">+</span> </div> <button class="btn btn-primary btn-sm">Add</button> </div> </div> </div> </div> </div> {/if}
2. 修改父组件调用逻辑
- 修正点击事件绑定,传递回调函数而非立即执行;
- 监听子组件的
close事件更新状态; - 打开模态框时直接设置
openModal = true,避免依赖状态取反:
<script> import { productSearch } from "../../lib/api/products.js"; import ProductModal from "../../lib/components/ProductModal.svelte"; let openModal = false; let productData = {} const openProductModal = (data) => { console.log(data); productData = data; // 直接设置为true,确保每次点击都打开模态框 openModal = true; console.log(openModal) } const closeProductModal = () => { openModal = false; } </script> {#if searchResults} {#each searchResults as result (result.searchable.id)} <div class="w-64 rounded-md overflow-hidden shadow-lg hover:scale-105 transition duration-500 bg-white"> <div class="flex items-center justify-center"> <img class="object-scale-down h-40 w-40" src="https://media.ldlc.com/r1600/ld/products/00/05/82/02/LD0005820208_1.jpg" alt="" /> </div> <div class="py-4 px-4 bg-white"> <p class="text-md font-semibold block text-gray-600 line-clamp-3 h-20 max-h-20"> { result.searchable.name } </p> <div class="flex flex-row justify-between"> <p class="text-lg font-semibold text-black cursor-auto my-3">${ result.searchable.retail_price }</p> <!-- 修正点击事件,传递回调函数 --> <a href="#" on:click={() => openProductModal(result.searchable)} class="btn btn-circle btn-outline"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 10.5V6a3.75 3.75 0 10-7.5 0v4.5m11.356-1.993l1.263 12c.07.665-.45 1.243-1.119 1.243H4.25a1.125 1.125 0 01-1.12-1.243l1.264-12A1.125 1.125 0 015.513 7.5h12.974c.576 0 1.059.435 1.119 1.007zM8.625 10.5a.375.375 0 11-.75 0 .375.375 0 01.75 0zm7.5 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" /> </svg> </a> </div> </div> </div> {/each} {/if} <!-- 监听子组件的close事件,移除父组件的if判断,让子组件自己控制显示 --> <ProductModal open={openModal} data={productData} modalID={productData.id} on:close={closeProductModal} />
内容的提问来源于stack exchange,提问作者MrCujo
相关产品推荐
相关产品推荐

