Validation

Client-side validation

validate: true mirrors your ActiveModel validators into shipped Stimulus controllers — the no-phlex-reactive fallback.

Turning it on#

validate: true introspects the model's validators and emits data-validations--* bindings per field, plus a form-level submit coordinator. The form gets novalidate — the shipped Stimulus controllers own error display, so you never see the inconsistent native browser bubbles.

Form(model: @partner, validate: true) do |f|
  f.field :title
  f.field :note
  f.submit :primary
end

Register the controllers once (see Installation):

app/javascript/controllers/index.js
import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
lazyLoadControllersFrom("phlex_forms/controllers", application)

Per-field overrides#

Form(model: @partner, validate: true) do |f|
  f.field :title                                      # every validator on :title
  f.field :slug, validate: false                      # opt this field out
  f.field :note, validate: { length: { maximum: 30 } } # explicit inline rules
end

What's supported#

Presence, length (with a live character counter), format, numericality, inclusion, exclusion, confirmation, and acceptance.

Validators with :if, :unless, or :on are skipped — they need server context — and uniqueness can't be checked client-side. The server stays authoritative either way; this layer is UX, not enforcement.

If those gaps matter, live validation closes all of them by running the real validators server-side.

Messages & i18n#

Validation messages ship for en, fr, and af; override any string client-side via window.PhlexForms.messages. The engine prepends the gem's locale files to I18n.load_path, so your app's files win.