如何绑定苗条的动态组件值

时间:2020-01-19 14:00:24

标签: svelte svelte-component

假设我有这个主应用程序:

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>

,我有两个组件CheckBoxTextArea,它们都只实现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>

这样,我将创建一个具有复选框和文本区域的动态表单。

我想要的是“绑定”属性,该属性可从组件内部访问,并能够绑定另一个组件,这样我就可以实现以下目的:

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

这意味着如果文本区域将包含文本,则该复选框将被选中;如果为空,则该复选框将未被选中。

所有组件渲染后,我可以使用components对象访问它们,因为我像这样bind:this={components[item.id]}来绑定它们

但是在渲染它们之前,我无法访问它们,是否有一种方法可以使一个组件动态绑定到另一个组件? 我演示了仅使用2个组件,也可能是很多组件。

我要确定绑定的方法是使用bind数组内的fields属性,该属性与另一个字段的id匹配。

1 个答案:

答案 0 :(得分:3)

我的建议是与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