Dynamic Forms provides type-safe validation that maps directly to Angular signal forms validators. Start with simple shorthand validators and progress to advanced conditional validation as your needs grow.
Signal Forms Integration
Dynamic Forms validation maps directly to Angular's signal forms validators:
// Your configuration
{ key: 'email', type: 'input', value: '', required: true, email: true }
// Becomes
import { required, email } from '@angular/forms/signals';
required(fieldPath);
email(fieldPath);This tight integration means:
- Zero overhead - Direct signal forms API usage
- Familiar patterns - Same validators you know
- Full type safety - TypeScript inference throughout
Which Validation Approach Should I Use?
Choose based on your validation complexity:
Shorthand Validators
Use when: Simple, always-active validation
{
key: 'email',
type: 'input',
value: '',
required: true,
email: true,
minLength: 5,
}Benefits:
- Concise and readable
- Perfect for common validations
- Type-safe with full IntelliSense
Validators Array
Use when: Conditional validation or custom messages
{
key: 'discount',
type: 'input',
value: 0,
validators: [{
type: 'max',
value: 100,
when: {
type: 'fieldValue',
fieldPath: 'discountType',
operator: 'equals',
value: 'percentage',
},
}],
}Benefits:
- Conditional validation
- Custom error messages
- Dynamic validator values
Logic Array
Use when: Changing field behavior (hidden/required/disabled)
{
key: 'taxId',
type: 'input',
value: '',
logic: [{
type: 'required',
condition: {
type: 'fieldValue',
fieldPath: 'accountType',
operator: 'equals',
value: 'business',
},
}],
}Benefits:
- Dynamic field behavior
- Conditional required fields
- See Conditional Logic for details
Shorthand Validators
required
Mark a field as mandatory:
{
key: 'name',
type: 'input',
value: '',
required: true,
}Validate email format:
{
key: 'email',
type: 'input',
value: '',
required: true,
email: true,
}minLength / maxLength
Validate string length:
{
key: 'username',
type: 'input',
value: '',
minLength: 3,
maxLength: 20,
}min / max
Validate numeric range:
{
key: 'age',
type: 'input',
value: null,
min: 18,
max: 120,
props: { type: 'number' },
}pattern
Validate with regular expressions:
{
key: 'zipCode',
type: 'input',
value: '',
pattern: '^[0-9]{5}$', // 5-digit US ZIP code
}Combining Validators
Stack multiple validators on the same field:
{
key: 'password',
type: 'input',
value: '',
required: true,
minLength: 8,
maxLength: 128,
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)',
props: { type: 'password' },
}All validators must pass for the field to be valid.
Validation Messages
Message Resolution
Built-in validators map directly to Angular's required(), email(), minLength(), and related functions, which produce errors without any message text. For each error kind, the message is resolved in this order:
- Field-level
validationMessages[kind] - Form-level
defaultValidationMessages[kind] - The error's own
messageproperty (present on schema validation errors, for example)
If none of these produce a message, a console warning is logged and the error is not displayed. Configure a message for every validator you use.
Custom Messages
Configure messages per field:
{
key: 'email',
type: 'input',
value: '',
required: true,
email: true,
validationMessages: {
required: 'Email address is required',
email: 'Please enter a valid email address',
},
}Dynamic Messages
Use signals or observables for i18n:
{
key: 'email',
type: 'input',
value: '',
required: true,
validationMessages: {
required: this.transloco.selectTranslate('validation.required'),
email: this.transloco.selectTranslate('validation.email'),
},
}Quick Examples
User Registration
const config = {
fields: [
{
key: 'username',
type: 'input',
value: '',
required: true,
minLength: 3,
maxLength: 20,
pattern: '^[a-zA-Z0-9_]+$',
validationMessages: {
required: 'Username is required',
minLength: 'Username must be at least 3 characters',
maxLength: 'Username cannot exceed 20 characters',
pattern: 'Username can only contain letters, numbers, and underscores',
},
},
{
key: 'email',
type: 'input',
value: '',
required: true,
email: true,
validationMessages: {
required: 'Email is required',
email: 'Please enter a valid email address',
},
},
{
key: 'password',
type: 'input',
value: '',
required: true,
minLength: 8,
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)',
validationMessages: {
required: 'Password is required',
minLength: 'Password must be at least 8 characters',
pattern: 'Password must include uppercase, lowercase, and a number',
},
props: { type: 'password' },
},
],
} as const satisfies FormConfig;When Validation Runs
Validators run reactively: every value change re-evaluates the field's validation state immediately. There is no updateOn setting. What is gated is the display of errors:
- Error display - Error messages appear once a field is both invalid and touched
- Submit buttons - Disabled by default while the form is invalid (configurable via
options.submitButton.disableWhenInvalid)
Next Steps
- Validation Advanced - Conditional validation, dynamic values
- Validation Reference - Complete validator API
- Conditional Logic - Dynamic field behavior
- Examples - Real-world validation patterns