This demo shows how native HTML5 constraint validation (min, max,
pattern, type, required) works in sync with Fore's update cycle.
The browser's ValidityState is read (side-effect-free) at the end of
fx-model.revalidate() and propagated via ModelItem.nativeValid
into AbstractControl.handleValid().
No <form> wrapper is needed.
widget.validity.valid works on individual inputs independent of a form element.
Fore's fx-submission handles submission — a native form would only conflict.
checkValidity() fires events:
The integration reads widget.validity.valid (a live property) rather than calling
widget.checkValidity(). Calling checkValidity() dispatches an
invalid event on the input that would clash with Fore's own invalid
event on fx-control.
:invalid flash on page load:
CSS :valid and :invalid apply the moment the page renders — before
the user touches anything. The solution is :user-valid / :user-invalid,
which wait for interaction. For browsers that don't support them yet, the
:valid:not(:placeholder-shown) / :invalid:not(:placeholder-shown)
fallback produces the same behaviour. Both patterns are integrated in fore.css.
required
Both fx-bind required="true()" (XForms) and native
required on the input are set. Either alone would mark
the field invalid.
type="number" min="0" max="120"
Native range validation via validity.rangeUnderflow /
validity.rangeOverflow. No XPath constraint needed.
type="email"
Native email format check via validity.typeMismatch.
Combined with XForms required.
pattern="[0-9][0-9][0-9][0-9][0-9]" (5 digits)
Native pattern check via validity.patternMismatch.
Empty input is valid here (not required) — only non-empty invalid patterns fail.
minlength="3" maxlength="20"
Length validation via validity.tooShort /
validity.tooLong. Browsers block typing beyond
maxlength, so tooLong is only reachable by
programmatic value assignment. Empty input is valid (not required).
type="number" min="0.01" step="0.01"
Validates positive currency amounts. validity.stepMismatch
catches values that don't align to the step (e.g. three decimal places).
src/fx-model.js revalidate() — after
the XPath constraint/required loop, before return valid.
ModelItem.nativeValid (new boolean MIP,
default true) is read by AbstractControl.handleValid() and
ANDed into the overall validity check.
widget.validity.valid (live
ValidityState property) — no checkValidity() calls, no
invalid events fired by the validation pass.
fx-control (slotted into its shadow). ValidityState is
always accessible directly on the input element regardless of slot projection.
fx-bind type="xs:integer" does
not set type="number" on the widget. Native attributes must be
written directly on <input class="widget">.
See // todo: handleType() in abstract-control.js.
minlength / maxlength:
validity.tooShort fires only when the field is non-empty and shorter
than minlength. An empty non-required field is always valid.
validity.tooLong is rarely reached through typing (browsers block input at
maxlength) but is caught correctly if the value is set programmatically.
:in-range / :out-of-range:
CSS pseudo-classes that apply to number/date inputs with min/max
constraints. Unlike :user-invalid, they have no :user- prefix —
an empty optional number field is considered :in-range on page load. Combine
with :user-invalid to avoid premature styling. These are purely visual
enhancements; Fore's [invalid] attribute and fx-alert visibility
are already handled by the nativeValid integration.