UNPKG

@mmstack/form-validation

Version:

Provides a type-safe, composable, and localizable validation system designed specifically for use with [@mmstack/form-core](https://www.npmjs.com/package/@mmstack/form-core). It enables defining validation rules clearly within your TypeScript code and int

912 lines (876 loc) 32.2 kB
import { InjectionToken, LOCALE_ID, inject } from '@angular/core'; import { formatDate } from '@angular/common'; const INTERNAL_ERROR_MERGE_DELIM = '::INTERNAL_MMSTACK_MERGE_DELIM::'; function defaultMergeMessage(errors) { const first = errors.at(0); if (!first) return { error: '', tooltip: '', }; if (errors.length === 1) { if (first.length > 60) { return { error: `${first.slice(0, 60)}...`, tooltip: first, }; } return { error: first, tooltip: '', }; } if (first.length > 40) { return { error: `${first.slice(0, 40)}..., +${errors.length - 1} issues`, tooltip: errors.join('\n'), }; } return { error: `${first}, +${errors.length - 1} issues`, tooltip: errors.join('\n'), }; } function toTooltipFn(merge) { return (errors) => { const result = merge(errors); if (typeof result === 'string') { return { error: result, tooltip: '', merged: errors.join(INTERNAL_ERROR_MERGE_DELIM), }; } return result; }; } function mergeValidators(...validators) { if (!validators.length) return () => []; return (value) => validators.map((val) => val(value)).filter(Boolean); } function createMergeValidators(merge) { const mergeFn = merge ? toTooltipFn(merge) : defaultMergeMessage; return (validators) => { const validate = mergeValidators(...validators); const fn = ((value) => validate(value).join(INTERNAL_ERROR_MERGE_DELIM)); fn.resolve = (mergedError) => { return mergeFn(mergedError.split(INTERNAL_ERROR_MERGE_DELIM)); }; return fn; }; } function defaultMaxLengthMessageFactory$1(max, elementsLabel) { return `Max ${max} ${elementsLabel}`; } function createMaxLengthValidator$1(createMsg) { return (max, elementsLabel = 'items') => { const msg = createMsg(max, elementsLabel); return (value) => { const length = value?.length ?? 0; if (length > max) return msg; return ''; }; }; } function defaultMinLengthMessageFactory$1(min, elementsLabel) { return `Min ${min} ${elementsLabel}`; } function createMinLengthValidator$1(createMsg) { return (min, elementsLabel = 'items') => { const msg = createMsg(min, elementsLabel); return (value) => { const length = value?.length ?? 0; if (length < min) return msg; return ''; }; }; } const DEFAULT_MESSAGES$4 = { minLength: defaultMinLengthMessageFactory$1, maxLength: defaultMaxLengthMessageFactory$1, }; function createArrayValidators(factories, merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES$4, ...factories }; const base = { minLength: createMinLengthValidator$1(t.minLength), maxLength: createMaxLengthValidator$1(t.maxLength), }; return { ...base, all: (opt) => { const validators = []; if (opt.minLength !== undefined) validators.push(base.minLength(opt.minLength, opt.elementsLabel)); if (opt.maxLength !== undefined) validators.push(base.maxLength(opt.maxLength, opt.elementsLabel)); return merger(validators); }, }; } function defaultMustBeTreFactory() { return `Must be true`; } function createMustBeTrueValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value !== true) return msg; return ''; }; }; } const DEFAULT_MESSAGES$3 = { mustBeTrue: defaultMustBeTreFactory, }; function createBooleanValidators(factories) { const t = { ...DEFAULT_MESSAGES$3, ...factories }; return { mustBeTrue: createMustBeTrueValidator(t.mustBeTrue), }; } function defaultMustBeMessageFactory(valueLabel) { return `Must be ${valueLabel}`; } function createMustBeValidator(createMessage) { return (value, valueLabel = `${value}`, matcher = Object.is) => { const msg = createMessage(valueLabel); return (currentValue) => { if (!matcher(value, currentValue)) return msg; return ''; }; }; } function defaultMustBeEmptyMessageFactory() { return defaultMustBeMessageFactory('empty'); } function createMustBeEmptyValidator(createMessage) { return () => createMustBeValidator(createMessage)(null); } function defaultNotMessageFactory(valueLabel) { return `Cannot be ${valueLabel}`; } function createNotValidator(createMessage) { return (value, valueLabel = `${value}`, matcher = Object.is) => { const msg = createMessage(valueLabel); return (currentValue) => { if (matcher(value, currentValue)) return msg; return ''; }; }; } function defaultNotOneOfMessageFactory(values) { return `Cannot be one of: ${values}`; } function defaultToLabel$1(value) { return `${value}`; } function createNotOneOfValidator(createMessage) { return (values, toLabel = defaultToLabel$1, identity = defaultToLabel$1, delimiter = ', ') => { const valuesLabel = values.map(toLabel).join(delimiter); const msg = createMessage(valuesLabel); const map = new Map(values.map((v) => [identity(v), v])); return (currentValue) => { if (map.has(identity(currentValue))) return msg; return ''; }; }; } function defaultOneOfMessageFactory(values) { return `Must be one of: ${values}`; } function defaultToLabel(value) { return `${value}`; } function createOneOfValidator(createMessage) { return (values, toLabel = defaultToLabel, identity = defaultToLabel, delimiter = ', ') => { const valuesLabel = values.map(toLabel).join(delimiter); const msg = createMessage(valuesLabel); const map = new Map(values.map((v) => [identity(v), v])); return (currentValue) => { if (!map.has(identity(currentValue))) return msg; return ''; }; }; } function defaultRequiredMessageFactory(label = 'Field') { return `${label} is required`; } function requiredNumber(value) { return value !== null && value !== undefined; } function createRequiredValidator(createMsg) { return (label = 'Field') => { const msg = createMsg(label); return (value) => { if (typeof value === 'number' && !requiredNumber(value)) return msg; if (!value) return msg; return ''; }; }; } const DEFAULT_MESSAGES$2 = { required: defaultRequiredMessageFactory, mustBe: defaultMustBeMessageFactory, mustBeNull: defaultMustBeEmptyMessageFactory, not: defaultNotMessageFactory, oneOf: defaultOneOfMessageFactory, notOneOf: defaultNotOneOfMessageFactory, }; /** * Represents the consolidated object containing all available validator generators, * configured with appropriate localization and date handling for the specified `TDate` type. * * Obtain this object using `injectValidators()` within an Angular injection context. * Use the nested `.all()` methods (e.g., `validators.string.all({...})`) with their * corresponding options types (e.g., `StringValidatorOptions`) to create combined * validator functions for your form controls. * * Individual validators (e.g., `validators.general.required()`) are also available. * * @template TDate The type used for date values (e.g., Date, Luxon DateTime). Defaults to `Date`. */ function createGeneralValidators(factories) { const t = { ...DEFAULT_MESSAGES$2, ...factories }; const mustBeNull = createMustBeEmptyValidator(t.mustBeNull); return { required: createRequiredValidator(t.required), mustBe: createMustBeValidator(t.mustBe), mustBeNull: createMustBeEmptyValidator(t.mustBeNull), not: createNotValidator(t.not), oneOf: (values, toLabel, identity, delimiter) => { if (!values.length) return mustBeNull(); return createOneOfValidator(t.oneOf)(values, toLabel, identity, delimiter); }, notOneOf: createNotOneOfValidator(t.notOneOf), }; } function defaultIsDateMessageFactory() { return `Must be a valid date`; } function createIsDateValidator(createMsg, toDate) { const msg = createMsg(); return () => { return (value) => { if (value === null) return ''; const date = toDate(value); if (isNaN(date.getTime())) return msg; return ''; }; }; } function defaultMaxDateMessageFactory(dateLabel) { return `Must be before ${dateLabel}`; } function createMaxDateValidator(createMsg, toDate, formatDate, locale) { return (date) => { const d = toDate(date); const matchTime = d.getTime(); const msg = createMsg(formatDate(d, locale)); return (value) => { if (value === null) return ''; if (toDate(value).getTime() > matchTime) return msg; return ''; }; }; } function defaultMinDateMessageFactory(dateLabel) { return `Must be after ${dateLabel}`; } function createMinDateValidator(createMsg, toDate, formatDate, locale) { return (date) => { const d = toDate(date); const matchTime = d.getTime(); const msg = createMsg(formatDate(d, locale)); return (value) => { if (value === null) return ''; if (toDate(value).getTime() < matchTime) return msg; return ''; }; }; } function isLuxonLike(date) { if (!date) return false; return ('toJSDate' in date && 'toUnixInteger' in date && typeof date.toJSDate === 'function' && typeof date.toUnixInteger === 'function'); } function isMomentLike(date) { if (!date) return false; return ('toDate' in date && 'unix' in date && typeof date.toDate === 'function' && typeof date.unix === 'function'); } function defaultToDate(date) { if (date instanceof Date) return date; if (typeof date === 'string' || typeof date === 'number') return new Date(date); if (!date) return new Date(); if (typeof date !== 'object') throw new Error('Date is not number, string, null, undefined or object'); if (isMomentLike(date)) return date.toDate(); if (isLuxonLike(date)) return date.toJSDate(); return new Date(); } function defaultFormatDate(date, locale, format = 'mediumDate') { return formatDate(date, format, locale); } const DEFAULT_DATE_MESSAGES = { min: defaultMinDateMessageFactory, max: defaultMaxDateMessageFactory, isDate: defaultIsDateMessageFactory, }; function createDateValidators(factories, toDate = (defaultToDate), formatDate = defaultFormatDate, locale = 'en-US', generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_DATE_MESSAGES, ...factories }; const base = { min: createMinDateValidator(t.min, toDate, formatDate, locale), max: createMaxDateValidator(t.max, toDate, formatDate, locale), isDate: createIsDateValidator(t.isDate, toDate), }; const toLabel = (d) => d ? formatDate(toDate(d), locale) : 'null'; return { ...base, all: (opt) => { const validators = []; if (opt.required) validators.push(generalValidators.required(opt.messageOptions?.label)); validators.push(base.isDate()); if (opt.mustBe !== undefined) { if (opt.mustBe === null) validators.push(generalValidators.mustBeNull()); else { const d = toDate(opt.mustBe); const formatted = formatDate(d, locale); validators.push(generalValidators.mustBe(d, formatted, (a, b) => { if (!a && !b) return true; if (!a || !b) return false; return toDate(a).getTime() === toDate(b).getTime(); })); } } if (opt.not !== undefined) { if (opt.not === null) validators.push(generalValidators.not(null)); else { const d = toDate(opt.not); const formatted = formatDate(d, locale); validators.push(generalValidators.not(d, formatted, (a, b) => { if (!a && !b) return true; if (!a || !b) return false; return toDate(a).getTime() === toDate(b).getTime(); })); } } if (opt.min !== undefined) validators.push(base.min(opt.min)); if (opt.max !== undefined) validators.push(base.max(opt.max)); if (opt.oneOf) { const dates = opt.oneOf.map((d) => (d ? toDate(d) : null)); validators.push(generalValidators.oneOf(dates, toLabel, (a) => a === null || a === undefined ? 'null' : toDate(a).getTime().toString())); } if (opt.notOneOf) { const dates = opt.notOneOf.map((d) => (d ? toDate(d) : null)); validators.push(generalValidators.notOneOf(dates, toLabel, (a) => a === null || a === undefined ? 'null' : toDate(a).getTime().toString())); } return merger(validators); }, util: { toDate, formatDate, }, }; } function createDateRangeValidators(factories, toDate = (defaultToDate), formatDate = defaultFormatDate, locale = 'en-US', generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_DATE_MESSAGES, ...factories }; const base = { min: createMinDateValidator(t.min, toDate, formatDate, locale), max: createMaxDateValidator(t.max, toDate, formatDate, locale), isDate: createIsDateValidator(t.isDate, toDate), }; return { ...base, all: (opt) => { const validators = []; if (opt.required) { const val = generalValidators.required(opt.messageOptions?.label); validators.push((value) => val(value.start) || val(value.end)); } const isDateValidator = base.isDate(); validators.push((value) => isDateValidator(value.start) || isDateValidator(value.end)); if (opt.min !== undefined) { const minVal = base.min(opt.min); validators.push((value) => minVal(value.start)); } if (opt.max !== undefined) { const maxVal = base.max(opt.max); validators.push((value) => maxVal(value.end)); } validators.push((value) => { if (value.start === null || value.end === null) return ''; const minVal = base.min(value.start); const maxVal = base.max(value.end); return minVal(value.end) || maxVal(value.start); }); return merger(validators); }, util: { toDate, formatDate, }, }; } function defaultIntegerMessageFactory() { return `Must be an integer`; } function createIntegerValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (!Number.isInteger(value)) return msg; return ''; }; }; } function defaultIsNumberMessageFactory() { return `Must be a number`; } function createIsNumberValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (typeof value !== 'number' || isNaN(value)) return msg; return ''; }; }; } function defaultMaxMessageFactory(max) { return `Must be at most ${max}`; } function createMaxValidator(createMsg) { return (max) => { const msg = createMsg(max); return (value) => { if (value === null) return ''; if (value > max) return msg; return ''; }; }; } function defaultMinMessageFactory(min) { return `Must be at least ${min}`; } function createMinValidator(createMsg) { return (min) => { const msg = createMsg(min); return (value) => { if (value === null) return ''; if (value < min) return msg; return ''; }; }; } function defaultMultipleOfMessageFactory(multipleOf) { return `Must be a multiple of ${multipleOf}`; } function createMultipleOfValidator(createMsg) { return (multipleOf) => { const msg = createMsg(multipleOf); return (value) => { if (value === null) return ''; if (value % multipleOf !== 0) return msg; return ''; }; }; } const DEFAULT_MESSAGES$1 = { min: defaultMinMessageFactory, max: defaultMaxMessageFactory, isNumber: defaultIsNumberMessageFactory, multipleOf: defaultMultipleOfMessageFactory, integer: defaultIntegerMessageFactory, }; function createNumberValidators(factories, generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES$1, ...factories }; const base = { min: createMinValidator(t.min), max: createMaxValidator(t.max), isNumber: createIsNumberValidator(t.isNumber), multipleOf: createMultipleOfValidator(t.multipleOf), integer: createIntegerValidator(t.integer), }; return { ...base, all: (opt) => { const validators = []; if (opt.required) validators.push(generalValidators.required(opt.messageOptions?.label)); validators.push(base.isNumber()); if (opt.mustBe !== undefined) validators.push(generalValidators.mustBe(opt.mustBe)); if (opt.not !== undefined) validators.push(generalValidators.not(opt.not)); if (opt.integer) validators.push(base.integer()); if (opt.min !== undefined) validators.push(base.min(opt.min)); if (opt.max !== undefined) validators.push(base.max(opt.max)); if (opt.multipleOf !== undefined) validators.push(base.multipleOf(opt.multipleOf)); if (opt.oneOf) validators.push(generalValidators.oneOf(opt.oneOf)); if (opt.notOneOf) validators.push(generalValidators.notOneOf(opt.notOneOf)); return merger(validators); }, }; } const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; function defaultEmailMessageFactory() { return `Must be a valid email`; } function createEmailValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (!EMAIL_REGEXP.test(value)) return msg; return ''; }; }; } function defaultIsStringMessageFactory() { return `Must be a string`; } function createIsStringValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (typeof value !== 'string') return msg; return ''; }; }; } function defaultMaxLengthMessageFactory(max) { return defaultMaxLengthMessageFactory$1(max, 'characters'); } function createMaxLengthValidator(createMsg) { return createMaxLengthValidator$1((max) => createMsg(max)); } function defaultMinLengthMessageFactory(min) { return defaultMinLengthMessageFactory$1(min, 'characters'); } function createMinLengthValidator(createMsg) { return createMinLengthValidator$1((min) => createMsg(min)); } function defaultPatternMessageFactory(patternLabel) { return `Must match pattern ${patternLabel}`; } function createPatternValidator(createMsg) { return (pattern) => { const regex = new RegExp(pattern); const msg = createMsg(regex.source); return (value) => { if (value === null) return ''; if (!regex.test(value)) return msg; return ''; }; }; } function defaultTrimmedMessageFactory() { return `Cannot contain leading or trailing whitespace`; } function createTrimmedValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (value.trim().length !== value.length) return msg; return ''; }; }; } const URI_REGEXP = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/; function defaultURIMessageFactory() { return `Must be a valid URI`; } function createURIValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (!URI_REGEXP.test(value)) return msg; return ''; }; }; } const DEFAULT_MESSAGES = { email: defaultEmailMessageFactory, uri: defaultURIMessageFactory, pattern: defaultPatternMessageFactory, trimmed: defaultTrimmedMessageFactory, isString: defaultIsStringMessageFactory, minLength: defaultMinLengthMessageFactory, maxLength: defaultMaxLengthMessageFactory, }; function createStringValidators(factories, generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES, ...factories }; const base = { email: createEmailValidator(t.email), uri: createURIValidator(t.uri), pattern: createPatternValidator(t.pattern), trimmed: createTrimmedValidator(t.trimmed), isString: createIsStringValidator(t.isString), minLength: createMinLengthValidator(t.minLength), maxLength: createMaxLengthValidator(t.maxLength), }; return { ...base, all: (opt) => { const validators = []; if (opt.required) validators.push(generalValidators.required(opt?.messageOptions?.label)); validators.push(base.isString()); if (opt.mustBe !== undefined) validators.push(generalValidators.mustBe(opt.mustBe)); if (opt.not !== undefined) validators.push(generalValidators.not(opt.not)); if (opt.trimmed) validators.push(base.trimmed()); if (opt.minLength !== undefined) validators.push(base.minLength(opt.minLength)); if (opt.maxLength) validators.push(base.maxLength(opt.maxLength)); if (opt.pattern) { switch (opt.pattern) { case 'email': validators.push(base.email()); break; case 'uri': validators.push(base.uri()); break; default: validators.push(base.pattern(opt.pattern)); break; } } if (opt.oneOf) validators.push(generalValidators.oneOf(opt.oneOf)); if (opt.notOneOf) validators.push(generalValidators.notOneOf(opt.notOneOf)); return merger(validators); }, }; } const token = new InjectionToken('INTERNAL_MMSTACK_VALIDATORS'); let defaultValidators = null; function createDefaultValidators() { if (!defaultValidators) { defaultValidators = createValidators({}, defaultToDate, defaultFormatDate, 'en-US'); } return defaultValidators; } function createValidators(msg, toDate, formatDate, locale) { const general = createGeneralValidators(msg?.general); const merger = createMergeValidators(msg?.merge); return { general, string: createStringValidators(msg?.string, general, merger), number: createNumberValidators(msg?.number, general, merger), date: createDateValidators(msg?.date, toDate, formatDate, locale, general, merger), dateRange: createDateRangeValidators(msg?.date, toDate, formatDate, locale, general, merger), array: createArrayValidators(msg?.array, merger), boolean: createBooleanValidators(msg?.boolean), }; } /** * Provides the configuration for the @mmstack/form-validation system within Angular's DI. * * Include the returned provider in your application's root providers (e.g., in `app.config.ts`) * or relevant feature/route providers to enable localization of validation error messages * and configure custom date handling. * * It automatically uses Angular's `LOCALE_ID` to determine which message factories to apply. * * @template TDate - The type used for date values throughout your application * (e.g., `Date`, Luxon `DateTime`, Moment). Defaults to `Date`. This generic ensures * type safety when providing custom `toDate` and `formatDate` functions. * @param messageFactoryProvider - A function that receives the current `LOCALE_ID` string * (e.g., 'en-US', 'de-DE') and should return an object containing optional custom * message factory functions for various validator types (`general`, `string`, `number`, `date`, `array`, `boolean`). * Only provide factories for the messages you want to override for that specific locale; * defaults will be used for any unspecified factories. Return `undefined` or an empty * object to use default messages for the locale. * @param toDate - Optional function to convert input values (of type `TDate` or `string`) * into standard JavaScript `Date` objects. This is used internally by date validators * for comparisons. Defaults to a helper supporting `Date`, `string`, `number`, and * common date library objects (Luxon, Moment). * **Provide this function if your application uses a date type other than native `Date`**. * @param formatDate - Optional function to format a `Date` object into a string suitable * for display in date-related error messages (e.g., min/max date errors), respecting * the provided `locale`. Defaults to using Angular's `formatDate` with 'mediumDate' format. * @returns An Angular `Provider` configuration object to be added to your providers array. * * @example * // In app.config.ts * import { ApplicationConfig } from '@angular/core'; * import { provideValidatorConfig } from '@mmstack/form-validation'; * // If using a custom date type like Luxon's DateTime * // import { DateTime } from 'luxon'; * // import { defaultToDate } from '@mmstack/form-validation'; // Or your custom util path * * export const appConfig: ApplicationConfig = { * providers: [ * provideValidatorConfig((locale): Partial<MessageFactories> | void => { * console.log('Configuring validators for locale:', locale); * if (locale.startsWith('de')) { // Example for German * return { * general: { required: (label = 'Feld') => `${label} ist erforderlich.` }, * string: { minLength: (min) => `Mindestens ${min} Zeichen.` } * // Only provide overrides needed for this locale * }; * } * // Return undefined or {} to use default messages for other locales * }) * * // Example if using Luxon DateTime: * // provideValidatorConfig<DateTime>( * // (locale) => { ... }, // your message factories * // // Custom toDate function for Luxon * // (d) => (d instanceof DateTime ? d.toJSDate() : defaultToDate(d)), * // // Custom formatDate function for Luxon * // (d, l) => DateTime.fromJSDate(d).setLocale(l).toLocaleString(DateTime.DATE_MED) * // ) * ] * }; */ function provideValidatorConfig(factory, toDate = (defaultToDate), formatDate = defaultFormatDate) { return { provide: token, useFactory: (locale) => createValidators(factory(locale), toDate, formatDate, locale), deps: [LOCALE_ID], }; } /** * Injects the configured `Validators` object within an Angular injection context. * * This function is the standard way to access the validation functions * (e.g., `validators.string.all`, `validators.number.min`) inside your Angular * components, services, directives, or form adapter factories. * * It ensures that you receive the set of validators configured with the appropriate * localization (via `provideValidatorConfig` and `LOCALE_ID`) and custom date handling, * if provided. * * If `provideValidatorConfig` was not used in the current or parent injectors, * this function gracefully falls back to a default set of validators using * English messages and default date handling (`Date` objects). * * @template TDate - The type used for date values in your application and configuration * (e.g., `Date`, Luxon `DateTime`, Moment). This should match the type argument * used in `provideValidatorConfig` if custom date handling was provided. Defaults to `Date`. * @param injector - Optional. A specific Angular `Injector` instance to use for resolving * the validators. If omitted, it uses the current injection context via `inject()`. * @returns The configured `Validators<TDate>` object, ready to use for creating * validator functions (e.g., `validators.string.all({ required: true })`). * * @example * // In an Angular component or service: * import { Component, inject } from '@angular/core'; * import { injectValidators } from '@mmstack/form-validation'; * import { formControl } from '@mmstack/form-core'; * // Assuming form is configured for native Date objects (or default) * * @Component({...}) * export class UserProfileComponent { * private validators = injectValidators(); // Inject the validators * // Or, if configured for Luxon DateTime: * // private validators = injectValidators<DateTime>(); * * // Use the injected validators to define form controls * readonly emailControl = formControl('', { * label: () => 'Email Address', * validator: () => this.validators.string.all({ required: true, email: true }) * }); * * readonly birthDateControl = formControl<Date | null>(null, { * label: () => 'Date of Birth', * validator: () => this.validators.date.all({ required: false, max: new Date() }) * }); * * readonly numberOfPetsControl = formControl<number>(0, { * label: () => 'Number of Pets', * validator: () => this.validators.number.min(0, 'Pet count') // Using an individual validator * }); * } */ function injectValidators(injector) { const validators = (injector ? injector.get(token, null, { optional: true, }) : inject(token, { optional: true })); return validators ?? createDefaultValidators(); } /** * Generated bundle index. Do not edit. */ export { injectValidators, provideValidatorConfig }; //# sourceMappingURL=mmstack-form-validation.mjs.map