Configuration
form(target, options?) accepts a FormControllerOptions object. Every option has a default, so you only pass what you need.
import { form } from '@samline/forms'
const contactForm = form('contact-form', { attributes: { filled: 'is-filled', error: 'is-invalid' }, autoValidate: true, autoSubmit: { debounce: 500 }, clearErrorsOnSubmit: true, clearManualErrorsOnChange: true, validators: { email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ } }})Signature
Section titled “Signature”interface FormControllerOptions { attributes?: Partial<VisualAttributes> autoValidate?: boolean autoSubmit?: boolean | AutoSubmitOptions clearManualErrorsOnChange?: boolean clearErrorsOnSubmit?: boolean validators?: ValidationSchema formats?: FieldFormatConfigMap}Options reference
Section titled “Options reference”| Option | Type | Default | Behaviour |
|---|---|---|---|
attributes |
Partial<VisualAttributes> |
{ filled: 'css-filled', error: 'css-error' } |
Override the names of the visual attributes applied to fields. |
autoValidate |
boolean |
true |
Run validation on mount and on every change for fields that have rules. |
autoSubmit |
boolean | AutoSubmitOptions |
false |
Submit the form automatically on every change. Pass { debounce: ms } to delay. |
clearErrorsOnSubmit |
boolean |
true |
Clear all manual errors before submit validation runs. |
clearManualErrorsOnChange |
boolean |
true |
Clear the manual error of a field when it changes. Set false to keep manual errors until you call clearErrors. |
validators |
ValidationSchema |
{} |
Field → rules map. See ValidationSchema and the rule reference below. |
formats |
FieldFormatConfigMap |
{} |
Declarative @samline/formatter configuration. Each entry is applied during form() initialization using the same logic as format(). Requires the optional peer dependency. |
attributes
Section titled “attributes”By default the controller adds css-filled to a field when it has a value, and css-error when it has at least one error. Override the names with attributes:
form('profile-form', { attributes: { filled: 'is-filled', error: 'is-invalid' }})This affects which DOM attributes the controller toggles. You provide the CSS — see CSS styling for recipes.
autoValidate
Section titled “autoValidate”When true (default), the controller:
- Runs an initial validation pass on mount for every field that has rules.
- Validates each field on every
input/changeevent when the field has rules.
Set this to false if you want to validate manually (e.g. only on submit, or only when the user moves past a step):
form('wizard-form', { autoValidate: false }).validate(['step-1'])autoSubmit
Section titled “autoSubmit”When true, the controller schedules a native submit (form.requestSubmit()) on every change. Pass an AutoSubmitOptions object to add a debounce:
form('search-form', { autoSubmit: { debounce: 300 }})autoSubmit: true— submit immediately on every change.autoSubmit: { debounce: 300 }— submit 300ms after the last change.autoSubmit: false— disabled at mount; enable later withautoSubmit().
Disable at any time with disableAutoSubmit().
clearErrorsOnSubmit
Section titled “clearErrorsOnSubmit”When true (default), all manual errors created with setErrors are cleared before submit validation runs. Validation errors from the current rules are computed fresh. Set this to false if you want manual errors to survive submit attempts:
form('profile-form', { clearErrorsOnSubmit: false })This only affects manual errors. Validation errors always recompute on submit.
clearManualErrorsOnChange
Section titled “clearManualErrorsOnChange”When true (default), the manual error of a field is cleared the moment the field changes (including changes triggered through setValue). Validation then runs and decides the new error state.
Set this to false to keep manual errors visible until you explicitly clear them with clearErrors:
form('profile-form', { clearManualErrorsOnChange: false })
profileForm.setErrors({ email: ['This email is already in use.'] })// The error stays visible even after the user edits the field.Use this when manual errors come from server validation and you want them to persist across edits.
validators
Section titled “validators”A map of field names to rule sets. Each rule set is a FieldValidationRules object.
form('signup-form', { validators: { email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }, password: { required: true, minLength: { value: 8, message: 'Use at least 8 characters.' }, maxLength: 64 }, confirm: { validate: ({ value, values }) => value === values.password ? null : 'Passwords do not match.' } }})Supported rules
Section titled “Supported rules”| Rule | Type | Description |
|---|---|---|
required |
boolean | { value: boolean; message?: string } |
Field must have a non-empty value. |
minLength |
number | { value: number; message?: string } |
Minimum string length (for checkbox groups, the minimum number of selected items). |
maxLength |
number | { value: number; message?: string } |
Maximum string length (for checkbox groups, the maximum number of selected items). |
pattern |
RegExp | { value: RegExp; message?: string } |
String must match the regular expression. Skipped when the field is empty. |
validate |
FieldValidator | FieldValidator[] |
Custom validators. Return a string to push an error, or null / undefined / true to pass. Return false to push a generic "Validation failed." message. |
Every rule accepts either a plain value or a { value, message } object. Use the object form when you want a custom error message per rule.
Custom validators
Section titled “Custom validators”form('checkout-form', { validators: { card: { validate: ({ value, field }) => { if (typeof value !== 'string') return `${field} is required.` if (!/^\d{16}$/.test(value.replace(/\s+/g, ''))) { return 'Card number must be 16 digits.' } return null } } }})The custom validator receives { field, value, values } and runs after the built-in rules. See FieldValidationContext.
Defaults at a glance
Section titled “Defaults at a glance”const DEFAULT_ATTRIBUTES = { filled: 'css-filled', error: 'css-error'}
const DEFAULT_OPTIONS = { autoValidate: true, clearErrorsOnSubmit: true, clearManualErrorsOnChange: true}Everything else (autoSubmit, validators, formats, partial attributes) defaults to neutral / empty values.
formats
Section titled “formats”Declarative way to bind one or more @samline/formatter pipelines at construction time. Each entry is applied during form() initialization using the same logic as format().
import { form } from '@samline/forms'
const checkout = form('checkout', { formats: { phone: { type: 'phone', field: 'phone', options: { country: 'MX' } }, card: { type: 'creditCard', field: 'card' }, amount: { type: 'numeral', field: 'amount', options: { prefix: '$' } } }})The map key is just an identifier — the visible field name lives in FieldFormatConfig.field. See FieldFormatConfigMap for the full type, and the formatting recipe for an end-to-end example.