Skip to content

Conversation

@rudrokhanpro
Copy link
Contributor

πŸ”— Linked issue

Nested dirty UForm doesn't make it's parent dirty #5144

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

If current form component has a parent form, emit 'input', 'change', 'focus' or 'blur' via the parent form event bus.

Prior to this change, dirtyFields and touchedFields didn't take into account nestedForms.

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 30, 2025

npm i https://pkg.pr.new/@nuxt/ui@5566

commit: 9bcf032

// Propagate dirty and touched fields to parent form
if ((event.type === 'change' || event.type === 'input' || event.type === 'focus' || event.type === 'blur') && parentBus) {
parentBus.emit(event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parentBus.emit(event)
const eventToPropagate = props.name ? { ...event, name: props.name as any } : event
parentBus.emit(eventToPropagate)

Event field names from nested forms are not properly scoped when propagated to the parent, causing incorrect entries in parent form's dirtyFields and touchedFields sets.

View Details

Analysis

Nested form events not properly scoped when propagated to parent form

What fails: Nested form field names are not prefixed with the nested form's path when propagating input events to the parent form, causing unscoped field names to be added to parent form's dirtyFields, touchedFields, and blurredFields sets, creating type mismatches and inconsistency with how errors are handled.

How to reproduce:

  1. Create a parent form with schema containing a nested object field, e.g., { email: string, address: { street: string } }
  2. Render a nested Form component with name="address" and schema { street: string }
  3. User types in the nested "street" input field
  4. Check parentForm.dirtyFields - it contains 'street' instead of 'address'

Example fixture in test/components/fixtures/FormNested.vue shows this pattern where parent has a nested form at name="nested" with a field "field". When user modifies this nested field, the parent's dirtyFields receives 'field' instead of 'nested'.

What happens vs expected behavior:

Current behavior:

  • Nested form's useFormField emits event: { type: 'change', name: 'street' }
  • Nested form receives it, correctly adds 'street' to its own dirtyFields
  • Nested form propagates to parent WITHOUT scoping: parentBus.emit({ type: 'change', name: 'street' })
  • Parent receives { type: 'change', name: 'street' } and adds 'street' to parent's dirtyFields
  • Result: Parent dirtyFields contains invalid key 'street' (should be 'address' or 'nested')

Expected behavior:

  • Nested form should scope the event name to its own path before propagating
  • Event propagated to parent should be: { type: 'change', name: 'address' }
  • Parent should add 'address' (valid key) to its dirtyFields

Type signature shows the issue:

const dirtyFields: Set<keyof I> = reactive(new Set<keyof I>())
// where I = InferInput<S>
// For parent form, this should only contain valid keys from parent schema like 'email' | 'address'

Root cause: Lines 167-169 in src/runtime/components/Form.vue emit events to parent without modifying the field name. When errors are handled (line 226), addFormPath() is used to prefix error names with the nested form's path. The same scoping should be applied to field name events.

Why it's a bug:

  1. Type mismatch: dirtyFields declared as Set<keyof I> will contain invalid keys
  2. Inconsistency: Error paths ARE scoped using addFormPath(), but event names are NOT
  3. API contract violation: Form.dirtyFields should only contain keys that exist in the form's schema
  4. Developers checking form.dirtyFields see nested field names instead of top-level field names, breaking abstraction

Reference: See Form type definition at src/runtime/types/form.ts line 18 which specifies dirtyFields: ReadonlySet<DeepReadonly<keyof FormData<S, false>>> - only valid schema keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant