Forms
Custom form field components that work with all Angular form strategies: Signal Forms, Reactive Forms, and Template-driven Forms. Each component implements FormValueControl<T> or FormCheckboxControl for Signal Forms, and optional ControlValueAccessor adapters (NgOatValueCva, NgOatCheckboxCva) enable formControlName, [formControl], and [(ngModel)] binding.
Validation errors display only after the field is touched (blurred or interacted with).
| Component | Selector | Signal Forms | Reactive / ngModel |
|---|---|---|---|
| Input | <ng-oat-input> | FormValueControl<string> | NgOatValueCva |
| Textarea | <ng-oat-textarea> | FormValueControl<string> | NgOatValueCva |
| Select | <ng-oat-select> | FormValueControl<string> | NgOatValueCva |
| Checkbox | <ng-oat-checkbox> | FormCheckboxControl | NgOatCheckboxCva |
| Switch | <ng-oat-switch> | FormCheckboxControl | NgOatCheckboxCva |
| Radio Group | <ng-oat-radio-group> | FormValueControl<string> | NgOatValueCva |
| Form Error | <ng-oat-form-error> | [control] | [errors] + [show] |
Import & Usage
typescript
import { Component, signal } from '@angular/core';
import { FormField, FormRoot, form, schema, required, email } from '@angular/forms/signals';
import { NgOatInput, NgOatSelect, NgOatCheckbox, NgOatFormError } from '@letsprogram/ng-oat';
@Component({
selector: 'app-example',
imports: [FormField, FormRoot, NgOatInput, NgOatSelect, NgOatCheckbox, NgOatFormError],
template: `
<form [formRoot]="myForm" (ngSubmit)="onSubmit()">
<ng-oat-input label="Email" type="email" [formField]="myForm.email" />
<ng-oat-form-error [control]="myForm.email" />
<ng-oat-checkbox label="Terms" [formField]="myForm.terms" />
<button type="submit">Submit</button>
</form>
`,
})
export class ExampleComponent {
private model = signal({ email: '', terms: false });
myForm = form(this.model, schema($ => {
required($.email);
email($.email);
}));
onSubmit() { console.log(this.model()); }
}Complete Form
html
// Component class
model = signal({ name: '', email: '', bio: '', country: '', plan: 'free', terms: false, newsletter: false });
profileForm = form(this.model, schema($ => {
required($.name);
minLength($.name, 2);
required($.email);
email($.email);
required($.country);
}));
// Template
<form [formRoot]="profileForm" (ngSubmit)="onSubmit()">
<ng-oat-input label="Name" [formField]="profileForm.name" />
<ng-oat-form-error [control]="profileForm.name" />
<ng-oat-input label="Email" type="email" [formField]="profileForm.email" />
<ng-oat-form-error [control]="profileForm.email" />
<ng-oat-textarea label="Bio" [formField]="profileForm.bio" />
<ng-oat-select label="Country" [options]="countries" [formField]="profileForm.country" />
<ng-oat-form-error [control]="profileForm.country" />
<ng-oat-radio-group label="Plan" [options]="plans" [formField]="profileForm.plan" />
<ng-oat-checkbox label="Accept terms" [formField]="profileForm.terms" />
<ng-oat-switch label="Newsletter" [formField]="profileForm.newsletter" />
<button type="submit">Submit</button>
</form>Standalone (no form)
Each component also works standalone with two-way binding via [(value)] or [(checked)].
Input:
Checkbox: false
Switch: true
html
<ng-oat-input label="Name" [(value)]="name" />
<ng-oat-checkbox label="Remember me" [(checked)]="remember" />
<ng-oat-switch label="Dark mode" [(checked)]="darkMode" />