From f4eb2911977230b08d6a2be26309097764748e69 Mon Sep 17 00:00:00 2001 From: Ansh Date: Tue, 25 Nov 2025 12:35:55 +0530 Subject: [PATCH 1/2] fix(form-core): validate fields without components when setFieldValue is called - Fix validateField to run form-level validation for fields without field instances - Add test cases to verify validation works for fields without components - Resolves issue where setFieldValue didn't trigger validation for fields without components Fixes: Form validation not working for fields without associated components --- packages/form-core/src/FormApi.ts | 8 +- .../tests/setFieldValue-validation.spec.ts | 86 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 packages/form-core/tests/setFieldValue-validation.spec.ts diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index b4887b97d..1cf7918d2 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1589,7 +1589,13 @@ export class FormApi< ) => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const fieldInstance = this.fieldInfo[field]?.instance - if (!fieldInstance) return [] + + if (!fieldInstance) { + // If there's no field instance, we still need to run form-level validation + // to ensure fields without components can still be validated + this.validate(cause) + return [] + } // If the field is not touched (same logic as in validateAllFields) if (!fieldInstance.state.meta.isTouched) { diff --git a/packages/form-core/tests/setFieldValue-validation.spec.ts b/packages/form-core/tests/setFieldValue-validation.spec.ts new file mode 100644 index 000000000..362e05dd0 --- /dev/null +++ b/packages/form-core/tests/setFieldValue-validation.spec.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from 'vitest' +import { FormApi } from '../src/FormApi' + +describe('setFieldValue validation for fields without components', () => { + it('should validate field when setFieldValue is called even without field component', () => { + const form = new FormApi({ + defaultValues: { + name: '', + }, + validators: { + onChange: ({ value }) => { + if (!value.name) { + return { + fields: { + name: 'Name is required', + }, + } + } + return undefined + }, + }, + }) + + form.mount() + + // Initially, form should be valid (no validation errors yet) + expect(form.state.isValid).toBe(true) + expect(form.state.errors).toEqual([]) + + // Set field value to empty string (should trigger validation and show error) + form.setFieldValue('name', '') + + // Form should now be invalid due to validation error + expect(form.state.isValid).toBe(false) + expect(form.state.fieldMeta.name?.errors).toEqual(['Name is required']) + + // Set field value to valid value (should clear validation error) + form.setFieldValue('name', 'John') + + // Form should now be valid again + expect(form.state.isValid).toBe(true) + expect(form.state.fieldMeta.name?.errors).toEqual([]) + }) + + it('should validate field with form-level async validator', async () => { + const form = new FormApi({ + defaultValues: { + email: '', + }, + validators: { + onChangeAsync: async ({ value }) => { + if (!value.email) { + return { + fields: { + email: 'Email is required', + }, + } + } + return undefined + }, + }, + }) + + form.mount() + + // Set field value to empty string (should trigger async validation) + form.setFieldValue('email', '') + + // Wait for async validation to complete + await new Promise(resolve => setTimeout(resolve, 100)) + + // Form should now be invalid due to validation error + expect(form.state.isValid).toBe(false) + expect(form.state.fieldMeta.email?.errors).toEqual(['Email is required']) + + // Set field value to valid value (should clear validation error) + form.setFieldValue('email', 'john@example.com') + + // Wait for async validation to complete + await new Promise(resolve => setTimeout(resolve, 100)) + + // Form should now be valid again + expect(form.state.isValid).toBe(true) + expect(form.state.fieldMeta.email?.errors).toEqual([]) + }) +}) \ No newline at end of file From 96f6d69267aa1d81b53940fbc888944a03be9311 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 08:09:48 +0000 Subject: [PATCH 2/2] ci: apply automated fixes and generate docs --- packages/form-core/src/FormApi.ts | 2 +- .../tests/setFieldValue-validation.spec.ts | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 1cf7918d2..5b6d69160 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1589,7 +1589,7 @@ export class FormApi< ) => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const fieldInstance = this.fieldInfo[field]?.instance - + if (!fieldInstance) { // If there's no field instance, we still need to run form-level validation // to ensure fields without components can still be validated diff --git a/packages/form-core/tests/setFieldValue-validation.spec.ts b/packages/form-core/tests/setFieldValue-validation.spec.ts index 362e05dd0..d0653be93 100644 --- a/packages/form-core/tests/setFieldValue-validation.spec.ts +++ b/packages/form-core/tests/setFieldValue-validation.spec.ts @@ -29,14 +29,14 @@ describe('setFieldValue validation for fields without components', () => { // Set field value to empty string (should trigger validation and show error) form.setFieldValue('name', '') - + // Form should now be invalid due to validation error expect(form.state.isValid).toBe(false) expect(form.state.fieldMeta.name?.errors).toEqual(['Name is required']) // Set field value to valid value (should clear validation error) form.setFieldValue('name', 'John') - + // Form should now be valid again expect(form.state.isValid).toBe(true) expect(form.state.fieldMeta.name?.errors).toEqual([]) @@ -65,22 +65,22 @@ describe('setFieldValue validation for fields without components', () => { // Set field value to empty string (should trigger async validation) form.setFieldValue('email', '') - + // Wait for async validation to complete - await new Promise(resolve => setTimeout(resolve, 100)) - + await new Promise((resolve) => setTimeout(resolve, 100)) + // Form should now be invalid due to validation error expect(form.state.isValid).toBe(false) expect(form.state.fieldMeta.email?.errors).toEqual(['Email is required']) // Set field value to valid value (should clear validation error) form.setFieldValue('email', 'john@example.com') - + // Wait for async validation to complete - await new Promise(resolve => setTimeout(resolve, 100)) - + await new Promise((resolve) => setTimeout(resolve, 100)) + // Form should now be valid again expect(form.state.isValid).toBe(true) expect(form.state.fieldMeta.email?.errors).toEqual([]) }) -}) \ No newline at end of file +})