我正在使用Svelte.js创建多步骤表单,但是在使用唯一道具呈现每个表单页面时遇到了问题。
这是一个简单的演示,向您展示我的意思:
// App.svelte
<script>
import FormPage from "./FormPage.svelte";
let formNode;
let pageSelected = 0;
let formPages = [
{
name: "email",
label: "Email"
},
{
name: "password",
label: "Password"
}
];
const handleIncPage = () => {
if(pageSelected + 1 < formPages.length)
pageSelected = pageSelected + 1;
}
const handleDecPage = () => {
if(pageSelected -1 > -1)
pageSelected = pageSelected - 1;
}
</script>
<form bind:this={formNode}>
<FormPage pageData={formPages[pageSelected]} />
</form>
<button on:click={handleDecPage}>Back</button>
<button on:click={handleIncPage}>Next</button>
<p>
Page Selected: {pageSelected}
</p>
这是FormPage
组件:
// FormPage.svelte
<script>
export let pageData;
const {name, label} = pageData;
</script>
<div id={`form-page-${name}`}>
<label>Label: {label}</label>
<input type="text" name={name} id={`input-${name}`} />
</div>
<pre>{JSON.stringify(pageData, null, 2)}</pre>
当我运行应用程序并使用inc / dec pageSelected
时,pageData
属性成功更改-如pre
元素所示。但是,label
和input
元素与第一页上的元素完全相同。包装的id
元素的div
和id
元素的input
也未更改。
当我键入input
并更改页面时,文本保持不变。
我的目标是:在FormPage
更改时重新呈现pageSelected
组件,并根据这些新道具使input
和label
更改其值。也应该是这样的情况,当我更改页面时,已经键入input
的文本应该更新并且为空(因为没有为输入提供初始值)。
在React.js中完成了多步骤表单后,我将使用唯一的key
属性来确保每次FormPage
状态更改时,我的pageSelected
都会重新呈现。但是我不确定如何在Svelte.js中做类似的事情。
有什么建议吗?
更新:
在StackOverflow上阅读了此question之后,我发现了如何更改input
和label
元素以及id
属性。这是我更新后的FormPage
组件:
// FormPage.svelte
<script>
export let pageData;
$: name = pageData.name;
$: label = pageData.label;
</script>
<div id={`form-page-${name}`}>
<label>Label: {label}</label>
<input type="text" name={name} id={`input-${name}`} />
</div>
<pre>{JSON.stringify(pageData, null, 2)}</pre>
但是,input
中的文本仍然没有改变。
有没有办法也更新输入的值?每次道具更改时,创建一个全新的元素对我的用例来说都是理想的选择,但看来Svelte只会更新在同一基础DOM节点上更改的少数属性。
在这种情况下可以告诉Svelte重新创建元素吗?
更新2
因此,我想出了一种方法来更改输入的值。这是我更新后的FormPage
组件:
// FormPage.svelte
<script>
export let pageData;
import {onMount, afterUpdate, onDestroy} from "svelte";
let inputNode;
$: name = pageData.name;
$: label = pageData.label;
$: value = pageData.initialValue || "";
onMount(() => {
console.log("Mounted");
});
afterUpdate(() => {
console.log("Updated");
inputNode.value = value
});
onDestroy(() => {
console.log("Destroyed");
});
</script>
<div id={`form-page-${name}`}>
<label>Label: {label}</label>
<input type="text" name={name} id={`input-${name}`} bind:this={inputNode} />
</div>
<pre>{JSON.stringify(pageData, null, 2)}</pre>
这解决了立即更新输入值的问题。
我添加了onMount
,afterUpdate
和onDestroy
函数,以查看组件随时间的变化。
首先,调用onDestroy
函数,然后调用onMount
。这些都只发射一次。但是,afterUpdate
会在每次道具更改时触发。这证实了我对没有重新创建组件的怀疑。
答案 0 :(得分:0)
有一种更简单的方法可以做到这一点。 pageData
中的FormPage
更新,问题是当您使用const {name, label} = pageData;
时,您基本上是在{中分配name
和label
参数的值{1}}分别为两个常量pageData
和name
。这两个变量不再绑定到父组件。为了解决这个问题,请像下面这样直接在label
中使用pageData
。
FormPage.svelte
FormPage
注意:如果您不希望编译器在没有传递
<script> export let pageData; </script> <div id={`form-page-${pageData.name}`}> <label>Label: {pageData.label}</label> <input type="text" name={pageData.name} bind:value={pageData.value} id={`input-${pageData.name}`} /> </div>
的情况下给您带来undefined
错误,则可以将pageData
初始化为空对象{{1} }像pageData
一样。
对于{}
问题,最简单的做法是将export let pageData = {};
添加到input
,然后将value
formPages
添加到{{ 1}},如上方的bind
所示。
新的value
数据
pageData.value
这是REPL上的一个有效示例。
答案 1 :(得分:0)
{#key}..{/key}
解决了问题。
就我而言,Sizing
组件用于更新大小(字体大小或行高等),就像您的 FormPage.svelte
<script>
import Sizing from '..../Sizing.svelte'
const pages = {
'font-size': Sizing,
'line-height': Sizing,
...
}
const props = {
'font-size': { min: 8, max: 20, step: 1},
'line-height': {min: 1, max: 3, step: 0.1}
}
let currentProp // 'font-size', 'line-height' etc ..
</script>
<div>
{#key currentProp}
<svelte:component this="{pages[currentProp]}" {...props}/>
{/key}
</div>
每当 currentProp
更改时,Sizing 组件也会更新。
{...props}
映射到导出的变量
// Sizing.svelte
<script>
export let min
export let max
export let step
</script>