T
TanStack3mo ago
evident-indigo

Field level validation possible?

Field level validation possible? I'm working on an webapp with many forms and many types of input. If I have a input field <AgeInputField/>, I want to do the input validation inside this component, not on the form level. All manuals I've found, assume the validation is on the form level: you have to add all validations in each form. Is there no way to do this on the input field level? This is not a rare use case, right? So I have a field like this export function IdFormField() { const field = useFieldContext<string>(); // Add validation function here return <div className={styles.inputLine}> <TextField label="Id" value={field.state.value} onChange={(event) => field.handleChange(event.target.value)} /> </div>; } Then, in my form: <form.AppField name="id"> {(field) => <field.IdField/>} </form.AppField> I see there are functions like field.pushValue() and field.setErrorMap(), but they're poorly documented and I can't find how to use them. An alternative solution would be to pass form to the input field component. I'm developing with TypeScript, and to pass form properly typed, I have to use AppFieldExtendedReactFormApi<> with 14 (fourteen!) generic parameters. I tried to use broad types, but couldn't make the types correct. In short: Where can I find documentation about the fieldAPI? So, the API as listed here: https://tanstack.com/form/v1/docs/reference/classes/fieldapi but with explanations, examples etcetera. And if that's not possible, how can I pass form well typed to a component?
9 Replies
equal-jade
equal-jade3mo ago
so you want to have a preset field that has only field level validation ready, and many forms should be able to use it? withFieldGroup could be a solution to that. It allows you to write sections of forms so that multiple forms may use them the thing is that when you do field level validators, it would not be
<form.AppField name="id">
{(field) => <field.IdField validators={}/>}
</form.AppField>
<form.AppField name="id">
{(field) => <field.IdField validators={}/>}
</form.AppField>
but instead
<form.AppField name="id" validators={}>
{(field) => <field.IdField />}
</form.AppField>
<form.AppField name="id" validators={}>
{(field) => <field.IdField />}
</form.AppField>
evident-indigo
evident-indigoOP3mo ago
I thought withFieldGroup was specifically to let multiple fields "talk" to each other. But you can also use it for a single input field. I'll use this. Thanks!
equal-jade
equal-jade3mo ago
yes, because that's the most common logic that has to be associated with fields that you don't want to repeat too often but field validation is another case for it
evident-indigo
evident-indigoOP2mo ago
Thanks for your answer. Is it also possible to pass a string instead of an object? const defaultValues: FormValues = { name: "", age: 0, account_data: { password: "", confirm_password: "", }, }; It seems withFieldGroup can only deal with account_data and not with name. My form data is just an object with key: values . Do I have to write a conversion function (and convert back function) for withFieldGroup to work?
equal-jade
equal-jade2mo ago
a string is a shorthand for all shallow keys starting with the string so in this case, account_data as string would just be account_data.password and account_data.confirm_password if you want to allow strings for your field group, you can put them into a namespace
evident-indigo
evident-indigoOP2mo ago
Oh, I meant the other way around. This is the example from the documentation: const defaultValues: FormValues = { name: "", age: 0, account_data: { password: "", confirm_password: "", }, }; But my data looks like this: const defaultValues: FormValues = { name: "", age: 0, password: "", confirm_password: "", }; But is seems withFieldGroup only accepts an object like account_data: { password: "", confirm_password: "", }, and not a top level property. Argh, I just discovered another problem: the property name (like "password") in the field component must be the same as the property name in the form. But I want to make them independent
equal-jade
equal-jade2mo ago
if account_data isn't an object that you expect to receive (because it could be called anything), then you should push those fields to the top level instead
- account_data: { password: '' }
+ account_password: ''
- account_data: { password: '' }
+ account_password: ''
evident-indigo
evident-indigoOP2mo ago
I found that assigning an object to fields solves my problem. Thanks for your help.
equal-jade
equal-jade2mo ago
in case you need to check for the future:
// these are syntactically the same
<MyGroup form={form} fields="nested" />
<MyGroup form={form} fields={{
"a": "nested.a",
"b": "nested.b",
"c": "nested.c"
}} />
// these are syntactically the same
<MyGroup form={form} fields="nested" />
<MyGroup form={form} fields={{
"a": "nested.a",
"b": "nested.b",
"c": "nested.c"
}} />

Did you find this page helpful?