Repository navigation
fix(validate): keep model values when fields use validate-on - #103
Merged
Merged
Conversation
With validate-on, the form binds per-field input/focusout listeners
while it initialises, before the field's own `model` listener. On each
keystroke checkValidity() notified the context synchronously, so
`model` re-rendered its stale value ('') over the typed text, then read
that '' back. A validated login posted empty credentials.
Re-append the field listeners in a microtask, once the subtree is
processed, so they run after `model`. Long-standing: reproduces with
Core and Elements v1.20.2 and v1.21.1.
Re-appending every field listener changed v1.21.1 behavior for fields without `model`: on:input read a stale $form, on:focusout saw the field untouched, and aria-invalid lagged while typing. Only `model` fields are re-ordered now; other fields keep their v1.21.1 order. `model` on select/checkbox/radio syncs on "change", so validate's value listener for those fields uses "change" too. Otherwise the "input" notify reverted the user's pick before `model` read it. Tests use the real `|` rule separator and pin the non-model order.
ErickXavier
added a commit
that referenced
this pull request
Oct 15, 2026
#103 moved the validate-on value listener of select/checkbox/radio fields with `model` from "input" to "change". A script-dispatched "input" no longer validated those fields, unlike v1.21.1. - listen on "input" again, in the v1.21.1 position; no "change" listener, so a real pick (input then change) validates once and an explicit validate-on keeps its v1.21.1 meaning - before validating, write the field value to its `model` the way core's `model` does on "change", so the validation notify no longer re-renders the stale value over the pick - skip that write for an unchecked radio and for an if-gated `model`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a form uses
validate-on, either on a field or on the form, a field withmodelloses the user's value. The documented validated login posts{"email":"","password":""}. Text inputs, textareas, checkboxes, selects and radios withmodelare all affected.Root cause
The problem is in
bindFieldEvents()insrc/validate/element.js.validate-on, the form'svalidateinit attaches value andfocusoutlisteners directly to each field.modelattaches its own listener on the same field.checkValidity()then callsctx.$set("$form", …)and_notifyFormSubtree()synchronously.modelre-render the stale context value over the user's input.model's own listener then reads the stale value back.modelsyncs onchange, but validate listened oninput, which fires earlier.The legacy path (no
validate-on) delegates its listeners on the form and is not affected.This is not a regression. It reproduces with Core and Elements v1.20.2 and v1.21.1, with current main, and in the cross combinations.
Fix
The fix only changes fields that carry
model. Withvalidate-on, those fields never kept their value on v1.21.1:modelfield's value listener (the validation trigger, or the silent dirty/re-validate listener) uses the same event as Core'smodel:changefor select, checkbox and radio, andinputotherwise.model's listener. A dispose guard skips the re-append when the form is gone before the microtask runs.model. Their listeners, events and order are exactly as on v1.21.1. That coverson:inputreading$form,on:focusoutreading touched state and errors, andaria-invalidwhile typing in a blur field.Known trade-offs, which apply only to
modelfields:modelfield's ownon:inputhandler reads the previous$formstate. Thearia-invalidtiming set by field-levelvalidatefollows the form's triggers.modelthat is attached after the one-shot microtask still lands after validate. One example is a field that anifadds later. This case was not verified in a browser.Tests
Rule strings now use the real
|separator, so the rules are actually exercised.Jest,
__tests__/validation.test.jstests 36 to 48:modelfields:blurandinput;validate-on;model, pinned to their v1.21.1 behavior:on:inputreads the current keystroke's state;on:focusoutreadstrue:Required;aria-invalidis set while typing in a blur field;disposeTreebefore the microtask runs.model.e2e,
e2e/examples/validation-model.htmlande2e/tests/validation-model.spec.ts: these use real keyboard and mouse input in Chromium, Firefox and WebKit.modeltests fail and the three tests for fields withoutmodelpass. So the pins match v1.21.1.Verification
npx jest: 532 passed, 5 skipped, against Core mainsrc/and against Core v1.21.1src/.cd e2e && npx playwright teston port 3953: 456 passed, against the Core main build and against the Core v1.21.1 build.modelis identical to Core v1.21.1 + Elements v1.21.1: R01, R02, R03, R05, R11, R12 and R14. R04, R06 and R16 are also identical.modelfield that now keeps its value, plus the error text that follows from that value. The affected scenarios are B01, B02, R07, R08, R09, R10, R13 and R15. In R08, only the form withmodeldiffers.