web-dev-qa-db-fra.com

Svelte.js - Comment rendre des composants enfants avec de nouveaux accessoires?

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éé.

6
always_testing

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 aucun pageData n'est passé, alors vous pouvez initialiser pageData comme un objet vide {} ainsi export 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 .

1
nash11