CustomValidator
@ng-forge/dynamic-forms
Custom validator function signature using Angular's public FieldContext API
Takes FieldContext (full Angular context) and optional params, returns validation error(s) or null. Provides access to field state, form hierarchy, and other fields for validation logic.
Use FieldContext public APIs to access:
- Current field value: ctx.
- Field state: value()ctx.state (errors, touched, dirty, etc.)
- Other field values: ctx.valueOf(path) where path is a FieldPath
- Other field states: ctx.stateOf(path)
- Other fields: ctx.fieldTreeOf(path)
- Current field tree: ctx.fieldTree
Return Types:
- Single error: { kind: 'errorKind' } for field-level validation
- Multiple errors: [{ kind: 'error1' }, { kind: 'error2' }] for cross-field validation
- No error: null when validation passes
Best Practice - Return Only Error Kind:
Validators should focus on validation logic, not presentation.
Return just the error kind and configure messages at field level for i18n support.
Signature
type CustomValidator<TValue = unknown> = (
ctx: FieldContext<TValue>,
params?: Record<string, unknown>,
) => ValidationError | ValidationError[] | null;Type Parameters
| Name | Constraint | Default | Description |
|---|---|---|---|
TValue | - | unknown | - |
Examples
const noSpaces: CustomValidator<string> = (ctx) => {
const value = ctx.value();
if (typeof value === 'string' && value.includes(' ')) {
return { kind: 'noSpaces' };
}
return null;
};
// Field configuration
{
key: 'username',
validators: [{ type: 'custom', functionName: 'noSpaces' }],
validationMessages: {
noSpaces: 'Spaces are not allowed'
}
}// Compare two fields using public API
const lessThan: CustomValidator<number> = (ctx, params) => {
const value = ctx.value();
const compareToPath = params?.field as string;
// Use valueOf() to access other field - public API!
const otherValue = ctx.valueOf(compareToPath as any);
if (otherValue !== undefined && value >= otherValue) {
return { kind: 'notLessThan' };
}
return null;
};const validateDateRange: CustomValidator = (ctx) => {
const errors: ValidationError[] = [];
const startDate = ctx.valueOf('startDate' as any);
const endDate = ctx.valueOf('endDate' as any);
if (!startDate) errors.push({ kind: 'startDateRequired' });
if (!endDate) errors.push({ kind: 'endDateRequired' });
if (startDate && endDate && startDate > endDate) {
errors.push({ kind: 'invalidDateRange' });
}
return errors.length > 0 ? errors : null;
};packages/dynamic-forms/src/lib/core/validation/validator-types.ts:91