Forms
@xngui/components/form@xngui/components/formSignal Forms shell — x-form, x-form-field, validators, and control composition.
Overview
- 1Schema
form(model, validators) - 2Shell
x-form + x-form-field - 3Control
x-input [formField]
Every value control implements Angular's FormValueControl (or checkbox/slider variants) and works standalone with [(value)] — [formField] is optional.
Signal Forms schema
Define the model as a signal, then pass a schema callback to form(). Import Angular validators and @xngui extensions from @xngui/components/form.
schema.tsimport { form } from '@angular/forms/signals';import { email, match, required } from '@xngui/components/form';interface ContactModel { name: string; email: string; confirmEmail: string;}const model = signal<ContactModel>({ name: '', email: '', confirmEmail: '' });const contactForm = form(model, (path) => { required(path.name, { message: 'Name is required' }); required(path.email, { message: 'Email is required' }); email(path.email, { message: 'Enter a valid email' }); match(path.confirmEmail, path.email, { message: 'Emails must match' });});x-form composition
Bind [formField] on the control inside x-form-field — not on the field wrapper. x-form cascades labelMode, look, size, density, and validateOn to descendants.
form.html<x-form [form]="contactForm" validateOn="touched"> <x-form-field label="Name"> <x-input [formField]="contactForm.name" /> </x-form-field> <x-form-field label="Email" hint="We never share your email."> <x-input [formField]="contactForm.email" /> </x-form-field> <x-form-field label="Confirm email"> <x-input [formField]="contactForm.confirmEmail" /> </x-form-field> <x-button type="submit" variant="primary">Submit</x-button></x-form>| Property | Values | Role |
|---|---|---|
labelMode | static · float · over | Label layout for nested fields |
look | outline · fill · flat · none | Control chrome |
size | xs – xl | Control scale tier |
density | compact · comfortable · standard | Spacing offsets |
validateOn | dirty · touched · submit · … | When errors appear |
Label modes
labelMode cascades from x-form to x-form-field. Use static for always-visible labels above the control; float animates the label into the field when focused or filled.
Validators
| Validator | Import from | Notes |
|---|---|---|
required | @angular/forms/signals | Non-empty value |
email | @angular/forms/signals | RFC-style email |
minLength / maxLength | @angular/forms/signals | String length bounds |
min / max | @angular/forms/signals | Numeric bounds |
pattern | @angular/forms/signals | RegExp match |
phone | @xngui/components/form | Phone-like string (7+ digits) |
url | @xngui/components/form | Parseable URL |
match | @xngui/components/form | Equals another field |
oneOf | @xngui/components/form | Value in allowed set |
Legacy import @xngui/components/validation re-exports the same helpers. See the form demo for a full profile form and the form builder for visual composition.
Validation i18n
x-form maps Signal Forms ValidationError objects to translation keys via validationErrorKey, validationErrorParams, and validationErrorFallback from @xngui/components/form-field. Register validation.* keys in provideX translations.
validators.tsimport { provideX } from '@xngui/core/config';import { mergeXTranslations } from '@xngui/core/i18n';export const appConfig = { providers: [ provideX({ translations: mergeXTranslations({ en: { 'validation.required': 'This field is required', 'validation.email': 'Enter a valid email address', }, }), }), ],};// x-form resolves validationErrorKey(error) → xTranslate pipe// validationErrorParams(error) supplies {{min}}, {{max}}, …Form controls
Single-line text
Masked password
Numeric with steppers
Multi-line text
Boolean (inline label)
Toggle boolean
Custom menu select
Filterable select
Date picker
Full list in Components under the Forms group. Checkbox and switch typically use inline labels — not nested in x-form-field.
Validation timing
Set validateOn on x-form (or override per x-form-field) to control when errors appear: dirty, touched, dirty-or-touched, submit, or always. Global default comes from provideX({ config: { validateOn: 'touched' } }).
x-form can show an error summary on submit via errorSummary="submit". Individual fields read validation state from the bound [formField] and reveal errors through x-form-field.