Open EuSouVoce opened 2 years ago
Hi! It's nice to see the discussion, I just want to share some implementation that I did using SolidStart in SSR. @fabian-hiller thank you for the awesome libs!!
import { SubmitHandler, createForm } from "@modular-forms/solid";
import { createServerAction$ } from "solid-start/server";
type SomeForm = {
title: string;
file: File | undefined;
};
export default function MyForm() {
const [_, Publish] = createServerAction$(async (formData: FormData) => {
console.log(formData);
});
const [__, { Form, Field }] = createForm<SomeForm>();
const handleSubmit: SubmitHandler<SomeForm> = async (values: {
[key: string]: any;
}) => {
try {
const body = new FormData();
for (let key in values) {
body.append(key, values[key]);
}
// Submit the action
await Publish(body);
} catch (error) {
console.log(error);
}
};
return (
<Form onSubmit={handleSubmit} enctype="multipart/form-data">
{/* Your Form */}
</Form>
);
}
@snowfluke yes it's possible to use modular-forms with SSR, the question is more about ergonomic validation. Like your form would work without JS enabled in the browser if you would add action={Publish.url}
, but validation on form values from modular-forms vs FormData on the server can be different (I think so at least 😅). Of course one could always validate on FormData I guess. I don't even know how this works for the other libraries modular-forms supports, gotta take a look.
Ah I see that one has to specify fields with special data types. I think the main blocker for built-in progressively enhanced forms for solid is still the extensibility of solid-start's APIs, as mentioned earlier.
Idk man, my head hurt and the docs is still lacking need to wait for 1.0
.
I know there's no specific support for server validation with React, but is there anyway to pass the server's response back into Modular Forms? Conform does this thing where you can get an action response from Remix and pass that to the useForm
hook. It populates the form's state.
Doesn't the form hold the state? If it is about the state at initialization, you can use initialValues
for the form values.
Doesn't the form hold the state? If it is about the state at initialization, you can use initialValues for the form values.
I guess initialValues
would work. I'm thinking about how to validate on the server and return the form state to the client from an action.
Big fan of Modular Forms with Qwik so I'm trying to use it with React to make a later Remix => Qwik City migration easier.
It would seem like there's no way to avoid the built-in onSubmit
handler. I'm wanting to have the form validation and then if it passes just do the normal browser submission if I don't pass my own onSubmit
handler to the form.
The problem is that Modular Forms for React is a bit experimental due to the Preact Signals. For example, it doesn't currently work with Next.js. Also, Signals are still mostly unfamiliar to React developers. So I'm not sure about the future of Modular Forms for React yet.
Yeah, I kept thinking about it and because Modular Forms requires you to use its own <Form />
component, you can't use Remix's <Form />
component and it probably can't work the way I'd like it to.
I think that Modular Forms does not require that. You can also use useFormStore
with a custom Form
component.
So pass the useFormStore
value to as of
, copy most of the onSubmit
hook from <Form />
, and then disable the preventDefault
it might work.
Yes, that could work. Feel free to try it out. In the end, the individual data from useFormStore
are simply held in signals.
Would there be any problems with using Modular Forms' <Field />
components without Modular Forms' <Form />
component—as long as you pass the form state to of
?
I managed to get this working my copying most of the Form component's onSubmit handler and modifying it to play nice with Remix's usual behavior on the client.
The last problems are:
FormData
for the server
For the FormData and server stuff you can check how I solved it for Qwik. The Field
component does not require our Form
component. The only thing that is needed in most cases ist our store object. You can also write me on Discord if that is easier for you.
What if createForm
accepts a Submission object and automatically extracts SSR input values and submission errors?
import { action, useSubmission } from '@solidjs/router'
import { createForm, required, email, minLength } from 'modular-forms'
import * as v from 'valibot'
const LoginSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
})
type LoginForm = v.InferOutput(typeof LoginSchema)
const login = action(async (fromData: FormData) => {
'use server'
const data = v.parseFormData(LoginSchema, formData) // throws FormError if necessary
console.log(`Login with email "${data.email}" and password ${data.password}`)
})
const LoginPage = () => {
const submission = useSubmission(login)
const [loginForm, { Form, Field }] = createForm<LoginForm>({
submission,
initialValues: {
email: 'user@email.com' // uses submission.input[0].get('email') || initialValues.email for initial state under the hood
}
})
return (
<Form> // action={submission.url} and method="post" are set by default
<Field
name="email"
validate={[
required('Please enter your email.'),
email('The email address is badly formatted.'),
]}
>
{(field, props) => (
<input
{...props}
value={field.value || ''}
type="email"
placeholder="Email"
required
prop:setCustomValidity={field.error || ''} // extracts error message from submission.error
/>
)}
</Field>
<Field
name="password"
validate={[
required('Please enter your password.'),
minLength(8, 'You password must have 8 characters or more.'),
]}
>
{(field, props) => (
<input
{...props}
value={field.value || ''}
type="password"
placeholder="Password"
required
prop:setCustomValidity={field.error || ''} // extracts error message from submission.error
/>
)}
</Field>
<button type="submit">Login</button>
</Form>
)
}
I am still focused on Valibot and cannot work on this at the moment. Sorry about that!
It would be awsome if it runs and validates on the server side too...