Array fields | Validating value by index, validating one array "row" exclusively #2025
Unanswered
m-kolomoyets
asked this question in
Q&A
Replies: 1 comment
-
|
UPD: I have implemented the separate "sub-form" for each table row that gets default values from the parent form and mutates the parent form only when data is valid and row submit trigger is called. In addition, the logic of removing the row from. both dynamic table and parent form if data is not initially in parent form by the index and uer clicks "cancel". Here is short demo of the result. CleanShot.2026-02-12.at.11.30.54.mp4Code for implementing it (with project-specific abstractions, but the whole idea is recognizable): Schema export const employeeItemSchema = z.object({
firstName: requiredStringSchema,
lastName: requiredStringSchema,
email: emailSchema,
phone: phoneSchema,
});
export const employeesInformationStepSchema = z.object({
// other fields
employees: z.array(employeeItemSchema).min(1, 'At least one employee is required'),
});Form part renders the array <Table>
<TableHeader>
<TableRow>
<TableHead>First name</TableHead>
<TableHead>Last name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Phone number</TableHead>
<TableHead />
</TableRow>
</TableHeader>
<TableBody>
<form.Field name="employees" mode="array">
{(field) => {
if (!field.state.value.length) {
return (
<TableRow>
<TableCell colSpan={5}>
<EmptyState className={s.empty}>
<EmptyStateTitle>There's nothing here yet</EmptyStateTitle>
<EmptyStateDescription>
Add employees manually or bulk CSV upload.
</EmptyStateDescription>
</EmptyState>
</TableCell>
</TableRow>
);
}
return field.state.value.map((_, rowIndex) => {
return (
<EmployeeTableEditableRow
form={form}
key={`employee-table-editable-row-${rowIndex}`}
index={rowIndex}
/>
);
});
}}
</form.Field>
</TableBody>
</Table>EmployeeTableEditableRow Component export const EmployeeTableEditableRow = withForm({
...getEmployerApplicationStepFormOptions('employeesInformationStep'),
props: {} as EmployeeTableEditableRowProps,
render: function Render({ form, index }) {
const { openNotification } = useToastNotificationContext();
const { editingRowIndex, setEditingRowIndex } = useEmployeesEditableRowsContext();
const initialEmployeeFromForm = useStore(form.store, (state) => {
return state.values.employees[index];
});
const editableRowForm = useAppForm({
defaultValues: initialEmployeeFromForm,
validators: {
onSubmit: employeeItemSchema,
},
onSubmit({ value }) {
form.setFieldValue(`employees[${index}]`, value);
setEditingRowIndex(null);
},
});
const isEditingCurrentRow = editingRowIndex === index;
return (
<TableRow key={`employee-table-editable-row-${index}`}>
<editableRowForm.AppField
name="firstName"
validators={{
onChange: employeeItemSchema.shape.firstName,
}}
>
{(firstNameSubField) => {
return (
<TableCell className={s.cell} data-state={isEditingCurrentRow ? 'editing' : 'not-editing'}>
<EditableCell isEditing={isEditingCurrentRow} value={firstNameSubField.state.value}>
<FormFieldWrapper name={firstNameSubField.name}>
<firstNameSubField.InputField placeholder="Enter first name" size="sm" />
</FormFieldWrapper>
</EditableCell>
</TableCell>
);
}}
</editableRowForm.AppField>
<editableRowForm.AppField
name="lastName"
validators={{
onChange: employeeItemSchema.shape.lastName,
}}
>
{(lastNameSubField) => {
return (
<TableCell className={s.cell} data-state={isEditingCurrentRow ? 'editing' : 'not-editing'}>
<EditableCell isEditing={isEditingCurrentRow} value={lastNameSubField.state.value}>
<FormFieldWrapper name={lastNameSubField.name}>
<lastNameSubField.InputField placeholder="Enter last name" size="sm" />
</FormFieldWrapper>
</EditableCell>
</TableCell>
);
}}
</editableRowForm.AppField>
<editableRowForm.AppField
name="email"
validators={{
onChange: employeeItemSchema.shape.email,
}}
>
{(emailSubField) => {
return (
<TableCell className={s.cell} data-state={isEditingCurrentRow ? 'editing' : 'not-editing'}>
<EditableCell isEditing={isEditingCurrentRow} value={emailSubField.state.value}>
<FormFieldWrapper name={emailSubField.name}>
<emailSubField.InputField
placeholder="Enter email"
type="email"
inputMode="email"
size="sm"
/>
</FormFieldWrapper>
</EditableCell>
</TableCell>
);
}}
</editableRowForm.AppField>
<editableRowForm.AppField
name="phone"
validators={{
onChange: employeeItemSchema.shape.phone,
}}
>
{(phoneSubField) => {
const formattedPhone = phoneSubField.state.value
? formatPhoneNumberIntl(phoneSubField.state.value)
: '';
return (
<TableCell className={s.cell} data-state={isEditingCurrentRow ? 'editing' : 'not-editing'}>
<EditableCell isEditing={isEditingCurrentRow} value={formattedPhone}>
<FormFieldWrapper name={phoneSubField.name}>
<phoneSubField.PhoneNumberInputField placeholder="Enter last name" size="sm" />
</FormFieldWrapper>
</EditableCell>
</TableCell>
);
}}
</editableRowForm.AppField>
<form.Field name="employees" mode="array">
{(field) => {
return (
<TableCell className={clsx(s.cell, s.actions)}>
<RowEditToolbar
isEditing={index === editingRowIndex}
onCancel={() => {
const isAllInitialFieldsEmpty = Object.values(initialEmployeeFromForm).every(
(value) => {
return !value;
}
);
if (isAllInitialFieldsEmpty) {
field.removeValue(index);
}
setEditingRowIndex(null);
}}
onEdit={() => {
if (!!editingRowIndex) {
openNotification({
variant: 'info',
title: 'Can not start editing other row',
description: `Please complete editing row #${editingRowIndex + 1} to start editing other rows`,
});
return;
}
setEditingRowIndex(index);
}}
onSave={() => {
editableRowForm.handleSubmit();
}}
onDelete={() => {
field.removeValue(index);
setEditingRowIndex(null);
}}
/>
</TableCell>
);
}}
</form.Field>
</TableRow>
);
},
}); |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello everyone. Is there any way to trigger validation of the some index of array field item with built-in Tanstack Form tools? As far as I researched, it is possible to trigger validation of the whole array (field has array validation schema), or a whole form.
A bit of context:
I have a component of table where entries can be added, edited and removed. While editing, I have to check whether the row itself is filled correctly.
Have anyone faced similar issue and how have you resolved this?
Please let me know if some additional input data is needed.
Thanks in advance
Beta Was this translation helpful? Give feedback.
All reactions