UNPKG

@studiohyperdrive/ngx-forms

Version:
1 lines 106 kB
{"version":3,"file":"studiohyperdrive-ngx-forms.mjs","sources":["../../../../libs/angular/forms/src/lib/validators/utils/is-empty-input-value.util.ts","../../../../libs/angular/forms/src/lib/validators/utils/form-error.util.ts","../../../../libs/angular/forms/src/lib/validators/all-or-nothing-required/all-or-nothing-required.validator.ts","../../../../libs/angular/forms/src/lib/validators/at-least-one-required/at-least-one-required.validator.ts","../../../../libs/angular/forms/src/lib/validators/depended-required/depended-required.validator.ts","../../../../libs/angular/forms/src/lib/validators/decimals-after-comma/decimals-after-comma.validator.ts","../../../../libs/angular/forms/src/lib/validators/chronological-dates/chronological-dates.validator.ts","../../../../libs/angular/forms/src/lib/validators/email/extended-email.validator.ts","../../../../libs/angular/forms/src/lib/validators/has-no-future-date/has-no-future-date.validator.ts","../../../../libs/angular/forms/src/lib/validators/date-range/date-range.validator.ts","../../../../libs/angular/forms/src/lib/validators/max-word-count/word-count.validator.ts","../../../../libs/angular/forms/src/lib/validators/compare/compare.validator.ts","../../../../libs/angular/forms/src/lib/validators/validators.ts","../../../../libs/angular/forms/src/lib/abstracts/base-form/base-form.accessor.ts","../../../../libs/angular/forms/src/lib/utils/mark-all-as-dirty/mark-all-as-dirty.util.ts","../../../../libs/angular/forms/src/lib/utils/custom-update-value-and-validity/custom-update-value-and-validity.util.ts","../../../../libs/angular/forms/src/lib/utils/form-accessor/form-accessor.utils.ts","../../../../libs/angular/forms/src/lib/utils/has-errors/has-errors.util.ts","../../../../libs/angular/forms/src/lib/utils/touched-event-listener/touched-event-listener.ts","../../../../libs/angular/forms/src/lib/utils/accessor-providers/accessor-providers.util.ts","../../../../libs/angular/forms/src/lib/abstracts/custom-control-value-accessor/custom-control-value-accessor.ts","../../../../libs/angular/forms/src/lib/abstracts/form/form.accessor.ts","../../../../libs/angular/forms/src/lib/abstracts/data-form/data-form.accessor.ts","../../../../libs/angular/forms/src/lib/abstracts/form-accessor-container/form-accessor-container.ts","../../../../libs/angular/forms/src/lib/abstracts/error/error.component.abstract.ts","../../../../libs/angular/forms/src/lib/abstracts/save-on-exit/save-on-exit.component.abstract.ts","../../../../libs/angular/forms/src/lib/abstracts/save-on-exit/save-on-exit.service.abstract.ts","../../../../libs/angular/forms/src/lib/tokens/errors-config.token.ts","../../../../libs/angular/forms/src/lib/directives/errors/errors.directive.ts","../../../../libs/angular/forms/src/lib/guards/save-on-exit/save-on-exit.guard.ts","../../../../libs/angular/forms/src/public-api.ts","../../../../libs/angular/forms/src/studiohyperdrive-ngx-forms.ts"],"sourcesContent":["export const isEmptyInputValue = (value: any): boolean => {\n\t// we don't check for string here so it also works with arrays\n\treturn value == null || value.length === 0;\n};\n","import { AbstractControl } from '@angular/forms';\nimport clean from 'obj-clean';\n\n/**\n * Removes an error from a form control\n *\n * @param control - Form control to remove the error from.\n * @param error - Name of the error to remove from the control.\n */\nexport const clearFormError = (control: AbstractControl, error: string): void => {\n\t// Iben: Check if there are no errors existing on this control or if the the provided error does not exist, and early exit if needed\n\tconst errors = new Set(Object.keys(control.errors || {}));\n\n\tif (errors.size === 0 || !errors.has(error)) {\n\t\treturn;\n\t}\n\n\t// Iben: In case the provided error is the only error on the control, clear all errors and early exit\n\tif (errors.has(error) && errors.size === 1) {\n\t\tcontrol.setErrors(null);\n\n\t\treturn;\n\t}\n\n\t// Iben: In case there are more errors, remove only the provided error\n\tcontrol.setErrors(\n\t\tclean({\n\t\t\t...control.errors,\n\t\t\t[error]: undefined,\n\t\t})\n\t);\n};\n\n/**\n * Adds an error to a form control\n *\n * @param control - Form control to attach the error to.\n * @param error - Name of the error to attach to the control.\n * @param value - Value of the error being attached to the control\n */\nexport const setFormError = (control: AbstractControl, error: string, value: any = true): void => {\n\t// Iben: Early exit in case the control already has the error\n\tif (control.hasError(error)) {\n\t\treturn;\n\t}\n\n\t// Iben: Add the provided error\n\tcontrol.setErrors({\n\t\t...control.errors,\n\t\t[error]: value,\n\t});\n};\n","import { FormGroup } from '@angular/forms';\nimport clean from 'obj-clean';\n\nimport { clearFormError, setFormError } from '../utils';\n\nconst EMPTY_SET = new Set([undefined, null, '']);\n\n/**\n * FormGroup validator which checks if either all values or no values are filled in\n *\n * @param controls - An array of controls.\n * @param dependedControlKey - A control within the group which the other controls depend on.\n * @param matchFunction - Optional function the dependedControl should check\n */\nexport const allOrNothingRequiredValidator = (\n\tform: FormGroup\n): { allOrNothingRequiredError: string[] } | null => {\n\tconst keys = Object.keys(form.value);\n\n\t// Iben: If the group is completely empty we clear all required errors\n\tif (Object.keys(clean(form.value, { preserveArrays: false })).length === 0) {\n\t\tfor (const key of keys) {\n\t\t\tclearFormError(form.get(key), 'required');\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t// Iben: Collect all control keys that are missing values\n\tconst requiredKeys = new Set<string>();\n\n\t// Iben: Loop over all keys and check each control on whether it is empty or not\n\tkeys.forEach((key) => {\n\t\tconst control = form.get(key);\n\n\t\t// Iben: Check if the control is empty\n\t\tconst isEmpty =\n\t\t\ttypeof control.value === 'object' && control.value !== null\n\t\t\t\t? Object.keys(clean(control.value)).length === 0\n\t\t\t\t: EMPTY_SET.has(control.value);\n\n\t\t// Iben: Add/remove the errors when needed\n\t\tif (isEmpty) {\n\t\t\tsetFormError(control, 'required');\n\n\t\t\trequiredKeys.add(key);\n\t\t} else {\n\t\t\tclearFormError(control, 'required');\n\n\t\t\trequiredKeys.delete(key);\n\t\t}\n\t});\n\n\t// Iben: Return either null or the list of controls that are missing values based on the empty state\n\treturn requiredKeys.size === 0 ? null : { allOrNothingRequiredError: Array.from(requiredKeys) };\n};\n","import { FormGroup } from '@angular/forms';\nimport clean from 'obj-clean';\n\nimport { clearFormError, setFormError } from '../utils';\n\nexport interface AtLeastOneRequiredValidatorOptions<KeyType extends string = string> {\n\tcontrols?: KeyType[];\n\tconditionalFunction?: (data: any) => boolean;\n}\n\n/**\n * FormGroup validator which checks if either at least one value is filled in\n *\n * @param options - An optional object with configuration options, see below params for more info\n */\nexport const atLeastOneRequiredValidator = <KeyType extends string = string>(\n\toptions?: AtLeastOneRequiredValidatorOptions<KeyType>\n) => {\n\treturn (group: FormGroup): { atLeastOneRequiredError: true } | null => {\n\t\t// Iben: Get the optional configuration items\n\t\tlet conditionalFunction: (data: any) => boolean;\n\t\tlet keys: KeyType[];\n\n\t\tif (options) {\n\t\t\tconditionalFunction = options.conditionalFunction;\n\t\t\tkeys = options.controls;\n\t\t}\n\t\t// Iben: Setup the needed variables to handle the validator\n\t\tconst cleanedFormValue = clean(group.value);\n\t\tconst cleanedKeys = new Set(Object.keys(cleanedFormValue));\n\t\tconst controls = Object.values(group.controls);\n\t\tconst empty = cleanedKeys.size === 0;\n\n\t\t// Iben: If nothing is filled in, we return an error\n\t\tif (\n\t\t\t(empty && !conditionalFunction) ||\n\t\t\t(empty && conditionalFunction && conditionalFunction(group.value))\n\t\t) {\n\t\t\tfor (const control of controls) {\n\t\t\t\tsetFormError(control, 'required');\n\t\t\t}\n\n\t\t\treturn { atLeastOneRequiredError: true };\n\t\t}\n\n\t\t// Iben: Check if we need to check on a specific key\n\t\tif (keys) {\n\t\t\tconst hasOneKey = keys.reduce((hasOne, key) => hasOne || cleanedKeys.has(key), false);\n\n\t\t\t// Iben: Only return an error when there is no key matched at all\n\t\t\t// and in case of a conditionalFunction if the conditionalFunction is matched as well\n\t\t\tif (\n\t\t\t\t(!hasOneKey && !conditionalFunction) ||\n\t\t\t\t(!hasOneKey && conditionalFunction && conditionalFunction(group.value))\n\t\t\t) {\n\t\t\t\tfor (const key of keys) {\n\t\t\t\t\tsetFormError(group.get(key), 'required');\n\t\t\t\t}\n\n\t\t\t\treturn { atLeastOneRequiredError: true };\n\t\t\t}\n\t\t}\n\n\t\t// Iben: In case there are no errors, clean the required errors and return null\n\t\tfor (const control of controls) {\n\t\t\tclearFormError(control, 'required');\n\t\t}\n\n\t\treturn null;\n\t};\n};\n","import { FormGroup } from '@angular/forms';\n\nimport { clearFormError, setFormError } from '../utils';\n\nconst EMPTY_SET = new Set([undefined, null, '']);\n\n/**\n * FormGroup validator which checks if an array of controls in the control are filled in if the depended control is filled in\n *\n * @param controls - An array of controls.\n * @param dependedControlKey - A control within the group which the other controls depend on.\n * @param matchFunction - Optional function the dependedControl should check\n */\nexport const dependedRequiredValidator = <KeyType extends string = string>(\n\tcontrols: KeyType[],\n\tdependedControlKey: KeyType,\n\tmatchFunction?: (data: any) => boolean\n) => {\n\treturn (form: FormGroup): { hasDependedRequiredError: string[] } | null => {\n\t\t// Iben: Make a set so we know which controls are not filled in\n\t\tconst keysWithErrors = new Set<KeyType>();\n\t\tconst dependedControl = form.get(dependedControlKey);\n\n\t\t// Iben: If the control is not filled in or the value doesn't match, we do an early exit and remove all potential required errors\n\t\tif (\n\t\t\t!dependedControl ||\n\t\t\t!(matchFunction\n\t\t\t\t? matchFunction(dependedControl.value)\n\t\t\t\t: !EMPTY_SET.has(dependedControl.value))\n\t\t) {\n\t\t\tfor (const key of controls) {\n\t\t\t\tconst control = form.get(key);\n\n\t\t\t\t// Continue if control does not exist\n\t\t\t\tif (!control) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tclearFormError(control, 'required');\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben: Set an overall error so we can see if all controls are filled in or not\n\t\tlet hasError = false;\n\n\t\tfor (const key of controls) {\n\t\t\tconst control = form.get(key);\n\n\t\t\t// Iben: Continue if control does not exist\n\t\t\tif (!control) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\thasError = hasError || EMPTY_SET.has(control.value);\n\n\t\t\t// Iben: If the control is not filled in we set a required error, if not, we remove it\n\t\t\tif (!EMPTY_SET.has(control.value)) {\n\t\t\t\tclearFormError(control, 'required');\n\t\t\t\tkeysWithErrors.delete(key);\n\t\t\t} else {\n\t\t\t\tsetFormError(control, 'required');\n\t\t\t\tkeysWithErrors.add(key);\n\t\t\t}\n\t\t}\n\n\t\tconst errors = Array.from(keysWithErrors);\n\n\t\treturn hasError ? { hasDependedRequiredError: errors } : null;\n\t};\n};\n","import { FormControl } from '@angular/forms';\n\n/**\n * Validates whether the inputted value has exceeded the maximum amount of decimals after the comma\n *\n * @param max - The maximum number of decimals after the comma\n */\nexport const decimalsAfterCommaValidator = (max: number) => {\n\treturn (control: FormControl): { invalidDecimalsAfterComma: true } | null => {\n\t\t// Iben: In case no control was provided, or the control value was empty, we early exit\n\t\tif (!control || (!control.value && control.value !== 0)) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben: We check if the input value matches the amount of decimals after the comma, if not, we return an error\n\t\treturn new RegExp(`^\\\\d+(.\\\\d{1,${max}})?$`).test(`${control.value}`)\n\t\t\t? null\n\t\t\t: { invalidDecimalsAfterComma: true };\n\t};\n};\n","import { FormGroup, ValidatorFn } from '@angular/forms';\nimport { format, isValid } from 'date-fns';\n\nimport { clearFormError, setFormError } from '../utils';\n\n/**\n * A FormGroup validator to check whether a start and end date are chronologically correct\n *\n * @param startControlKey - The key of the control containing the start date value\n * @param endControlKey - The key of the control containing the end date value\n * @param format - Optional format of the dates provided by the controls, by default yyyy-MM-dd\n */\nexport const chronologicalDatesValidator = (\n\tstartControlKey: string,\n\tendControlKey: string,\n\tdateFormat = 'yyyy-MM-dd'\n): ValidatorFn => {\n\treturn (form: FormGroup): { incorrectChronologicalDates: true } | null => {\n\t\t// Iben: Get the date values\n\t\tconst value = form.getRawValue();\n\t\tconst startValue = value[startControlKey];\n\t\tconst endValue = value[endControlKey];\n\n\t\t// Iben: Clear the form error on the endControl\n\t\tclearFormError(form.get(endControlKey), 'incorrectChronologicalDate');\n\n\t\t// Iben: If either date value is not filled in, we early exit to handle this in a potential required validator\n\t\tif (!startValue || !endValue) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben: If the dates as is are not valid, early exit\n\t\tif (!isValid(new Date(startValue)) || !isValid(new Date(endValue))) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben: Create dates so we can compare them\n\t\tconst startDate = format(new Date(startValue), dateFormat);\n\t\tconst endDate = format(new Date(endValue), dateFormat);\n\n\t\t// Iben: If either date is invalid based on the format, we early exit to handle this in a date validator\n\t\tif (!isValid(new Date(startDate)) || !isValid(new Date(endDate))) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben: If the endDate falls before the startDate, we return an error\n\t\tif (endDate < startDate) {\n\t\t\tsetFormError(form.get(endControlKey), 'incorrectChronologicalDate');\n\n\t\t\treturn { incorrectChronologicalDates: true };\n\t\t}\n\n\t\treturn null;\n\t};\n};\n","import { AbstractControl, ValidationErrors } from '@angular/forms';\n\nimport { isEmptyInputValue } from '../utils';\n\nexport const extendedEmailValidator = (control: AbstractControl): ValidationErrors | null => {\n\tif (isEmptyInputValue(control.value)) {\n\t\treturn null; // don't validate empty values to allow optional controls\n\t}\n\n\t// Validates more strictly than the default email validator. Requires a period in the tld part.\n\treturn /^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]+$/gi.test(control.value)\n\t\t? null\n\t\t: { extendedEmail: true };\n};\n","import { FormControl, ValidationErrors, ValidatorFn } from '@angular/forms';\nimport { isValid } from 'date-fns';\n\n/**\n * hasNoFutureDateValidator\n *\n * Validator function to ensure that the selected date is not in the future.\n * If the date is in the future, it returns an error.\n * @returns ValidationErrors if the date is in the future, otherwise null.\n *\n */\nexport const hasNoFutureDateValidator = (): ValidatorFn => {\n\treturn (control: FormControl): ValidationErrors | null => {\n\t\t// Early exit in case the control or the value does not exist\n\t\tif (!control.value) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Create date objects based on the provided date and current date\n\t\tconst inputDate = new Date(control.value);\n\t\tconst currentDate = new Date();\n\n\t\t// In case the date itself is invalid, we early exit to let a potential date validator handle the error\n\t\tif (!isValid(inputDate)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn inputDate <= currentDate ? null : { isFutureDate: { valid: false } };\n\t};\n};\n","import { AbstractControl } from '@angular/forms';\nimport { isValid, parse } from 'date-fns';\n\ntype DateRangeErrorCodes =\n\t| 'invalidMaxDate'\n\t| 'invalidMinDate'\n\t| 'dateAfterMaxDate'\n\t| 'dateBeforeMinDate';\n\n/**\n * Form control validator which validates if a date is between a provided range (edges not included)\n *\n * @param minDate - Minimum valid date\n * @param maxDate - Maximum valid date\n * @param format - Optional format used for all 3 dates, by default yyyy-MM-dd\n */\nexport const dateRangeValidator = (min: string, max: string, format: string = 'yyyy-MM-dd') => {\n\treturn (control: AbstractControl): { invalidRange: DateRangeErrorCodes } | null => {\n\t\t// Iben: Early exit in case the control or the value does not exist\n\t\tif (!control?.value) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben : Create date objects based on the provided dates\n\t\tconst date = parse(control.value, format, new Date());\n\t\tconst maxDate = parse(max, format, new Date());\n\t\tconst minDate = parse(min, format, new Date());\n\n\t\t// Iben: In case either of the boundary dates is invalid, we mark the input as invalid as we cannot confirm it's in the right range\n\t\tif (!isValid(maxDate) || !isValid(minDate)) {\n\t\t\treturn {\n\t\t\t\tinvalidRange: !isValid(maxDate) ? 'invalidMaxDate' : 'invalidMinDate',\n\t\t\t};\n\t\t}\n\n\t\t// Iben: In case the date itself is invalid, we early exit to let a potential date validator handle the error\n\t\tif (!isValid(date)) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Iben: We check if the date is in between the boundaries and return an error if need be\n\t\tif (!(minDate <= date) || !(date <= maxDate)) {\n\t\t\treturn {\n\t\t\t\tinvalidRange: date > maxDate ? 'dateAfterMaxDate' : 'dateBeforeMinDate',\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t};\n};\n","import { FormControl, ValidationErrors, ValidatorFn } from '@angular/forms';\n\n/**\n * WordCountValidator\n *\n * The WordCountValidator validator will check the amount of words provided in a control.\n *\n * @param .min\n * @param .max\n * @returns ValidatorFn\n */\nexport const WordCountValidator = ({ min, max }: { min?: number; max?: number }): ValidatorFn => {\n\treturn (control: FormControl): ValidationErrors | null => {\n\t\tif (\n\t\t\ttypeof control?.value !== 'string' ||\n\t\t\t(typeof min !== 'number' && typeof max !== 'number')\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst wordCount = control.value.trim().split(' ').length;\n\n\t\tif (typeof min === 'number' && wordCount <= min) {\n\t\t\treturn { minWordCountNotReached: { valid: false } };\n\t\t}\n\n\t\tif (typeof max === 'number' && wordCount > max) {\n\t\t\treturn { maxWordCountReached: { valid: false } };\n\t\t}\n\n\t\treturn null;\n\t};\n};\n","import { FormControl, FormGroup, ValidationErrors } from '@angular/forms';\n\nimport { clearFormError, setFormError } from '../utils';\n\n/**\n * CompareValidator\n *\n * The CompareValidator will return a validator that compares the values of two FormControls\n * within a FormGroup based on a given comparator function.\n *\n * Note: This validator will only set an error on the group it is set to\n * unless the `setErrorOnKey` argument is given.\n *\n * @param keys {string[]}\n * @param comparatorFn {(...args: ValueType[]) => boolean}\n * @param setErrorOnKey {string}\n * @returns {(group: FormGroup<{ [key: string]: FormControl<ValueType>; }>) => ValidationErrors}\n */\nexport const CompareValidator = <ValueType = unknown>(\n\tkeys: string[],\n\tcomparatorFn: (...args: ValueType[]) => boolean,\n\tsetErrorOnKey?: string\n): ((\n\tgroup: FormGroup<{\n\t\t[key: string]: FormControl<ValueType>;\n\t}>\n) => ValidationErrors) => {\n\treturn (\n\t\tgroup: FormGroup<{\n\t\t\t[key: string]: FormControl<ValueType>;\n\t\t}>\n\t): ValidationErrors => {\n\t\t// Denis: map the values to an array:\n\t\tconst values: ValueType[] = keys.map((key: string) => group?.get(key).getRawValue());\n\t\tconst setErrorOnKeyControl = group?.get(setErrorOnKey);\n\n\t\t// Denis: check if any of the keys contains an undefined or null value:\n\t\tif (values.some((value: ValueType) => typeof value === 'undefined' || value === null)) {\n\t\t\tsetErrorOnKeyControl && clearFormError(group.get(setErrorOnKey), 'compareError');\n\n\t\t\treturn null;\n\t\t}\n\n\t\tif (comparatorFn(...values)) {\n\t\t\tsetErrorOnKeyControl && setFormError(group.get(setErrorOnKey), 'compareError');\n\n\t\t\treturn {\n\t\t\t\tcompareError: true,\n\t\t\t};\n\t\t}\n\n\t\tsetErrorOnKeyControl && clearFormError(group.get(setErrorOnKey), 'compareError');\n\n\t\treturn null;\n\t};\n};\n","import {\n\tAbstractControl,\n\tFormControl,\n\tFormGroup,\n\tValidationErrors,\n\tValidatorFn,\n} from '@angular/forms';\n\nimport { allOrNothingRequiredValidator } from './all-or-nothing-required/all-or-nothing-required.validator';\nimport {\n\tAtLeastOneRequiredValidatorOptions,\n\tatLeastOneRequiredValidator,\n} from './at-least-one-required/at-least-one-required.validator';\nimport { dependedRequiredValidator } from './depended-required/depended-required.validator';\nimport { decimalsAfterCommaValidator } from './decimals-after-comma/decimals-after-comma.validator';\nimport { chronologicalDatesValidator } from './chronological-dates/chronological-dates.validator';\nimport { extendedEmailValidator } from './email/extended-email.validator';\nimport { hasNoFutureDateValidator } from './has-no-future-date/has-no-future-date.validator';\nimport { dateRangeValidator } from './date-range/date-range.validator';\nimport { WordCountValidator } from './max-word-count/word-count.validator';\nimport { CompareValidator } from './compare/compare.validator';\n\n/**\n * Exported Class\n */\n\nexport class NgxValidators {\n\t/**\n\t * A stricter validator for e-mail validation\n\t *\n\t * @param control - A form control\n\t */\n\tstatic extendedEmail(control: AbstractControl): ValidationErrors | null {\n\t\treturn extendedEmailValidator(control);\n\t}\n\n\t/**\n\t * A validator to check if all or none of the values of a form group are filled in.\n\t * Particularly useful in situations where a form group field within itself is optional,\n\t * but all fields are required in case it does get filled in\n\t *\n\t * Returns an `allOrNothingRequiredError` error on the provided FormGroup and a `required` error on the individual controls\n\t *\n\t * @param control - A form group control\n\t */\n\tstatic allOrNothingRequired(control: FormGroup): ValidationErrors | null {\n\t\treturn allOrNothingRequiredValidator(control);\n\t}\n\n\t/**\n\t * A validator to check if at least one of the provided controls of the form group are filled in\n\t *\n\t * Returns an `atLeastOneRequiredError` error on the provided FormGroup and a `required` error on the individual controls\n\t *\n\t * @param options - An optional object with configuration options, see below params for more info\n\t * @param controlNames - Optional list of controls, if not provided the validator is applied to all controls of the group\n\t * @param conditionalFunction - Optional function the form value needs to return true to for the required to be se\n\t */\n\tstatic atLeastOneRequired<KeyType extends string = string>(\n\t\toptions?: AtLeastOneRequiredValidatorOptions<KeyType>\n\t): ValidatorFn {\n\t\treturn atLeastOneRequiredValidator<KeyType>(options);\n\t}\n\n\t/**\n\t * The compareValidator will return a validator that compares the values of two FormControls\n\t * within a FormGroup based on a given comparator function.\n\t *\n\t * Returns a `compareError` on the provided FormGroup and on the individual controls if the `setErrorKey` argument is provided.\n\t *\n\t * @param keys {string[]}\n\t * @param comparatorFn {(...args: ValueType[]) => boolean}\n\t * @param setErrorOnKey {string}\n\t * @returns {(group: FormGroup<{ [key: string]: FormControl<ValueType>; }>) => ValidationErrors}\n\t *\n\t */\n\tstatic compareValidator<ValueType = unknown>(\n\t\tkeys: string[],\n\t\tcomparatorFn: (...args: ValueType[]) => boolean,\n\t\tsetErrorOnKey?: string\n\t): (\n\t\tgroup: FormGroup<{\n\t\t\t[key: string]: FormControl<ValueType>;\n\t\t}>\n\t) => ValidationErrors {\n\t\treturn CompareValidator(keys, comparatorFn, setErrorOnKey);\n\t}\n\n\t/**\n\t * FormGroup validator which checks if an array of controls in the control are filled in if the depended control is filled in\n\t *\n\t * Returns a `hasDependedRequiredError` error on the provided FormGroup and a `required` error on the individual controls\n\t *\n\t * @param controls - An array of controls.\n\t * @param dependedControlKey - A control within the group which the other controls depend on.\n\t * @param matchFunction - Optional function the dependedControl should check\n\t */\n\tstatic dependedRequired<KeyType extends string = string>(\n\t\tcontrols: KeyType[],\n\t\tdependedControlKey: KeyType,\n\t\tmatchFunction?: (data: any) => boolean\n\t): ValidatorFn {\n\t\treturn dependedRequiredValidator<KeyType>(controls, dependedControlKey, matchFunction);\n\t}\n\n\t/**\n\t * Validates whether the inputted value has exceeded the maximum amount of decimals after the comma\n\t *\n\t * Returns an `invalidDecimalsAfterComma` error on the provided control\n\t *\n\t * @param max - The maximum number of decimals after the comma\n\t */\n\tstatic decimalsAfterComma(max: number): ValidatorFn {\n\t\treturn decimalsAfterCommaValidator(max);\n\t}\n\n\t/**\n\t * A FormGroup validator to check whether a start and end date are chronologically correct\n\t *\n\t * Returns an `incorrectChronologicalDates` error on the provided FormGroup and a `incorrectChronologicalDate` on the endControl\n\t *\n\t * @param startControlKey - The key of the control containing the start date value\n\t * @param endControlKey - The key of the control containing the end date value\n\t * @param format - Optional format of the dates provided by the controls, by default yyyy-MM-dd\n\t */\n\tstatic chronologicalDates(\n\t\tstartControlKey: string,\n\t\tendControlKey: string,\n\t\tformat = 'yyyy-MM-dd'\n\t): ValidatorFn {\n\t\treturn chronologicalDatesValidator(startControlKey, endControlKey, format);\n\t}\n\n\t/**\n\t * Form control validator which validates if a date is between a provided range\n\t *\n\t * Returns an `invalidRange` error\n\t *\n\t * @param minDate - Minimum valid date\n\t * @param maxDate - Maximum valid date\n\t * @param format - Optional format used for all 3 dates, by default yyyy-MM-dd\n\t */\n\tstatic dateRangeValidator(min: string, max: string, format = 'yyyy-MM-dd'): ValidatorFn {\n\t\treturn dateRangeValidator(min, max, format);\n\t}\n\n\t/**\n\t * Form control validator which validates if a date is not in the future.\n\t *\n\t * Returns an `isFutureDate` error\n\t */\n\tstatic hasNoFutureDateValidator = (): ValidatorFn => {\n\t\treturn hasNoFutureDateValidator();\n\t};\n\n\t/**\n\t * Form control validator which validates if a provided string does not contain more or less words than a provided min and/or max.\n\t *\n\t * Returns either a `minWordCountNotReached` or a `maxWordCountReached`\n\t */\n\tstatic wordCountValidator = ({ min, max }: { min: number; max: number }): ValidatorFn => {\n\t\treturn WordCountValidator({ min, max });\n\t};\n\n\t// Add other custom validators :-)\n}\n","/**\n * In order to select all accessors in a FormContainer, we need this base class to pass to our ViewChildren.\n *\n * IMPORTANT: This will never be used as an actual functional component\n */\nexport class BaseFormAccessor {}\n","import { AbstractControl } from '@angular/forms';\n\nimport { FormStateOptionsEntity } from '../../interfaces';\n\n/**\n * Allows for a deep markAsDirty of all controls. Can be used for a FormGroup or a FormArray\n *\n * @param controls - The controls we wish to update the value and validity of\n * @param onlySelf - Whether or not we want it to be only the control itself and not the direct ancestors. Default this is true\n */\nexport const markAllAsDirty = (\n\tcontrols: Record<string, AbstractControl> | AbstractControl[],\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: We loop over all controls\n\t(Array.isArray(controls) ? controls : Object.values(controls)).forEach((control) => {\n\t\t// Iben: If there are no child controls, we update the value and validity of the control\n\t\tif (!control['controls']) {\n\t\t\tcontrol.markAsDirty(options);\n\t\t\treturn;\n\t\t}\n\n\t\t// Iben: If there are child controls, we recursively update the value and validity\n\t\tmarkAllAsDirty(control['controls'], options);\n\t});\n};\n","import { AbstractControl } from '@angular/forms';\n\nimport { FormStateOptionsEntity } from '../../interfaces';\n\n/**\n * Adds a deep update value and validity to the existing update value and validity\n *\n * @param form - The provided abstract control\n * @param options - The options we wish to call along with the update value and validity function\n */\nexport const updateAllValueAndValidity = (\n\tform: AbstractControl,\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: Call the original updateValueAndValidity\n\tform.updateValueAndValidity(options);\n\t// Iben: If we don't have the inner form yet we just do the default update value\n\tif (!form || !form['controls']) {\n\t\treturn;\n\t}\n\n\t// Iben: We update the value and validity recursively for each child control\n\tdeepUpdateValueAndValidity(form['controls'], { ...options, onlySelf: true });\n};\n/**\n * Allows for a deep updateValueAndValidity of all controls. Can be used for a FormGroup or a FormArray\n *\n * @param controls - The controls we wish to update the value and validity of\n * @param onlySelf - Whether or not we want it to be only the control itself and not the direct ancestors. Default this is true\n */\nexport const deepUpdateValueAndValidity = (\n\tcontrols: Record<string, AbstractControl> | AbstractControl[],\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: We loop over all controls\n\t(Array.isArray(controls) ? controls : Object.values(controls)).forEach((control) => {\n\t\t// Iben: If there are no child controls, we update the value and validity of the control\n\t\tif (!control['controls']) {\n\t\t\tcontrol.updateValueAndValidity(options);\n\t\t\treturn;\n\t\t}\n\n\t\t// Iben: If there are child controls, we recursively update the value and validity\n\t\tdeepUpdateValueAndValidity(control['controls'], options);\n\t});\n};\n","import { AbstractControl, FormGroup } from '@angular/forms';\n\nimport { DataFormAccessor, FormAccessor } from '../../abstracts';\nimport { markAllAsDirty } from '../mark-all-as-dirty/mark-all-as-dirty.util';\nimport { FormStateOptionsEntity } from '../../interfaces';\nimport { updateAllValueAndValidity } from '../custom-update-value-and-validity/custom-update-value-and-validity.util';\n\n/**\n * Disable a FormControl/FormArray\n *\n * @param keys - The keys of the fields we wish to disable\n * @param emitEvent - Whether or not we wish to emit the event\n */\nconst handleDisableFormControlOfFormArray = (\n\tform: AbstractControl,\n\tkeys: Set<string>,\n\temitEvent: boolean\n) => {\n\t// Iben: Early exit in case the state already matches so we don't do unnecessary emits\n\tif (\n\t\t(keys.has('formAccessorSelf') && form.disabled) ||\n\t\t(!keys.has('formAccessorSelf') && form.enabled)\n\t) {\n\t\treturn;\n\t}\n\n\t// Iben: Disable/enable the control based on the key\n\tkeys.has('formAccessorSelf') ? form.disable({ emitEvent }) : form.enable({ emitEvent });\n};\n\n/**\n * Disable the controls of a FormGroup\n *\n * @param keys - The keys of the fields we wish to disable\n * @param emitEvent - Whether or not we wish to emit the event\n */\nconst handleDisableFormGroup = (form: FormGroup, keys: Set<string>, emitEvent: boolean) => {\n\t// Iben: Loop over all controls and enable them so that they are re-enabled in case the set of keys changes\n\tenableControls(form, emitEvent);\n\n\t// Iben: Disable the keys\n\tArray.from(keys).forEach((key) => {\n\t\tconst control = form.get(key);\n\t\tif (!control) {\n\t\t\tconsole.warn(\n\t\t\t\t`FormAccessor: The key \"${key}\" was provided in the disableFields array but was not found in the provided form.`\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Iben: Prevent emit event if the control is already disabled\n\t\tif (!control.disabled) {\n\t\t\tcontrol.disable({ emitEvent });\n\t\t}\n\t});\n};\n\n/**\n * Recursively enables all fields of a control\n *\n * @param control - An AbstractControl which we want to enable and enable all children off\n * @param emitEvent - Whether or not we wish to emit an event\n */\nconst enableControls = (control: AbstractControl, emitEvent: boolean = false): void => {\n\t//Iben: If no control was found, early exit\n\tif (!control) {\n\t\treturn;\n\t}\n\n\t// Iben: Enable the control itself if it is not enabled yet\n\tif (!control.enabled) {\n\t\tcontrol.enable({ emitEvent });\n\t}\n\n\t// Iben: If there are no controls, early exit\n\tif (!control['controls']) {\n\t\treturn;\n\t}\n\n\t// Iben: Recursively enable each control\n\t(Array.isArray(control['controls'])\n\t\t? control['controls']\n\t\t: Object.values(control['controls'])\n\t).forEach((child: AbstractControl) => {\n\t\tenableControls(child, emitEvent);\n\t});\n};\n\n/**\n * Disables and enables a form's control based on a set of provided keys\n *\n * @param form - The form we wish to disable the controls for\n * @param controlKeys - A set of keys of the controls\n * @param emitEvent - Whether or not we wish to emit the event\n */\nexport const handleFormAccessorControlDisabling = (\n\tform: AbstractControl,\n\tcontrolKeys: Set<string>,\n\temitEvent: boolean\n) => {\n\t// Iben: Depending on whether we're dealing with a FormArray/FormControl or a FormGroup, we have different and handle the disable/enable state\n\tif (!form['controls'] || Array.isArray(form['controls'])) {\n\t\thandleDisableFormControlOfFormArray(form, controlKeys, emitEvent);\n\t} else {\n\t\thandleDisableFormGroup(form as FormGroup, controlKeys, emitEvent);\n\t}\n};\n\n/**\n * Marks a form and all the form-accessors this form is based on as dirty\n *\n * @param form - The form we wish to mark as dirty\n * @param accessors - An array of all the accessors we wish to mark as dirty\n * @param options - Form state options we wish to provide\n */\nexport const handleFormAccessorMarkAsDirty = (\n\tform: AbstractControl,\n\taccessors: (FormAccessor | DataFormAccessor)[],\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: If the control has child controls, recursively mark them as dirty\n\tif (form['controls']) {\n\t\tmarkAllAsDirty(form['controls'], options);\n\t} else {\n\t\t// Iben : Mark the form as dirty\n\t\tform.markAsDirty(options);\n\t}\n\n\t// Iben: Loop over each form accessor and call the mark as dirty function, so all subsequent accessors are also marked as dirty\n\taccessors.forEach((accessor) => accessor.markAsDirty(options));\n};\n\n/**\n * Marks a form and all the form-accessors this form is based on as touched\n *\n * @param form - The form we wish to mark as touched\n * @param accessors - An array of all the accessors we wish to mark as touched\n * @param options - Form state options we wish to provide\n */\nexport const handleFormAccessorMarkAsTouched = (\n\tform: AbstractControl,\n\taccessors: (FormAccessor | DataFormAccessor)[],\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: Mark all the controls and the children as touched\n\tform.markAllAsTouched();\n\n\t// Iben: Loop over each form accessor and call the mark as touched function, so all subsequent accessors are also marked as touched\n\taccessors.forEach((accessor) => accessor.markAsTouched(options));\n};\n\n/**\n * Marks a form and all the form-accessors this form is based on as pristine\n *\n * @param form - The form we wish to mark as pristine\n * @param accessors - An array of all the accessors we wish to mark as pristine\n * @param options - Form state options we wish to provide\n */\nexport const handleFormAccessorMarkAsPristine = (\n\tform: AbstractControl,\n\taccessors: (FormAccessor | DataFormAccessor)[],\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: Mark all the controls and the children as touched\n\tform.markAsPristine();\n\n\t// Iben: Loop over each form accessor and call the mark as touched function, so all subsequent accessors are also marked as touched\n\taccessors.forEach((accessor) => accessor.markAsPristine(options));\n};\n\n/**\n * Updates a form and all the form-accessors this form i\n *\n * @param form - The form we wish to update the value and validity of\n * @param accessors - An array of all the accessors we wish to update the value and validity of\n * @param options - Form state options we wish to provide\n */\nexport const handleFormAccessorUpdateValueAndValidity = (\n\tform: AbstractControl,\n\taccessors: (FormAccessor | DataFormAccessor)[],\n\toptions: FormStateOptionsEntity = {}\n) => {\n\t// Iben: Update the value and validity of the form\n\tupdateAllValueAndValidity(form, options);\n\n\t// Iben: Loop over each form accessor and call the updateValueAndValidity function, so all subsequent accessors are also updated\n\taccessors.forEach((accessor) => accessor.updateAllValueAndValidity(options));\n};\n","import { AbstractControl } from '@angular/forms';\n\n/**\n * Recursively checks if a form and its possible children have an error\n *\n * @param control - The provided abstract control\n */\nexport const hasErrors = (control: AbstractControl): boolean => {\n\t// Iben: If the form has no children we just return the state of the current form\n\tif (!control['controls']) {\n\t\treturn control.invalid;\n\t}\n\n\t// Iben: If the form has children, we check if some of the child controls have errors\n\tconst controls = control['controls'];\n\n\treturn (Array.isArray(controls) ? controls : Object.values(controls)).some((control) =>\n\t\thasErrors(control)\n\t);\n};\n","import { AbstractControl } from '@angular/forms';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\n/**\n * Listen to the touched event of a control\n *\n * @param control - An AbstractControl\n */\nexport const touchedEventListener = (control: AbstractControl): Observable<boolean> => {\n\t// Iben: Grab the current markAsTouched and UnTouched methods\n\tconst markAsTouched = control.markAsTouched;\n\tconst markAsUnTouched = control.markAsUntouched;\n\n\t// Iben: Set a subject with the current touched state\n\tconst touchedSubject = new BehaviorSubject<boolean>(control.touched);\n\n\t// Iben: Overwrite the existing functions and emit the touched state\n\tcontrol.markAsTouched = (options?: { onlySelf: boolean }) => {\n\t\ttouchedSubject.next(true);\n\t\tmarkAsTouched.bind(control)(options);\n\t};\n\n\tcontrol.markAsUntouched = (options?: { onlySelf: boolean }) => {\n\t\ttouchedSubject.next(false);\n\t\tmarkAsUnTouched.bind(control)(options);\n\t};\n\n\t// Iben: Return the touched state\n\treturn touchedSubject.asObservable();\n};\n","import { Provider, forwardRef } from '@angular/core';\nimport { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nimport { BaseFormAccessor } from '../../abstracts';\n\n/**\n * An interface to wrap an component class in.\n *\n * This interface is for internal use, as a similar one already exists in the CDK, but we don't want to create a dependency on that for this package.\n */\ninterface ComponentTypeWrapper<ComponentType> {\n\tnew (...args: any[]): ComponentType;\n}\n\n/**\n * Generates the necessary providers for a (Data)FormAccessor.\n *\n * @param component - The component class of the (Data)FormAccessor\n */\nexport const createAccessorProviders = <ComponentType>(\n\tcomponent: ComponentTypeWrapper<ComponentType>\n): Provider[] => {\n\treturn [\n\t\t// Iben: Generate a provider for the control handling\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: forwardRef(() => component),\n\t\t\tmulti: true,\n\t\t},\n\t\t// Iben: Generate a provider for the validation handling\n\t\t{\n\t\t\tprovide: NG_VALIDATORS,\n\t\t\tuseExisting: forwardRef(() => component),\n\t\t\tmulti: true,\n\t\t},\n\t\t// Iben: Generate a provider for the FormContainer handling\n\t\t{\n\t\t\tprovide: BaseFormAccessor,\n\t\t\tuseExisting: forwardRef(() => component),\n\t\t},\n\t];\n};\n","import {\n\tChangeDetectorRef,\n\tDirective,\n\tInjector,\n\tInput,\n\tOnDestroy,\n\tOutput,\n\tQueryList,\n\tViewChildren,\n\tinject,\n} from '@angular/core';\nimport {\n\tAbstractControl,\n\tControlValueAccessor,\n\tFormControl,\n\tNgControl,\n\tValidationErrors,\n} from '@angular/forms';\n\nimport { BehaviorSubject, Observable, Subject, filter, takeUntil, tap } from 'rxjs';\nimport { FormAccessorControlsEntity, FormStateOptionsEntity } from '../../interfaces';\nimport { BaseFormAccessor } from '../base-form/base-form.accessor';\nimport { DataFormAccessor } from '../data-form/data-form.accessor';\nimport { FormAccessor } from '../form/form.accessor';\nimport {\n\thandleFormAccessorControlDisabling,\n\thandleFormAccessorMarkAsDirty,\n\thandleFormAccessorMarkAsPristine,\n\thandleFormAccessorMarkAsTouched,\n\thandleFormAccessorUpdateValueAndValidity,\n\thasErrors,\n} from '../../utils';\n\n@Directive()\nexport abstract class NgxFormsControlValueAccessor<\n\t\tDataType = unknown,\n\t\tFormAccessorFormType extends AbstractControl = FormControl,\n\t\tFormValueType = DataType,\n\t>\n\timplements ControlValueAccessor, OnDestroy\n{\n\t/**\n\t * The Injector needed in the constructor\n\t */\n\tprivate readonly injector: Injector = inject(Injector);\n\n\t/**\n\t * The ChangeDetector reference\n\t */\n\tpublic readonly cdRef: ChangeDetectorRef = inject(ChangeDetectorRef);\n\n\t/**\n\t * A subject to hold the parent control\n\t */\n\tprivate readonly parentControlSubject$: Subject<AbstractControl> =\n\t\tnew Subject<AbstractControl>();\n\n\t/**\n\t * A reference to the control tied to this control value accessor\n\t */\n\tprotected readonly parentControl$: Observable<AbstractControl> =\n\t\tthis.parentControlSubject$.pipe(filter(Boolean));\n\n\t/**\n\t * Inner form to write to\n\t */\n\tpublic form: FormAccessorFormType;\n\n\t/**\n\t * Whether the first setDisable has run\n\t */\n\tprotected initialSetDisableHasRun: boolean = false;\n\n\t/**\n\t * On destroy flow handler\n\t */\n\tprotected readonly destroy$ = new Subject();\n\n\t/**\n\t * Subject to check whether the form is initialized\n\t */\n\tprotected readonly initializedSubject$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(\n\t\tfalse\n\t);\n\n\t/**\n\t * Whether or not we want to emit a value when we use the disableFields, by default this will emit\n\t *\n\t * @param keys - Keys we're about to disable\n\t */\n\tprotected emitValueWhenDisableFieldsUsingInput?(\n\t\tkeys: FormAccessorControlsEntity<FormAccessorFormType>[]\n\t): boolean;\n\n\t/**\n\t * A list of all DataFormAccessors en FormAccessors of this component\n\t */\n\t@ViewChildren(BaseFormAccessor) accessors: QueryList<DataFormAccessor | FormAccessor>;\n\n\t/**\n\t * Keys of the fields we wish to disable.\n\t * By default this will emit a valueChanges, this can be overwritten by the emitValueWhenDisableFieldsUsingInput in the Accessor\n\t *\n\t * @memberof FormAccessor\n\t */\n\t@Input() set disableFields(keys: FormAccessorControlsEntity<FormAccessorFormType>[]) {\n\t\t// Iben: Early exit in case the keys are not provided\n\t\tif (!keys) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Iben: Setup a subject to track whether we're still disabling the fields\n\t\tconst disabling = new Subject();\n\n\t\t// Iben: Add the keys to a set for more performant lookup and convert those to a string to not have Typescript issues later down the line\n\t\tconst controlKeys = new Set(keys);\n\n\t\t// Iben: Check if we need to dispatch the disable or enable event\n\t\tconst emitEvent = this.emitValueWhenDisableFieldsUsingInput\n\t\t\t? this.emitValueWhenDisableFieldsUsingInput(keys)\n\t\t\t: true;\n\n\t\t// Iben: Listen to the initialized state of the form\n\t\tthis.initialized$\n\t\t\t.pipe(\n\t\t\t\tfilter(Boolean),\n\t\t\t\ttap(() => {\n\t\t\t\t\t// TODO: Iben: Remove this setTimeout once we're in a Signal based component\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t// Iben: Handle the disabling of the fields\n\t\t\t\t\t\thandleFormAccessorControlDisabling(this.form, controlKeys, emitEvent);\n\t\t\t\t\t});\n\n\t\t\t\t\t// Iben: Set the disabling subject so that we can complete this subscription\n\t\t\t\t\tdisabling.next(undefined);\n\t\t\t\t\tdisabling.complete();\n\t\t\t\t}),\n\t\t\t\ttakeUntil(disabling)\n\t\t\t)\n\t\t\t.subscribe();\n\t}\n\n\t/**\n\t * Whether we want to skip the first setDisable (https://github.com/angular/angular/pull/47576).\n\t * By default, this is true\n\t */\n\t@Input() public skipInitialSetDisable: boolean = true;\n\n\t/**\n\t * Stream to know whether the form has been initialized\n\t */\n\t@Output()\n\tpublic readonly initialized$: Observable<boolean> = this.initializedSubject$.asObservable();\n\n\tconstructor() {\n\t\t// Iben: Use setTimeOut to avoid the circular dependency issue\n\t\tsetTimeout(() => {\n\t\t\ttry {\n\t\t\t\tconst parentControl = this.injector.get(NgControl);\n\n\t\t\t\t// Iben: If for some reason we can't find the control or the ngControl, early exit and throw an error\n\t\t\t\tif (!parentControl?.control) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t'NgxForms: No control was found after initializing. Check if a control was assigned to the FormAccessor.'\n\t\t\t\t\t);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.parentControlSubject$.next(parentControl.control);\n\n\t\t\t\t// Iben: Grab the control from the parent container\n\t\t\t\tconst control = parentControl.control;\n\n\t\t\t\t// Iben: Setup the markAsTouched flow\n\t\t\t\t// Iben: Keep a reference to the original `markAsTouched` handler.\n\t\t\t\tconst markAsTouched = control.markAsTouched.bind(control);\n\n\t\t\t\t// Iben: Override the `markAsTouched` handler with our own.\n\t\t\t\tcontrol.markAsTouched = (options?: FormStateOptionsEntity) => {\n\t\t\t\t\t// Iben: If the control is already marked as touched, we early exit\n\t\t\t\t\tif (control.touched) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Iben: Invoke the original `markAsTouchedHandler`.\n\t\t\t\t\tmarkAsTouched(options);\n\n\t\t\t\t\t// Iben: If the onlySelf flag is set to true, we early exit\n\t\t\t\t\tif (options?.onlySelf) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Iben: Invoke the custom `markAsTouchedHandler`.\n\t\t\t\t\tthis.markAsTouched(options);\n\t\t\t\t};\n\n\t\t\t\t// Iben: Setup the markAsDirty flow\n\t\t\t\t// Iben: Keep a reference to the original `markAsDirty` handler.\n\t\t\t\tconst markAsDirty = control.markAsDirty.bind(control);\n\n\t\t\t\t// Iben: Override the `markAsDirty` handler with our own.\n\t\t\t\tcontrol.markAsDirty = (options?: FormStateOptionsEntity) => {\n\t\t\t\t\t// Iben: If the control is already marked as dirty, we early exit\n\t\t\t\t\tif (control.dirty) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Iben: Invoke the original `markAsDirtyHandler`.\n\t\t\t\t\tmarkAsDirty(options);\n\n\t\t\t\t\t// Iben: If the onlySelf flag is set to true, we early exit\n\t\t\t\t\tif (options?.onlySelf) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Iben: Invoke the custom `markAsDirtyHandler`.\n\t\t\t\t\tthis.markAsDirty(options);\n\t\t\t\t};\n\n\t\t\t\t// Iben: Setup the markAsPristine flow\n\t\t\t\t// Iben: Keep a reference to the original `markAsPristine` handler.\n\t\t\t\tconst markAsPristine = control.markAsPristine.bind(control);\n\n\t\t\t\t// Iben: Override the `markAsPristine` handler with our own.\n\t\t\t\tcontrol.markAsPristine = (options?: FormStateOptionsEntity) => {\n\t\t\t\t\t// Iben: If the control is already marked as pristine, we early exit\n\t\t\t\t\tif (control.pristine) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Iben: Invoke the original `markAsPristineHandler`.\n\t\t\t\t\tmarkAsPristine(options);\n\n\t\t\t\t\t// Iben: If the onlySelf flag is set to true, we early exit\n\t\t\t\t\tif (options?.onlySelf) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Iben: Invoke the custom `markAsPristineHandler`.\n\t\t\t\t\tthis.markAsPristine(options);\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t'NgxForms: No parent control was found while trying to set up the form accessor.'\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Sets up the ControlValueAccessor connectors\n\t */\n\tpublic onTouch: Function = () => {}; // tslint:disable-line:no-empty\n\tpublic onChange: Function = (_: any) => {}; // tslint:disable-line:no-empty\n\n\tpublic registerOnChange(fn: any): void {\n\t\tthis.onChange = fn;\n\t}\n\n\tpublic registerOnTouched(fn: any): void {\n\t\tthis.onTouch = fn;\n\t}\n\n\t/**\n\t * Writes value to the inner form\n\t *\n\t * @param value - Value to patch in the inner form\n\t */\n\tpublic writeValue(value: DataType | undefined | null): void {\n\t\t// Iben: Early exit in case the form was not found\n\t\tif (!this.form) {\n\t\t\tconsole.error(\n\t\t\t\t'NgxForms: No form was found when trying to write a value. This error can occur when overwriting the ngOnInit without invoking super.OnInit().'\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Iben: Reset the current form without emitEvent to not trigger the valueChanges\n\t\tthis.form.reset(undefined, { emitEvent: false });\n\n\t\t// Iben: Patch the current form with the new value without emitEvent to not trigger the valueChanges\n\t\tif (value !== undefined && value !== null) {\n\t\t\tthis.form.patchValue(this.onWriteValueMapper ? this.onWriteValueMapper(value) : value, {\n\t\t\t\temitEvent: false,\n\t\t\t});\n\t\t}\n\n\t\t// Iben: Validate the current value\n\t\tthis.validate();\n\n\t\t// Iben: Detect changes so the changes are visible in the dom\n\t\tthis.cdRef.detectChanges();\n\t}\n\n\t/**\n\t * Mark all controls of the form as touched\n\t */\n\tpublic markAsTouched(options: FormStateOptionsEntity = {}): void {\n\t\thandleFormAccessorMarkAsTouched(this.form, this.accessors?.toArray() || [], options);\n\n\t\t// Iben: Det