как связать изящные значения динамических компонентов

Скажем, у меня есть это основное приложение:

import Field from '../components/Field.svelte';
var fields = [
    {
        id: 'Check',
        type: 'CheckBox',
        value: false,
    },
    {
        id: 'Text'
    }
];
var components = {};
$: console.log(components);
</script>
<style>
</style>
<form>
{#each fields as item}
    <Field {...item} bind:bind={components[item.bind]} bind:this={components[item.id]}></Field>
{/each}
</form>

и у меня есть два компонента, CheckBox и TextArea, оба просто реализуют ввод HTML, а компонент поля реализован следующим образом:

import CheckBox from './CheckBox.svelte';
import TextArea from './TextArea.svelte';

export let attributes = {};
export let type = 'TextArea';
export let value = '';
export let id;
export let bind;

const fieldComponents = {
    'CheckBox': CheckBox,
    'TextArea': TextArea
};
</script>
<svelte:component {bind} {id} value={value} this={fieldComponents[type]} {attributes}></svelte:component>

Таким образом я создаю динамическую форму с флажком и текстовым полем.

Я хочу, чтобы атрибут "bind" был доступен изнутри компонента и чтобы иметь возможность связывать другой компонент. Таким образом я смогу достичь чего-то вроде этого:

<input type="checkbox" bind:checked={bind.value}>

это означает, что если текстовое поле будет иметь текст, флажок будет установлен, если он пуст, флажок будет снят.

после рендеринга всех компонентов я могу получить к ним доступ, используя объект components, потому что я привязываю их так bind:this={components[item.id]}

но до их рендеринга я не могу получить к ним доступ, есть ли способ сделать так, чтобы один компонент мог динамически связываться с другим? Я продемонстрировал использование только двух компонентов, с таким же успехом это может быть большой набор компонентов.

Я хочу определить привязку с помощью свойства bind внутри массива fields, которое соответствует id другого поля.


person Hagai Wild    schedule 19.01.2020    source источник


Ответы (1)


Я предлагаю поработать с магазином Svelte и сохранить в нем объект конфигурации формы. Это позволит любому из ваших компонентов Svelte получить доступ к состоянию формы.

Рабочий пример можно протестировать и разветвить на странице https://svelte.dev/repl/3f161dd253624d4ea7a3b8b9e5763e96?version=3.21.0

Разбивка кода для примера приложения ниже.

Где App.svelte:

<script>
/*
@abstract This app is used to demonstrate one way to track form state with Svelte.
We use the 'store' to save an object that will contain our form field configurations
and field values. A JSON string formatted configuration is used as opposed to a purely javascipt object so that we can for instance pull in our form configuration from a back-end database to dynmaically build our form (in this example we are simply hard-coding the JSON into the app, but for production you might want to pull from an server-side API).
*/
import Field from './Field.svelte'; // used to build our form fields
import Box from './Box.svelte'; // just for show
import { storeFE } from './store.js';   // store our form state
let objForm;    // @testing - used to listen for changes in our form state

// @testing - keep up to date on the form object
const unsubscribe = storeFE.subscribe(value => {
        objForm = value;
});

// opting for JSON string config (which is what we would want if we are pulling this config from say a server data API)
// the 'fIndex' value is used within our form components know which form element object to work with within our main 'storeFE' object store. the 'fType' value tells the Field.svelte component which form element to build
let objFormConfig = JSON.parse(`{
    "formElements": [
        {
                "fIndex":0,
                "fId":"cc2",
                "fType": "CheckBox",
                "fValue": "true",
                "fDisable":"ct1.fValue==''"
        },
        {
                "fIndex":1,
                "fId":"ct1",
                "fType": "TextArea",
                "fValue": "textee area",
                "fChangeEvent":"cc2 disable",
                "fDisable":"cc2 checked is false"
        }
    ]
}`);
// @testing: let us know when the form values have changed (the storeFE object has updated)
$: {
    console.log('objForm:');
    console.log(objForm);
}
$storeFE = objFormConfig;   // save the initial form configuration to the store
</script>
<form>
{#each objFormConfig.formElements as item}
    <Box>
    <Field objAttributes={item}></Field>
    </Box>
{/each}
</form>

Где Field.svelte:

<script>
import CheckBox from './CheckBox.svelte';
import TextArea from './TextArea.svelte';

export let objAttributes = {};

const fieldComponents = {
    'CheckBox': CheckBox,
    'TextArea': TextArea
};
</script>
<div>
        <svelte:component this={fieldComponents[objAttributes.fType]} {objAttributes} />
</div>

Где CheckBox.svelte:

<script>
/* Here we want to get the store index */
import { storeFE } from './store.js';
export let objAttributes = {};
const fIndex = objAttributes.fIndex;
const strDisable = objAttributes.fDisable;
function fDisable() {
    if (strDisable) {
        console.log('do some stuff like check: '+strDisable);
    }
}
console.log("checkbox here, showing you my field attributes:");
console.log(objAttributes);
</script>
<h2>
    My checkbox
</h2>
<input id={objAttributes.fId} type=checkbox bind:checked={$storeFE.formElements[fIndex].fValue} on:change={fDisable}>

Где TextArea.svelte:

<script>
import { storeFE } from './store.js';
export let objAttributes = {};
const fIndex = objAttributes.fIndex;


console.log("textarea here, showing you my field attributes:");
console.log(objAttributes);
</script>
<h2>
    My text
</h2>
<textarea bind:value={$storeFE.formElements[fIndex].fValue}></textarea>

Где store.js:

import { writable } from 'svelte/store';
export let storeFE = writable({});

Box.svelte не нужен, он нужен только для галочки (взят из руководств по Svelte):

<style>
    .box {
        width: 300px;
        border: 1px solid #aaa;
        border-radius: 2px;
        box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
        padding: 1em;
        margin: 0 0 1em 0;
    }
</style>

<div class="box">
    <slot></slot>
</div>

Другой пример этого кода с проверкой формы можно найти в этом приложении Svelte REPL: https://svelte.dev/repl/253ddd578806497b8b54c339490f8221?version=3.21.0

person w. Patrick Gale    schedule 30.04.2020