Open TylerRick opened 4 years ago
Well, I just discovered that there is a svelte-final-form project, so I will probably be abandoning svelte-forms-lib and switching to svelte-final-form... because final-form is awesome, and a more mature forms library, and what I had been wanting for svelte was a port of react-final-form anyway (I just didn't know one existed until today). :smile:
I wrote an implementation of FieldArray
for svelte-forms-lib. I don't want to spend much more time on it since I'm probably going to switch to svelte-final-form, but I could probably push up a PR if anyone is interested.
Here's an example of how it looks to use it:
<FieldArray name="users" let:fields>
{#each fields.entries() as [name, i]}
<div class="form-group">
<div>
<Field
name={`${name}.name`}
type="text"
let:field
>
<input
{...field.input}
on:input={field['on:input']}
on:blur={field.handleChange}
placeholder={`Name of person #${i + 1}`}
/>
{#if field.error}
<small class="error">{field.error}</small>
{/if}
</Field>
</div>
<div>
<Field
name={`${name}.email`}
type="email"
let:field
>
<input
{...field.input}
on:input={field['on:input']}
on:blur={field.handleChange}
placeholder="E-mail address"
/>
{#if field.error}
<small class="error">{field.error}</small>
{/if}
</Field>
</div>
<button type="button" on:click={fields.remove(i)}>-</button>
</div>
{/each}
<div class="button-group">
<button type="button" on:click={() => fields.push({ name: "", email: "" })}>+</button>
<button type="button" on:click={handleSubmit}>submit</button>
<button type="button" on:click={handleReset}>reset</button>
</div>
</FieldArray>
@TylerRick never heard of Final Form, thanks for linking to the Svelte implementation - Final Form's approach sounds excellent!
If you've got something ready to push as a PR that'd be great :)
@TylerRick I would be really interested in the PR for this if you still have the code available.
Example of nice API: react-final-form-arrays
Coming to this library from react-final-form, there are a lot of things I miss from it. I really liked how clean, consistent, and extensible it was. One of the extensions to it was react-final-form-arrays (also see this article).
This provides a really, really nice API for adding and removing new elements to/from an array of fields:
fields.remove(index)
fields.push({ firstName: '', lastName: '' }
Compared to current API
Compare that with the current recommended way to add/remove fields in an array, which is very verbose and boilerplatey:
Not only is it boilerplatey, it exposes and requires knowledge of the internals of the library. And end-users of this library should not have to do all that work. And it's very easy to get one of those functions wrong. In fact, even this official example is not quite correct, because it fails to also modify
$touched.users
!Bug in doc example
Here is a sandbox (based on https://svelte-forms-lib-sapper-docs.now.sh/array) showing how
$touched.users
should be modified too.If you comment those 2 statements out and don't modify
$touched.users
when you add/remove a user, it creates a bug where if you add a new user,$isValid
(initiallytrue
because it starts with 0 users) does not change fromtrue
tofalse
. As soon as you add an empty user to the array, it should be considered invalid becausename
is a required field.Related: a single store with form state instead of 3?
Ideally, I think it would be nice if we didn't have to try to keep 3+ objects ($form, $touched, $errors) in sync. Maybe there could be a centralized store of form data, keyed by field path, and under each field would be the state (value, touched, errors) for that field? I would highly recommend checking out how final-form manages state, since that is a very well-written library. (I haven't yet checked how Erik did it there but I would like to.)
Describe the solution you'd like
What I would like to see is a
<FieldArray>
component added to this library that works similarly to the one in react-final-form, and provides an API that is so enjoyable, simple, and easy to use that users literally can't mess up their add/remove functions like they can with the current API.fields.remove(index)
fields.push({ firstName: '', lastName: '' }