Ten dialogs disable submit on vee-validate's meta.valid, which can be false with zero errors #178

Open
opened 2026-09-29 08:07:01 +00:00 by padreug · 0 comments
Owner

Split out from #177, which fixed this for the event-creation form only.

What happened

On demo, a fully filled event form — title, dates, price, capacity, payment methods, a promo code and three named ticket waves — had its Submit button greyed with nothing on screen explaining why. Diagnosis:

  • document.querySelectorAll('[aria-invalid="true"]') → empty, so no field had an error
  • the Cancel button was live, and it shares the same :disabled="isLoading" binding, so the in-flight flag was false
  • the non-schema validators (promo codes, ticket waves) returned clean for that exact data

So meta.valid was false while errors was empty — vee-validate #4630 reproduced. The organiser had no way to discover that, and no way to proceed.

Why meta.valid is the wrong thing to gate on

  • Disabled fields count against validity. The docs state "disabled fields are not necessarily valid... they affect the Form meta just like other fields"; disabled was removed from <Field> because consistent behaviour was unachievable. Most of our dialogs disable inputs while submitting.
  • It goes stale — #4630: false with no errors after a form is re-shown, and it never recovers.
  • It is false before the form has ever validated, which is why the docs suggest valid && dirty rather than valid.

A flag unreliable in both directions should not be the only thing between a user and their action — least of all as a disabled button, which offers no affordance for finding out why.

The other ten sites

Every one disables a button on !isFormValid, and none uses handleSubmit's invalid callback (only CreateEventDialog does, since #177):

file binding
wallet/ReceiveDialog.vue !isFormValid || isCreating
chatelet/BookRoomDialog.vue !isFormValid || flow.isRequesting.value
market/CreateProductDialog.vue isCreating || !isFormValid
market/CreateStoreDialog.vue isCreating || !isFormValid
market/MarketSettings.vue isSaving || !isFormValid
base/ProfileSettings.vue isUpdating || !isFormValid
expenses/AddExpense.vue isSubmitting || !isFormValid
nostr-feed/NoteComposer.vue isPublishing || !isFormValid
nostr-feed/RideshareComposer.vue isPublishing || !isFormValid
accounting-app/views/AddIncome.vue isSubmitting || !isFormValid

ReceiveDialog and BookRoomDialog look like the ones to do first — a user unable to create an invoice, or a guest unable to book a room, with no error and no recourse.

The pattern from #177

  1. Disable only while a submit is genuinely in flight.
  2. Pass handleSubmit an invalid callback that names the offending fields and surfaces them — ours toasts "Check Tickets, Start date, Title", opens whichever collapsible holds one, and scrolls the first into view.
  3. Validate anything outside the Zod schema inside the submit handler, revealing its section rather than returning silently.

Worth also checking each form for a schema field with no FormField in the template — CreateEventDialog has one (notification_subject), so an error there could never have been displayed. Harmless while it defaults to '', but the same latent shape.

Not urgent

Nothing is broken today beyond what #177 already fixed; this is a class of latent dead-ends. Filing so it is tracked rather than rediscovered one blocked user at a time.

Split out from #177, which fixed this for the event-creation form only. ## What happened On demo, a fully filled event form — title, dates, price, capacity, payment methods, a promo code and three named ticket waves — had its Submit button greyed with **nothing on screen explaining why**. Diagnosis: - `document.querySelectorAll('[aria-invalid="true"]')` → **empty**, so no field had an error - the Cancel button was live, and it shares the same `:disabled="isLoading"` binding, so the in-flight flag was false - the non-schema validators (promo codes, ticket waves) returned clean for that exact data So `meta.valid` was **false while `errors` was empty** — vee-validate [#4630](https://github.com/logaretm/vee-validate/issues/4630) reproduced. The organiser had no way to discover that, and no way to proceed. ## Why `meta.valid` is the wrong thing to gate on - **Disabled fields count against validity.** The docs state *"disabled fields are not necessarily valid... they affect the Form meta just like other fields"*; `disabled` was removed from `<Field>` because consistent behaviour was unachievable. Most of our dialogs disable inputs while submitting. - **It goes stale** — #4630: false with no errors after a form is re-shown, and it never recovers. - **It is false before the form has ever validated**, which is why the docs suggest `valid && dirty` rather than `valid`. A flag unreliable in both directions should not be the only thing between a user and their action — least of all as a *disabled button*, which offers no affordance for finding out why. ## The other ten sites Every one disables a button on `!isFormValid`, and **none** uses `handleSubmit`'s invalid callback (only `CreateEventDialog` does, since #177): | file | binding | |---|---| | `wallet/ReceiveDialog.vue` | `!isFormValid \|\| isCreating` | | `chatelet/BookRoomDialog.vue` | `!isFormValid \|\| flow.isRequesting.value` | | `market/CreateProductDialog.vue` | `isCreating \|\| !isFormValid` | | `market/CreateStoreDialog.vue` | `isCreating \|\| !isFormValid` | | `market/MarketSettings.vue` | `isSaving \|\| !isFormValid` | | `base/ProfileSettings.vue` | `isUpdating \|\| !isFormValid` | | `expenses/AddExpense.vue` | `isSubmitting \|\| !isFormValid` | | `nostr-feed/NoteComposer.vue` | `isPublishing \|\| !isFormValid` | | `nostr-feed/RideshareComposer.vue` | `isPublishing \|\| !isFormValid` | | `accounting-app/views/AddIncome.vue` | `isSubmitting \|\| !isFormValid` | `ReceiveDialog` and `BookRoomDialog` look like the ones to do first — a user unable to create an invoice, or a guest unable to book a room, with no error and no recourse. ## The pattern from #177 1. Disable only while a submit is genuinely in flight. 2. Pass `handleSubmit` an **invalid callback** that names the offending fields and surfaces them — ours toasts `"Check Tickets, Start date, Title"`, opens whichever collapsible holds one, and scrolls the first into view. 3. Validate anything outside the Zod schema inside the submit handler, revealing its section rather than returning silently. Worth also checking each form for a schema field with **no `FormField` in the template** — `CreateEventDialog` has one (`notification_subject`), so an error there could never have been displayed. Harmless while it defaults to `''`, but the same latent shape. ## Not urgent Nothing is broken today beyond what #177 already fixed; this is a class of latent dead-ends. Filing so it is tracked rather than rediscovered one blocked user at a time.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/webapp#178
No description provided.