Svelte.js - Как переназначить дочерние компоненты с новым реквизитом? - PullRequest
2 голосов
/ 19 сентября 2019

Я работаю над созданием многошаговой формы с использованием 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:

<code>// 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)}

Когда я запускаю приложение и 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 я узнал, как заставить изменяться элементы input и label, а также атрибуты id.Вот мой обновленный FormPage компонент:

<code>// 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)}

Однако текст внутри input по-прежнему не изменяется.

Есть ли способ такжеобновить значение ввода, а?В моем случае было бы идеально создавать абсолютно новый элемент при каждом изменении реквизита, но похоже, что Svelte обновляет только несколько атрибутов, которые изменились на одном и том же базовом узле DOM.

Может ли Svelte бытьвелели воссоздать элементы в этих обстоятельствах?

ОБНОВЛЕНИЕ 2

Итак, я нашел способ получить значение входного значения для изменения.Вот мой обновленный FormPage компонент:

<code>// 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)}

Это решает непосредственную проблему обновления входного значения.

Я добавил onMount,Функции afterUpdate и onDestroy позволяют увидеть, как компонент изменяется с течением времени.

Сначала вызывается функция onDestroy, а затем onMount.Оба они стреляют только один раз.Однако afterUpdate срабатывает каждый раз, когда меняется реквизит.Это подтверждает мое подозрение, что компонент не был воссоздан.

1 Ответ

0 голосов
/ 20 сентября 2019

Есть более простой способ сделать это.pageData обновляется в вашем FormPage, проблема в том, что когда вы используете const {name, label} = pageData;, вы в основном присваиваете значения параметров name и label в pageData двум константам, name иlabel соответственно.Эти две переменные больше не связаны с родительским компонентом.Чтобы это исправить, используйте pageData непосредственно в FormPage, как показано ниже.

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>

Примечание.если компилятор не выдаст ошибку undefined, если не передано pageData, вы можете инициализировать pageData как пустой объект {}, например, export let pageData = {};.

Что касается проблемы input, проще всего было бы добавить value к formPages, а затем bind к value к pageData.value, как показано выше в FormPage.

Новый formPages data

let formPages = [{
    name: "email",
    label: "Email",
    value: ""
}, {
    name: "password",
    label: "Password",
    value: ""
}];

Вот рабочий пример для REPL .

...