New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Form binding support for React Vaadin components #587
Comments
This is what I can see by building a simple form using Hilla, React, Formik, and Yup. Consider an endpoint like this one: @Endpoint
@AnonymousAllowed
public class HelloFormEndpoint {
@Nonnull
public String validate(@Nonnull RegistrationInfo info) {
return "Registration accepted";
}
public static record RegistrationInfo(
@NotBlank String name,
@NotBlank @Email String email,
@Pattern(regexp = "^[0-9]+$") String phone,
@Size(min = 2, max = 2) String country,
@AssertTrue boolean conditions) {}
} It can be used in this form: <Formik
initialValues={{ ...initialValues }}
validationSchema={yupSchema}
onSubmit={async values => {
const response = await HelloFormEndpoint.validate(values);
Notification.show(response, { theme: 'success' });
}}
>
{({ submitForm, isValid, dirty }) => (
<Form>
<VerticalLayout theme="spacing padding">
<Field name="name">
{({ field, meta }: any) => (
<TextField {...field} placeholder="Name"
invalid={meta.error && meta.touched} errorMessage={meta.error} />
)}
</Field>
<Field name="email">
{({ field, meta }: any) => (
<TextField {...field} placeholder="Email"
invalid={meta.error && meta.touched} errorMessage={meta.error} />
)}
</Field>
<Field name="phone">
{({ field, meta }: any) => (
<TextField {...field} placeholder="Phone"
invalid={meta.error && meta.touched} errorMessage={meta.error} />
)}
</Field>
<Field name="country">
{({ field, meta }: any) => (
<Select {...field} placeholder="Country" items={countries} name={field.name}
invalid={meta.error && meta.touched} errorMessage={meta.error} />
)}
</Field>
<Field name="conditions">
{({ field, meta }: any) =>
<Checkbox {...field} label="I accept terms and conditions"
invalid={meta.error && meta.touched} errorMessage={meta.error}
onCheckedChanged={field.onChange} />
}
</Field>
<Button theme="primary" onClick={submitForm} disabled={!(dirty && isValid)}>Submit</Button>
</VerticalLayout>
</Form>
)}
</Formik> Both const initialValues: RegistrationInfo = {
name: '',
email: '',
phone: '',
country: '',
conditions: false,
};
const yupSchema: ObjectSchema<RegistrationInfo> = object({
name: string().required(),
email: string().email().required(),
phone: string().matches(/^[0-9]+$/).required(),
country: string().min(2).max(2).required(),
conditions: boolean().oneOf([true]).required(),
}); Concerning fields, there's a clear pattern that could be formalized and implemented to replace them with something like <Formik
initialValues={{ ...initialValues }}
validationSchema={yupSchema}
onSubmit={async values => {
const response = await HelloFormEndpoint.validate(values);
Notification.show(response, { theme: 'success' });
}}
>
{({ submitForm, isValid, dirty }) => (
<Form>
<VerticalLayout theme="spacing padding">
<FormTextField name="name" placeholder="Name" />
<FormTextField name="email" placeholder="Email" />
<FormTextField name="phone" placeholder="Phone" />
<FormSelect name="country" placeholder="Country" items={countries} />
<FormCheckbox name="conditions" label="I accept terms and conditions" />
<Button theme="primary" onClick={submitForm} disabled={!(dirty && isValid)}>Submit</Button>
</VerticalLayout>
</Form>
)}
</Formik> |
Let's consider a generic <Form>
<FormLayout>
<FormField component={TextField} name="name" label="Name" />
<FormField component={EmailField} name="email" label="Email" />
<Button theme="primary" onClick={submitForm} disabled={!(dirty && isValid)}>Submit</Button>
</FormLayout>
</Form> This would then support both HTML and Vaadin React components, and even custom third-party components, for as long as they are based on Vaadin components or follow Vaadin conventions to some extent. |
That would be great, but how do you handle special needs like the I also don't see the difference between our |
I assume we can rely on events that work with all components, such as HTML standard The Formik's |
Hello everyone! I'm interested in the discussion on React Form support in Hilla and wanted to share my thoughts on the use of Formik for field validation. While Formik has been a popular choice for managing form state and validation in React, I noticed that its development activity seems to be slowing down - the last release (2.2.9) was back in June 2021. In light of this, I'd like to suggest considering React Hook Form instead, as it's actively developed and provides an easy-to-use API for building forms in React. I found some helpful information on this topic in the 'Is Formik dead?' issue on GitHub (jaredpalmer/formik#3526). What are your thoughts on this? Have you considered React Hook Form as an alternative to Formik for hilla? |
Hello, yes, React Hook Form is definitely in the list of candidates. The link you cited confirms that it should get more attention than Formik. |
Should we change the title of this ticket until we decide which form library we plan to support? |
Now that we have realised that Formik is not a sustainable choice, we have to pick some other library. While React Hook Form is a good candidate, the alternative that we are also considering right now is adapting the existing Hilla+Lit form binder to React. Right now it seems that the complexity in the form binding feature is not in the management of the from state, lifecycle, and client-side validation (all libraries are reasonably good at this), but rather in integration with Vaadin components and Hilla endpoints, both of which use a number of custom conventions / APIs, that are not generally supported by third-party form libraries out-of-the-box. Reusing Hilla+Lit form binder is a quick way to address all these integration challenges (both this issue and the related validation one #585), with some additional benefits in the resulting solution. I did some prototyping for the idea of using Hilla form binder with React and hooks. Here is the API I came up with in the prototype: export default function FormView() {
const { model, submit } = useBinder(EntityModel, {onSubmit: FormEndpoint.sendEntity});
return (
<>
<section className="flex p-m gap-m items-baseline">
<TextField label="Name" {...field(model.name)}></TextField>
<ComboBox label="Choose" {...field(model.choice)} items={comboBoxItems}></ComboBox>
<NumberField label="Number" {...field(model.number)}></NumberField>
<DatePicker label="Date" {...field(model.date)}></DatePicker>
<Button onClick={submit}>submit</Button>
</section>
</>
);
} The result is quite similar to React Hook Form. This is not a big surprise to me, as we took some inspiration from React hooks in the original Hilla+Lit form binder itself. Please share your feedback about this idea and the API. |
Let us use this issue as an epic for the other Hilla React binder implementation issues. |
No description provided.
The text was updated successfully, but these errors were encountered: