Je travaille sur la création d'un formulaire en plusieurs étapes à l'aide de Svelte.js mais j'ai rencontré un problème de rendu de chaque page de formulaire avec des accessoires uniques.
Voici une démo simple pour vous montrer ce que je veux dire:
// 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>
Et voici le composant 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>
Lorsque j'exécute l'application et inc/dec pageSelected
, la prop pageData
change avec succès - comme on peut le voir dans l'élément pre
. Cependant, les éléments label
et input
sont exactement les mêmes que sur la première page. Le id
de l'élément enveloppant div
et le id
de l'élément input
sont également inchangés.
Quand je tape dans le input
et change la page, le texte reste le même.
Mes objectifs sont les suivants: rendre à nouveau le composant FormPage
lorsque pageSelected
change et faire en sorte que input
et label
changent leurs valeurs en fonction de ces nouveaux accessoires. Il devrait également être le cas que lorsque je change de page, le texte déjà tapé dans le input
doit se mettre à jour et être vide (car aucune valeur initiale n'est donnée aux entrées).
Après avoir fait des formulaires en plusieurs étapes dans React.js, j'utiliserais un attribut key
unique pour m'assurer que mon FormPage
se redirige à chaque fois que l'état pageSelected
change. Mais je ne sais pas comment faire quelque chose de similaire dans Svelte.js.
Aucune suggestion?
MISE À JOUR:
Après avoir lu ceci question sur StackOverflow, j'ai découvert comment faire changer les éléments input
et label
ainsi que les attributs id
. Voici mon composant FormPage
mis à jour:
// 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>
Cependant, le texte à l'intérieur de input
ne change toujours pas.
Existe-t-il également un moyen de mettre à jour la valeur de l'entrée? Il serait idéal pour mon cas d'utilisation de créer un élément entièrement nouveau à chaque fois que les accessoires changent, mais il semble que Svelte ne met à jour que les quelques attributs qui ont changé sur le même nœud DOM sous-jacent.
Peut-on dire à Svelte de recréer des éléments dans cette circonstance?
MISE À JOUR 2
J'ai donc trouvé un moyen de changer la valeur de l'entrée. Voici mon composant FormPage
mis à jour:
// 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>
Cela résout le problème immédiat de la mise à jour de la valeur d'entrée.
J'ai ajouté les fonctions onMount
, afterUpdate
et onDestroy
pour voir comment le composant change au fil du temps.
Tout d'abord, la fonction onDestroy
est appelée, puis onMount
après cela. Ces deux ne tirent qu'une seule fois. Cependant, afterUpdate
se déclenche chaque fois que les accessoires changent. Cela confirme ma suspicion que le composant n'a pas été recréé.
Il existe un moyen plus simple de procéder. Les mises à jour pageData
dans votre FormPage
, le problème est lorsque vous utilisez const {name, label} = pageData;
, vous affectez essentiellement les valeurs des paramètres name
et label
dans pageData
à deux constantes, name
et label
respectivement . Ces deux variables ne sont plus liées au composant parent. Pour résoudre ce problème, utilisez pageData
directement dans FormPage
comme ci-dessous.
FormPage.svelte
<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>
Remarque: Si vous ne voulez pas que le compilateur vous donne une erreur
undefined
si aucunpageData
n'est passé, alors vous pouvez initialiserpageData
comme un objet vide{}
ainsiexport let pageData = {};
.
En ce qui concerne le problème input
, la chose la plus simple à faire serait d'ajouter value
à formPages
, puis bind
le value
à pageData.value
comme indiqué ci-dessus dans le FormPage
.
Nouvelles données formPages
let formPages = [{
name: "email",
label: "Email",
value: ""
}, {
name: "password",
label: "Password",
value: ""
}];
Voici un exemple de travail sur REPL .