pablo-abc / felte

An extensible form library for Svelte, Solid and React
https://felte.dev
MIT License
1.01k stars 43 forks source link

Provide `initialValues` as writable store #78

Closed taffit closed 2 years ago

taffit commented 2 years ago

For some form handling, we need to read AND set the initial values as well. Examples:

In order to be able to do this, providing the initialValues as a store similar to $data and $touched would be a tremendous improvement for us.

pablo-abc commented 2 years ago

From how initialValues is being used internally, making it a store would be a bit impractical. From your requirements, do you only need to reset the touched store to false when the Reset button is clicked? If that's so, would keeping track of your new initialValues outside of Felte work?

Currently, a way I see to reset your form to new initialValues could look something like this:

// This can mutate due to an external action
let initialValues = { /* Your initial values */};

const { reset, setFields } = createForm({
  initialValues,
  /* ... */
});

// This could act as your `reset` function
function resetForm() {
  // We reset the whole form
  reset();
  // We set the form's values to its new `initialValues`
  setFields(initialValues);
}

Regarding your last point, the previous call to setFields should be able to set the re-calculated date.


Is there something I missed/misunderstood from your issue? Felte's API has grown quite big so I'm hesitant to increasing said API even more if it's not absolutely necessary.

taffit commented 2 years ago

Thank you @pablo-abc for commenting. I will try what your solution and check if it fits our needs and will report back.

To clarify your questions: Setting just the $touched-state is not enough. When resetting or comparing it should be able to refer to the latest initialValues. These are initially set during the creation of the form, but when these need to be updated, then the new values should be used. E. g. a form is shown to the user with initial values. The user changes some values and submits the form. The data is sent to the backend and saved. The user changes some fields and hits the reset button. Now the values/$data must be set back to the values when the form was submitted, not the ones used during the creation of the form.

Handling the initialValues outside of Felte is an option, of course (I'm doing this currently). But this results in more code in each component to handle these, instead of letting the form-component/Felte let handle this.

Instead of providing a store, maybe a method like setFields(), but for initial values (setInitialValues())?

I will report back once I tried your proposed solution.

taffit commented 2 years ago

Hello @pablo-abc , so I tried your mentioned solution, but unfortunately it didn't work. I need the initial values somehow to set during form creation as well as when the backend processed the entries. As these are not available via Felte, I created and handle them outside of Felte. setFields() does what it says, and this works, but when resetting, the initial values are set again instead of the updated initialValues. So I'm using now my own reset-method.
As I have another problem right now (in combination with @imask/svelte: somehow the events overlay each other, so that the masking overwrites the current value instead of "extending" of what is in the input-field right now), I'm afraid that I need to turn to some other form-library (svelte-formula has the same problem, btw).

So from my side this issue can be closed now.

pablo-abc commented 2 years ago

Hey, @taffit ! Thanks for giving Felte a try. Regarding the initial values issue, I guess I'm not seeing how handling this within Felte would remove complexity from your requirements. Is this more or less what you're referring to about resetting the form to different "initialValues"?

If initialValues were a store (or if a method to change it would be exposed) I guess I don't see much difference on the work you'd have to do from handling it outside of Felte with a custom reset form:

// This would be reactive regardless
let initialValues = {};
const { form } = createForm({ initialValues });

To handling it within Felte:

// Assuming it is a store
const { form, initialValues } = createForm({ initialValues: {} });

Your logic outside of Felte would need to be almost the same regardless of if Felte handles it internally or not. Specially since your validation requirements (comparing current values with latest initialValues) seem, at least to me, as a bit of a special case scenario.


Regarding imask, you're not the first person to open an issue regarding this. This is a scenario on which in order to guarantee no race conditions with Felte I have dabbled around making a package integrating vanilla imask. (E.g. maybe a @felte/imask package). Anyway this is something I'll need to explore more in-depth at a future date. Thanks for your input, it serves a lot for improving Felte!

taffit commented 2 years ago

@pablo-abc You are probably right about the initialValues. I just thought that it might be slightly simpler if I can refer to the initialValues from Felte when comparing (as it contains initialValues already), but you are absolutely right: there is not much difference.
I created a REPL to illustrate my problem/intention, however as $touched seems to not work on the REPL (or more probable: I'm just too dumb ;-)), I couldn't finish it. Strangely also the isFormTouched doesn't work on the REPL while working fine locally. As it is only half-finished, take it with caution. But maybe it clarifies some questions.
It's comforting that even experienced developers have problems in combination with imask. Feeling a little less stupid now :-)

Whatever, thank you @pablo-abc for the prompt responses and @loremaps for the quick implementation of my needs. I was very impressed on how such feature requests were handled, as well as the whole Felte library, which IMHO is a very mature, complete and feature full one to handle forms with svelte. Felt at home immediately ;-).

(Fwiw, I try to revert now to svelte-use-form, which suffers from some other problems, but at least in combination with imask I didn't experience any problems. Hopefully I'm remembering this one correctly ;-))

Thank you for such a wonderful library!

pablo-abc commented 2 years ago

I've experienced that, depending on the packages you use, CodeSandbox works better than the REPL sometimes. I'll check it out.

Regarding the touched part. The function of the touched store is just to let you know if the user has interacted with the field. On text/number/email/etc inputs this means focusing + blurring, on other inputs just interacting with it. Unless I'm not understanding correctly, the comparison to your new initial values would better be handled within the validation process rather than with the touched store?

Anyway, thanks a lot for taking your time to answer to my follow up questions! It's really appreciated!

taffit commented 2 years ago

Sorry, @pablo-abc , but I am now back at Felte again ;-). Just wanted to tell you about my findings regarding imask (which I will post in the related issue as well).
After reverting to svelte-use-form, I experienced the same problems there in combination with @imask/svelte. As I remembered that I had it working there, I continued experimenting.

What doesn't work: Having imask as action (as @imask/svelte provides) on the input-element in combination with some other library that accesses the field/events of the element. I experimented a bit with the events and the action, as e. g. adding an on:focus which invokes the sometimes mentioned IMask.updateValue(), but to no avail. And I'm also too noob on the frontend site to invest more time on this.

What does work, though:

This way it seems that imask got the value updated (I experienced some other quirks after resetting the form, but that may be my fault, i. e. parts of the mask reflect the former modifications. E. g. the field contains 1.000 (masked) -> I change it to 231.000 (masked) -> Reset -> 1.000 -> I add 987 on the head of the number -> 9.8731.000, i. e. the 3 from the previous edit is added. May be my fault, though).
What helps in this case with svelte-use-form is, that it contains a store with all the html-elements of the form. This way you can easily iterate and e. g. attach a mask or similar. That would be maybe useful also for Felte (or is there a way to iterate the form-controls that I didn't find yet?)

Regarding your proposal for moving the check for changed values to the validation: imo this is something different. Let me clarify my intention/idea behind all this. I want to make some forms as idiot-user-proof ( ;-) ) as possible while hopefully being as user-friendly as possible.

But maybe I just pretend too much from the frontend :-) .

Tl;dr: Moving back to Felte as I found a workaround for the imask-problem

Edit: For completeness sake, a working REPL hopefully showing what I want is available here. It works fine, there is just so much to add in order to make it work. We have defaultData, initialData, initialValues (containing the (partly formatted) initialData), element-bindings and masks for each input.