UNPKG

@rxap/forms

Version:

This package provides a set of tools and directives to simplify working with Angular forms, including reactive forms, custom validators, and form directives for handling loading, submitting, and error states. It offers decorators for defining forms and co

1 lines 212 kB
{"version":3,"file":"rxap-forms.mjs","sources":["../../../../../packages/angular/forms/src/lib/validators/is-array.ts","../../../../../packages/angular/forms/src/lib/validators/is-boolean.ts","../../../../../packages/angular/forms/src/lib/validators/is-string.ts","../../../../../packages/angular/forms/src/lib/validators/is-number.ts","../../../../../packages/angular/forms/src/lib/validators/is-complex.ts","../../../../../packages/angular/forms/src/lib/validators/is-date.ts","../../../../../packages/angular/forms/src/lib/validators/is-email.ts","../../../../../packages/angular/forms/src/lib/validators/is-enum.ts","../../../../../packages/angular/forms/src/lib/validators/is-int.ts","../../../../../packages/angular/forms/src/lib/validators/is-ip.ts","../../../../../packages/angular/forms/src/lib/validators/is-object.ts","../../../../../packages/angular/forms/src/lib/validators/is-phone-number.ts","../../../../../packages/angular/forms/src/lib/validators/is-port.ts","../../../../../packages/angular/forms/src/lib/validators/is-url.ts","../../../../../packages/angular/forms/src/lib/validators/is-uuid.ts","../../../../../packages/angular/forms/src/lib/validators/index.ts","../../../../../packages/angular/forms/src/lib/directives/form-control-error.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-control-mark-dirty.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-control-mark-pristine.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-control-mark-touched.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-control-mark-untouched.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-control-name.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-group-name.directive.ts","../../../../../packages/angular/forms/src/lib/directives/tokens.ts","../../../../../packages/angular/forms/src/lib/decorators/metadata-keys.ts","../../../../../packages/angular/forms/src/lib/control-actions.ts","../../../../../packages/angular/forms/src/lib/form-array.ts","../../../../../packages/angular/forms/src/lib/form-group.ts","../../../../../packages/angular/forms/src/lib/form-control.ts","../../../../../packages/angular/forms/src/lib/form-builder.ts","../../../../../packages/angular/forms/src/lib/directives/form.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-loaded.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-loading-error.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-loading.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-reset.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-submit-failed.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-submit-invalid.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-submit-successful.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-submit.directive.ts","../../../../../packages/angular/forms/src/lib/directives/form-submitting.directive.ts","../../../../../packages/angular/forms/src/lib/directives/parent-control-container.directive.ts","../../../../../packages/angular/forms/src/lib/directives/forms.module.ts","../../../../../packages/angular/forms/src/lib/directives/models.ts","../../../../../packages/angular/forms/src/lib/decorators/control-async-validator.ts","../../../../../packages/angular/forms/src/lib/decorators/control-change.ts","../../../../../packages/angular/forms/src/lib/decorators/control-set-value.ts","../../../../../packages/angular/forms/src/lib/decorators/control-validator.ts","../../../../../packages/angular/forms/src/lib/decorators/form.ts","../../../../../packages/angular/forms/src/lib/decorators/use-form-array.ts","../../../../../packages/angular/forms/src/lib/decorators/use-form-control.ts","../../../../../packages/angular/forms/src/lib/decorators/use-form-group.ts","../../../../../packages/angular/forms/src/lib/control-value-accessor.ts","../../../../../packages/angular/forms/src/lib/tokens.ts","../../../../../packages/angular/forms/src/index.ts","../../../../../packages/angular/forms/src/rxap-forms.ts"],"sourcesContent":["import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\nexport function IsArray({ message }: { message?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!Array.isArray(control.value)) {\n return {\n isArray: {\n expected: 'A array value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\nexport function IsBoolean({ message }: { message?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(control.value instanceof Boolean || typeof control.value === 'boolean')) {\n return {\n isBoolean: {\n expected: 'A boolean value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\nexport function IsString({ message }: { message?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(control.value instanceof String || typeof control.value === 'string')) {\n return {\n isString: {\n expected: 'A string value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\n/**\n * Options to be passed to IsNumber decorator.\n */\nexport interface IsNumberOptions {\n allowNaN?: boolean;\n allowInfinity?: boolean;\n maxDecimalPlaces?: number;\n strict?: boolean;\n}\n\n/**\n * Checks if a given value is a number.\n */\nexport function isNumber(value: unknown, options: IsNumberOptions = {}): value is number {\n if (typeof value !== 'number') {\n return false;\n }\n\n if (value === Infinity || value === -Infinity) {\n return options.allowInfinity ?? false;\n }\n\n if (Number.isNaN(value)) {\n return options.allowNaN ?? false;\n }\n\n if (options.maxDecimalPlaces !== undefined) {\n let decimalPlaces = 0;\n if (value % 1 !== 0) {\n decimalPlaces = value.toString().split('.')[1].length;\n }\n if (decimalPlaces > options.maxDecimalPlaces) {\n return false;\n }\n }\n\n return Number.isFinite(value);\n}\n\n/**\n * @deprecated use RxapValidators.isNumber() instead\n * @param control\n * @constructor\n */\nexport function IsNumber(control: AbstractControl): ValidationErrors | null {\n if (control.value === null) {\n return null;\n }\n if (isNaN(Number(control.value))) {\n return {\n isNumber: {\n expected: 'A number or a string representing a number',\n actual: control.value,\n },\n };\n }\n return null;\n}\n\nexport function _IsNumber({\n message,\n options,\n }: { message?: string, options?: IsNumberOptions } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!isNumber(\n !options?.strict && typeof control.value === 'string' ? Number(control.value) : control.value,\n options,\n )) {\n return {\n isNumber: {\n expected: 'A number value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport { IsString } from './is-string';\nimport { isNumber } from './is-number';\n\nexport interface ComplexityOptions {\n uppercase?: number;\n special?: number;\n digit?: number;\n lowercase?: number;\n upperLower?: number;\n alphaNumeric?: number;\n min?: number,\n max?: number,\n range?: number,\n exact?: number,\n}\n\ninterface RegexOptions {\n uppercase: string;\n special: string;\n digit: string;\n lowercase: string;\n upperLower: string;\n alphaNumeric: string;\n}\n\nconst regexOptions: RegexOptions = {\n uppercase: '.*[A-Z]',\n special: '.*[^A-Za-z0-9]',\n digit: '.*[0-9]',\n lowercase: '.*[a-z]',\n upperLower: '.*[a-zA-Z]',\n alphaNumeric: '.*[a-zA-Z0-9]',\n};\n\ninterface LengthOptions {\n min: string,\n max: string,\n range: string,\n exact: string,\n no_limit: string;\n}\n\nconst lengthOptions: LengthOptions = {\n min: '.{n,}',\n max: '.{0,n}',\n range: '.{min,max}',\n exact: '.{n}',\n no_limit: '.*',\n};\n\ntype Discriminate<U, K extends PropertyKey> =\n U extends any\n ? K extends keyof U ? U : U & Record<K, unknown>\n : never;\n\nfunction inOperator<K extends PropertyKey, T extends object>(k: K, o: T): o is Discriminate<T, K> {\n return k in o;\n}\n\nfunction create(options: ComplexityOptions): string {\n let regex = '^';\n for (const [ key, value ] of Object.entries(regexOptions)) {\n if (inOperator(key, options) && isNumber(options[key])) {\n regex += '(?=' + value.repeat(options[key]) + ')';\n }\n }\n if (isNumber(options.min) && isNumber(options.max)) {\n regex += lengthOptions.range.replace('min', options.min.toFixed(0)).replace('max', options.max.toFixed(0));\n } else if (isNumber(options.max)) {\n regex += lengthOptions.max.replace('n', options.max.toFixed(0));\n } else if (isNumber(options.min)) {\n regex += lengthOptions.min.replace('n', options.min.toFixed(0));\n } else if (isNumber(options.exact)) {\n regex += lengthOptions.exact.replace('n', options.exact.toFixed(0));\n } else {\n regex += lengthOptions.no_limit;\n }\n regex += '$';\n return regex;\n}\n\nfunction check(str: string, regexStringOrOptions: string | ComplexityOptions) {\n let regexString: string;\n if (typeof regexStringOrOptions === 'object') {\n regexString = create(regexStringOrOptions);\n } else {\n regexString = regexStringOrOptions;\n }\n return new RegExp(regexString).test(str);\n}\n\nfunction checkError(str: string, options: ComplexityOptions) {\n const returnObject: Record<string, boolean> = {};\n for (const [ key, value ] of Object.entries(options)) {\n returnObject[key] = check(str, { [key]: value });\n }\n return returnObject;\n}\n\nexport function IsComplex({\n message,\n options,\n }: { message?: string, options: ComplexityOptions }) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n const isNotString = IsString({ message })(control);\n if (isNotString !== null) {\n return isNotString;\n }\n const errors = checkError(control.value, options);\n if (Object.keys(errors).length && Object.values(errors).some(item => !item)) {\n return {\n isComplex: {\n expected: options,\n actual: errors,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\nexport function IsDate({ message }: { message?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(control.value instanceof Date && !isNaN(control.value.getTime()))) {\n return {\n isDate: {\n expected: 'A date value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport {\n isEmail,\n IsEmailOptions,\n} from '@rxap/validator';\n\nexport function IsEmail({\n message,\n options,\n }: { message?: string, options?: IsEmailOptions } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(typeof control.value === 'string' && isEmail(control.value, options))) {\n return {\n isEmail: {\n expected: 'A email value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\nexport function IsEnum({\n message,\n entity,\n }: { message?: string, entity: any }) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (Object.keys(entity).map(k => entity[k]).indexOf(control.value) === -1) {\n return {\n isEnum: {\n expected: 'A enum value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport {\n isNumber,\n IsNumberOptions,\n} from './is-number';\n\nexport function IsInt({\n message,\n options,\n }: { message?: string, options?: IsNumberOptions } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!isNumber(control.value, options) || Number.isInteger(control.value)) {\n return {\n isInt: {\n expected: 'A int value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport { isIP } from '@rxap/validator';\n\nexport function IsIP({\n message,\n version,\n }: { message?: string, version?: string | number } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(typeof control.value === 'string' && isIP(control.value, version))) {\n return {\n isIp: {\n expected: `A valid IPv${ version ?? '4' } value`,\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\n\nexport function IsObject({ message }: { message?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(control.value !==\n undefined &&\n (typeof control.value === 'object' || typeof control.value === 'function') &&\n !Array.isArray(control.value))) {\n return {\n isObject: {\n expected: 'A object value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport {\n isMobilePhone,\n IsMobilePhoneOptions,\n MobilePhoneLocale,\n} from '@rxap/validator';\n\nexport function IsPhoneNumber({\n message,\n locale,\n options,\n }: {\n message?: string,\n locale?: 'any' | MobilePhoneLocale | MobilePhoneLocale[],\n options?: IsMobilePhoneOptions,\n} = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!isMobilePhone(control.value, locale, options)) {\n return {\n isPhoneNumber: {\n expected: 'A phone number value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport { isPort } from '@rxap/validator';\n\nexport function IsPort({ message }: { message?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(typeof control.value === 'string' && isPort(control.value))) {\n return {\n isPort: {\n expected: `A valid port number between 1 and 65535`,\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport {\n isURL,\n IsURLOptions,\n} from '@rxap/validator';\n\nexport function IsUrl({\n message,\n options,\n }: { message?: string, options?: IsURLOptions } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(typeof control.value === 'string' && isURL(control.value, options))) {\n return {\n isURL: {\n expected: 'A url value',\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import {\n AbstractControl,\n ValidationErrors,\n} from '@angular/forms';\nimport { isUUID } from '@rxap/validator';\n\nexport function IsUUID({\n message,\n version = 'all',\n }: { message?: string, version?: string } = {}) {\n return (control: AbstractControl): ValidationErrors | null => {\n if (control.value === null) {\n return null;\n }\n if (!(typeof control.value === 'string' && isUUID(control.value, version))) {\n return {\n isUuid: {\n expected: version === 'all' ? `A valid uuid` : `A valid uuid version ${ version }`,\n actual: control.value,\n message,\n },\n };\n }\n return null;\n };\n}\n","import { IsArray } from './is-array';\nimport { IsBoolean } from './is-boolean';\nimport { IsComplex } from './is-complex';\nimport { IsDate } from './is-date';\nimport { IsEmail } from './is-email';\nimport { IsEnum } from './is-enum';\nimport { IsInt } from './is-int';\nimport { IsIP } from './is-ip';\nimport { _IsNumber as IsNumber } from './is-number';\nimport { IsObject } from './is-object';\nimport { IsPhoneNumber } from './is-phone-number';\nimport { IsPort } from './is-port';\nimport { IsString } from './is-string';\nimport { IsUrl } from './is-url';\nimport { IsUUID } from './is-uuid';\n\nexport const RxapValidators = {\n IsNumber,\n IsBoolean,\n IsArray,\n IsDate,\n IsEnum,\n IsInt,\n IsObject,\n IsString,\n IsComplex,\n IsPhoneNumber,\n IsEmail,\n IsUrl,\n IsIP,\n IsPort,\n IsUUID,\n};\n","import {\n Directive,\n Host,\n Input,\n OnDestroy,\n OnInit,\n SkipSelf,\n TemplateRef,\n ViewContainerRef,\n} from '@angular/core';\nimport { ControlContainer } from '@angular/forms';\nimport { Subscription } from 'rxjs';\nimport {\n filter,\n startWith,\n tap,\n} from 'rxjs/operators';\nimport { ValidationErrors } from '../types';\n\n/**\n * @deprecated removed use the rxapControlError or rxapControlErrors directive\n */\n@Directive({\n selector: '[rxapFormControlError]',\n standalone: true,\n})\nexport class FormControlErrorDirective implements OnInit, OnDestroy {\n\n // eslint-disable-next-line @angular-eslint/no-input-rename\n @Input({\n required: true,\n alias: 'rxapFormControlErrorFrom',\n })\n public name!: string;\n\n // eslint-disable-next-line @angular-eslint/no-input-rename\n @Input({\n required: true,\n alias: 'rxapFormControlErrorIf',\n })\n public errorKey!: string;\n\n private subscription?: Subscription;\n\n constructor(\n private readonly template: TemplateRef<{ $implicit: ValidationErrors }>,\n @Host() @SkipSelf() private readonly parent: ControlContainer,\n private readonly viewContainerRef: ViewContainerRef,\n ) {\n }\n\n public ngOnInit() {\n const control = this.parent.control?.get(this.name);\n\n if (!control) {\n throw new Error('Could not extract form control instance');\n }\n\n this.subscription = control.statusChanges.pipe(\n startWith(control.status),\n filter(status => status === 'INVALID'),\n tap(() => {\n this.viewContainerRef.clear();\n if (control.hasError(this.errorKey)) {\n this.viewContainerRef.createEmbeddedView(this.template, { $implicit: control.getError(this.errorKey) });\n }\n }),\n ).subscribe();\n\n }\n\n public ngOnDestroy() {\n this.subscription?.unsubscribe();\n }\n\n}\n","import {\n Directive,\n HostListener,\n} from '@angular/core';\nimport { ControlContainer } from '@angular/forms';\nimport { hasIndexSignature } from '@rxap/utilities';\n\n@Directive({\n selector: '[rxapFormControlMarkDirty]',\n standalone: true,\n})\nexport class FormControlMarkDirtyDirective {\n\n constructor(private readonly parent: ControlContainer) {\n }\n\n @HostListener('click')\n public onClick() {\n const control = this.parent.control;\n\n if (control && hasIndexSignature(control) && typeof control['markAllAsDirty'] === 'function') {\n control['markAllAsDirty']();\n } else {\n control?.markAsDirty();\n }\n\n }\n\n}\n","import {\n Directive,\n HostListener,\n} from '@angular/core';\nimport { ControlContainer } from '@angular/forms';\nimport { hasIndexSignature } from '@rxap/utilities';\n\n@Directive({\n selector: '[rxapFormControlMarkPristine]',\n standalone: true,\n})\nexport class FormControlMarkPristineDirective {\n\n constructor(private readonly parent: ControlContainer) {\n }\n\n @HostListener('click')\n public onClick() {\n const control = this.parent.control;\n\n if (control && hasIndexSignature(control) && typeof control['markAllAsPristine'] === 'function') {\n control['markAllAsPristine']();\n } else {\n control?.markAsPristine();\n }\n }\n\n}\n","import {\n Directive,\n HostListener,\n} from '@angular/core';\nimport { ControlContainer } from '@angular/forms';\n\n@Directive({\n selector: '[rxapFormControlMarkTouched]',\n standalone: true,\n})\nexport class FormControlMarkTouchedDirective {\n\n constructor(private readonly parent: ControlContainer) {\n }\n\n @HostListener('click')\n public onClick() {\n this.parent.control?.markAllAsTouched();\n }\n\n}\n","import {\n Directive,\n HostListener,\n} from '@angular/core';\nimport { ControlContainer } from '@angular/forms';\nimport { hasIndexSignature } from '@rxap/utilities';\n\n@Directive({\n selector: '[rxapFormControlMarkUntouched]',\n standalone: true,\n})\nexport class FormControlMarkUntouchedDirective {\n\n constructor(private readonly parent: ControlContainer) {\n }\n\n @HostListener('click')\n public onClick() {\n const control = this.parent.control;\n\n if (control && hasIndexSignature(control) && typeof control['markAllAsUntouched'] === 'function') {\n control['markAllAsUntouched']();\n } else {\n control?.markAsUntouched();\n }\n }\n\n}\n","import {\n Directive,\n forwardRef,\n Inject,\n Input,\n Optional,\n Self,\n SkipSelf,\n} from '@angular/core';\nimport {\n AsyncValidator,\n AsyncValidatorFn,\n ControlContainer,\n ControlValueAccessor,\n FormControlName,\n NG_ASYNC_VALIDATORS,\n NG_VALIDATORS,\n NG_VALUE_ACCESSOR,\n NgControl,\n Validator,\n ValidatorFn,\n} from '@angular/forms';\nimport { RxapFormControl } from '../form-control';\n\n/**\n * A full exertion of FormControlName from @angular/forms. The only change is the\n * ability to access the control container outside of the current component\n *\n * @deprecated use the ParentControlContainerDirective\n */\n@Directive({\n selector: '[rxapFormControlName]',\n providers: [\n {\n provide: NgControl,\n useExisting: forwardRef(() => FormControlNameDirective),\n },\n ],\n exportAs: 'rxapFormControl',\n standalone: true,\n})\nexport class FormControlNameDirective extends FormControlName {\n\n override readonly control!: RxapFormControl;\n\n @Input('rxapFormControlName')\n public override name!: string | number | null;\n\n constructor(\n @Optional() @SkipSelf() parent: ControlContainer,\n @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator | ValidatorFn>,\n @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:\n Array<AsyncValidator | AsyncValidatorFn>,\n @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[],\n ) {\n super(parent, validators, asyncValidators, valueAccessors, null);\n }\n\n}\n","import {\n Directive,\n forwardRef,\n Inject,\n Input,\n Optional,\n Self,\n SkipSelf,\n} from '@angular/core';\nimport {\n ControlContainer,\n FormGroupName,\n NG_ASYNC_VALIDATORS,\n NG_VALIDATORS,\n} from '@angular/forms';\nimport { RxapFormGroup } from '../form-group';\n\n/**\n * A full exertion of FormGroupName from @angular/forms. The only change is the\n * ability to access the control container outside of the current component\n *\n * @deprecated use the ParentControlContainerDirective\n */\n@Directive({\n selector: '[rxapFormGroupName]',\n providers: [\n {\n provide: ControlContainer,\n useExisting: forwardRef(() => FormGroupNameDirective),\n },\n ],\n exportAs: 'rxapFormGroup',\n standalone: true,\n})\nexport class FormGroupNameDirective extends FormGroupName {\n\n @Input('rxapFormGroupName')\n public override name!: string | number | null;\n\n public override get control(): RxapFormGroup {\n // TODO : add type check\n return this.formDirective!.getFormGroup(this) as any;\n }\n\n constructor(\n @Optional() @SkipSelf() parent: ControlContainer,\n @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],\n @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[],\n ) {\n super(parent, validators, asyncValidators);\n }\n\n}\n","import { InjectionToken } from '@angular/core';\nimport { RxapFormBuilder } from '../form-builder';\nimport { FormDefinition } from '../model';\nimport {\n FormLoadMethod,\n FormSubmitMethod,\n} from './models';\n\nexport const RXAP_FORM_DEFINITION = new InjectionToken<FormDefinition>('rxap/forms/definition');\n\nexport const RXAP_FORM_DEFINITION_BUILDER = new InjectionToken<RxapFormBuilder>('rxap/forms/definition-builder');\n\nexport const RXAP_FORM_SUBMIT_METHOD = new InjectionToken<FormSubmitMethod<any>>('rxap/form/submit-method');\n\nexport const RXAP_FORM_SUBMIT_FAILED_METHOD = new InjectionToken<FormSubmitMethod<any>>('rxap/form/submit-failed-method');\n\nexport const RXAP_FORM_SUBMIT_SUCCESSFUL_METHOD = new InjectionToken<FormSubmitMethod<any>>(\n 'rxap/form/submit-successful-method');\n\nexport const RXAP_FORM_LOAD_METHOD = new InjectionToken<FormLoadMethod>('rxap/form/load-method');\n\nexport const RXAP_FORM_LOAD_FAILED_METHOD = new InjectionToken<FormLoadMethod>('rxap/form/load-failed-method');\n\nexport const RXAP_FORM_LOAD_SUCCESSFUL_METHOD = new InjectionToken<FormLoadMethod>('rxap/form/load-successful-method');\n","export enum MetadataKeys {\n CONTROL_VALIDATORS = 'rxap/forms/control-validators',\n CONTROL_ASYNC_VALIDATORS = 'rxap/forms/control-async-validators',\n CONTROL_CHANGES = 'rxap/forms/control-changes',\n CONTROL_SET_VALUE = 'rxap/forms/control-set-value',\n FORM_ARRAY_GROUPS = 'rxap/forms/form-array-groups',\n FORM_ARRAY_CONTROLS = 'rxap/forms/form-array-controls',\n FORM_GROUPS = 'rxap/forms/form-groups',\n FORM_CONTROLS = 'rxap/forms/form-controls'\n}\n","import {\n defer,\n EMPTY,\n merge,\n Observable,\n of,\n Subscription,\n} from 'rxjs';\nimport {\n distinctUntilChanged,\n map,\n} from 'rxjs/operators';\nimport {\n AbstractControl,\n ControlOptions,\n ControlPath,\n ControlState,\n ValidatorFn,\n} from './types';\nimport {\n coerceArray,\n equals,\n} from '@rxap/utilities';\nimport { RxapFormArray } from './form-array';\nimport { RxapFormGroup } from './form-group';\nimport { RxapFormControl } from './form-control';\n\nfunction getControlValue<T>(control: AbstractControl<T>): T {\n if ((control as any).getRawValue) {\n return (control as any).getRawValue();\n }\n return control.value;\n}\n\nexport function controlValueChanges$<T>(control: AbstractControl<T>): Observable<T> {\n return merge(\n defer(() => of(getControlValue(control))),\n control.valueChanges.pipe(map(() => getControlValue(control))),\n );\n}\n\nexport function controlDisabled$<T>(control: AbstractControl<T>): Observable<boolean> {\n return merge(\n defer(() => of(control.disabled)),\n merge(control.statusChanges, control.stateChanges?.asObservable() ?? EMPTY).pipe(\n map(() => control.disabled),\n distinctUntilChanged(),\n ),\n );\n}\n\nexport function controlEnabled$<T>(control: AbstractControl<T>): Observable<boolean> {\n return merge(\n defer(() => of(control.enabled)),\n merge(control.statusChanges, control.stateChanges?.asObservable() ?? EMPTY).pipe(\n map(() => control.enabled),\n distinctUntilChanged(),\n ),\n );\n}\n\nexport function controlReadonly$<T>(control: AbstractControl<T>): Observable<boolean> {\n return merge(\n defer(() => of(control.readonly ?? false)),\n merge(control.statusChanges, control.stateChanges?.asObservable() ?? EMPTY).pipe(\n map(() => control.readonly ?? false),\n distinctUntilChanged(),\n ),\n );\n}\n\nexport function controlStatusChanges$<T>(control: AbstractControl<T>): Observable<ControlState> {\n return merge(\n defer(() => of(control.status as ControlState)),\n control.statusChanges.pipe(\n map(() => control.status as ControlState),\n distinctUntilChanged(),\n ),\n );\n}\n\nexport function controlErrorChanges$<E>(control: AbstractControl): Observable<E | null> {\n return merge(\n defer(() => of(control.errors as E)),\n control.valueChanges.pipe(\n map(() => control.errors as E),\n ),\n control.statusChanges.pipe(\n map(() => control.errors as E),\n ),\n ).pipe(distinctUntilChanged((a, b) => equals(a, b)));\n}\n\nexport function enableControl<T>(control: AbstractControl<T>, enabled: boolean, opts?: ControlOptions): void {\n if (enabled) {\n control.enable(opts);\n } else {\n control.disable(opts);\n }\n}\n\nexport function disableControl<T>(control: AbstractControl<T>, disabled: boolean, opts?: ControlOptions): void {\n enableControl(control, !disabled, opts);\n}\n\nexport function controlDisabledWhile<T>(\n control: AbstractControl<T>,\n observable: Observable<boolean>,\n opts?: ControlOptions,\n): Subscription {\n return observable.subscribe(isDisabled => disableControl(control, isDisabled, opts));\n}\n\nexport function controlEnabledWhile<T>(\n control: AbstractControl<T>,\n observable: Observable<boolean>,\n opts?: ControlOptions,\n): Subscription {\n return observable.subscribe(isEnabled => enableControl(control, isEnabled, opts));\n}\n\nexport function mergeControlValidators<T, Control extends AbstractControl<T>>(\n control: Control,\n validators: ValidatorFn<T> | ValidatorFn<T>[],\n): void {\n control.setValidators([ ...coerceArray(control.validator), ...coerceArray(validators) ]);\n control.updateValueAndValidity();\n}\n\nexport function validateControlOn<T>(control: AbstractControl<T>, validation: Observable<null | object>): Subscription {\n return validation.subscribe(maybeError => {\n control.setErrors(maybeError);\n });\n}\n\nexport function hasErrorAndTouched<T>(control: AbstractControl<T>, error: string, path?: ControlPath): boolean {\n const hasError = control.hasError(error, !path || path.length === 0 ? undefined : path);\n return hasError && control.touched;\n}\n\nexport function hasErrorAndDirty<T>(control: AbstractControl<T>, error: string, path?: ControlPath): boolean {\n const hasError = control.hasError(error, !path || path.length === 0 ? undefined : path);\n return hasError && control.dirty;\n}\n\nexport function markAllDirty<T>(control: RxapFormArray<T> | RxapFormGroup<T>): void {\n control.markAsDirty({ onlySelf: true });\n (control as any)._forEachChild((_control: any) => _control.markAllAsDirty());\n}\n\nexport function markAllPristine<T>(control: RxapFormArray<T> | RxapFormGroup<T>): void {\n control.markAsPristine({ onlySelf: true });\n (control as any)._forEachChild((_control: any) => _control.markAllAsPristine());\n}\n\nexport function markAllUntouched<T>(control: RxapFormArray<T> | RxapFormGroup<T>): void {\n control.markAsUntouched({ onlySelf: true });\n (control as any)._forEachChild((_control: any) => _control.markAllAsUntouched());\n}\n\nexport function selectControlValue$<T, R>(\n control: RxapFormGroup<T> | RxapFormArray<T> | RxapFormControl<T>,\n mapFn: (state: T) => R,\n): Observable<R> {\n return (control.value$ as Observable<any>).pipe(map(mapFn), distinctUntilChanged());\n}\n","import {\n AbstractControl as NgAbstractControl,\n UntypedFormArray,\n} from '@angular/forms';\nimport {\n AbstractControl,\n AsyncValidator,\n ControlEventOptions,\n ControlOptions,\n ControlPath,\n ControlState,\n EmitEvent,\n ExtractStrings,\n OnlySelf,\n Validator,\n} from './types';\nimport {\n distinctUntilChanged,\n map,\n} from 'rxjs/operators';\nimport {\n isObservable,\n Observable,\n Subject,\n Subscription,\n} from 'rxjs';\nimport {\n controlDisabled$,\n controlDisabledWhile,\n controlEnabled$,\n controlEnabledWhile,\n controlErrorChanges$,\n controlStatusChanges$,\n controlValueChanges$,\n disableControl,\n enableControl,\n hasErrorAndDirty,\n hasErrorAndTouched,\n markAllDirty,\n markAllPristine,\n markAllUntouched,\n mergeControlValidators,\n} from './control-actions';\nimport { coerceArray } from '@rxap/utilities';\nimport {\n ControlInsertedFn,\n ControlRemovedFn,\n FormArrayOptions,\n FormBuilderFn,\n FormDefinition,\n FormType,\n} from './model';\nimport { isDevMode } from '@angular/core';\n\nexport class RxapFormArray<T = any,\n E extends object = any,\n Parent extends object = any>\n extends UntypedFormArray\n implements AbstractControl<T[]> {\n\n private _readonly = false;\n\n public get readonly(): boolean {\n return (this.parent as any)?.readonly ?? this._readonly;\n }\n\n public set readonly(value: boolean) {\n this._readonly = value;\n this.controls.forEach(control => (control as any).stateChanges?.next());\n }\n\n /**\n * @internal\n */\n public get rxapFormDefinition():\n | (FormType<Parent> & FormDefinition<Parent>)\n | undefined {\n return (this.parent as any).rxapFormDefinition;\n }\n\n public get controlPath(): string {\n const parent: any = this.parent;\n if (parent) {\n if (parent.controlPath) {\n if (parent === this.root) {\n return this.controlId;\n } else {\n return [ parent.controlPath, this.controlId ].join('.');\n }\n }\n }\n return this.controlId;\n }\n\n public override readonly value!: T[];\n\n public get fullControlPath(): string {\n const parent: any = this.parent;\n if (parent) {\n if (parent.fullControlPath) {\n return [ parent.fullControlPath, this.controlId ].join('.');\n }\n }\n return this.controlId;\n }\n\n public override readonly valueChanges!: Observable<T[]>;\n\n public readonly value$ = controlValueChanges$<T[]>(this);\n // @ts-expect-error overwrite the public type\n public override readonly status!: ControlState;\n public readonly disabled$ = controlDisabled$(this);\n public override readonly statusChanges!: Observable<ControlState>;\n public readonly enabled$ = controlEnabled$(this);\n public override readonly errors!: E | null;\n public readonly status$ = controlStatusChanges$(this);\n public readonly errors$ = controlErrorChanges$<E>(this);\n public readonly controlId: string;\n private readonly touchChanges = new Subject<boolean>();\n public readonly touch$ = this.touchChanges\n .asObservable()\n .pipe(distinctUntilChanged());\n private readonly dirtyChanges = new Subject<boolean>();\n public readonly dirty$ = this.dirtyChanges\n .asObservable()\n .pipe(distinctUntilChanged());\n private readonly _builder: FormBuilderFn;\n private readonly _controlInsertedFn: ControlInsertedFn;\n private readonly _controlRemovedFn: ControlRemovedFn;\n\n constructor(\n public override controls: Array<AbstractControl<T>>,\n options: FormArrayOptions,\n ) {\n super(controls, options);\n this.controlId = options.controlId;\n this._builder = options.builder;\n this._controlInsertedFn = options.controlInsertedFn;\n this._controlRemovedFn = options.controlRemovedFn;\n }\n\n public select<R>(mapFn: (state: T[]) => R): Observable<R> {\n return this.value$.pipe(map(mapFn), distinctUntilChanged());\n }\n\n public override at(index: number): AbstractControl<T> {\n return super.at(index) as AbstractControl<T>;\n }\n\n public override getRawValue(): T[] {\n return super.getRawValue();\n }\n\n public override insert(index: number, control: AbstractControl<T>): void {\n if (isDevMode()) {\n console.warn('It is not recommend to use the FormArray.insert method');\n }\n return super.insert(index, control);\n }\n\n /**\n * inserts a new control at the specified index. If the index is undefined\n * the new control will be added to the end.\n *\n * @param index (optional) the index where the control should be created\n * @param state (optional) the initial state of the new control\n * @param options (optional) ControlEventOptions\n */\n public insertAt(index?: number, state?: T, options?: ControlEventOptions): void {\n const insertIndex = index ?? this.controls.length;\n const controlOrDefinition = this._builder(state, {\n controlId: insertIndex.toFixed(0),\n });\n\n this._controlInsertedFn(insertIndex, controlOrDefinition);\n\n if (insertIndex < this.controls.length) {\n // update the control ids for all controls, that are moved.\n for (let i = insertIndex; i < this.controls.length; i++) {\n Reflect.set(this.controls[i], 'controlId', (i + 1).toFixed(0));\n }\n }\n\n // call the super insert after the update, bc the insert method will\n // trigger a change detection\n if (controlOrDefinition instanceof NgAbstractControl) {\n super.insert(insertIndex, controlOrDefinition, options);\n } else {\n super.insert(insertIndex, controlOrDefinition.rxapFormGroup!, options);\n }\n }\n\n public disabledWhile(\n observable: Observable<boolean>,\n options?: ControlOptions,\n ) {\n return controlDisabledWhile(this, observable, options);\n }\n\n public enabledWhile(\n observable: Observable<boolean>,\n options?: ControlOptions,\n ) {\n return controlEnabledWhile(this, observable, options);\n }\n\n public mergeValidators(validators: Validator) {\n mergeControlValidators(this, validators);\n }\n\n public mergeAsyncValidators(validators: AsyncValidator) {\n this.setAsyncValidators([\n ...coerceArray(this.asyncValidator),\n ...coerceArray(validators),\n ]);\n this.updateValueAndValidity();\n }\n\n public markAllAsDirty(): void {\n markAllDirty(this);\n }\n\n public markAllAsPristine(): void {\n markAllPristine(this);\n }\n\n public override setValue(\n valueOrObservable: T[] | Observable<T[]>,\n options?: ControlEventOptions,\n ): Subscription | void {\n if (isObservable(valueOrObservable)) {\n return valueOrObservable.subscribe((value) => {\n super.setValue(value, options);\n },\n );\n }\n\n super.setValue(valueOrObservable, options);\n }\n\n public markAllAsUntouched(): void {\n markAllUntouched(this);\n }\n\n public validateOn(observableValidation: Observable<null | object>) {\n return observableValidation.subscribe((maybeError) => {\n this.setErrors(maybeError);\n });\n }\n\n public hasErrorAndTouched(\n errorCode: ExtractStrings<E>,\n path?: ControlPath,\n ): boolean {\n return hasErrorAndTouched(this, errorCode, path);\n }\n\n private _patchValue(value: T[], options?: ControlEventOptions) {\n // Even though the `value` argument type doesn't allow `null` and `undefined` values, the\n // `patchValue` can be called recursively and inner data structures might have these values, so\n // we just ignore such cases when a field containing FormArray instance receives `null` or\n // `undefined` as a value.\n if (value == null /* both `null` and `undefined` */) {\n return;\n }\n if (options?.strict) {\n if (this.length > value.length) {\n for (let index = this.length - 1; index >= value.length; index--) {\n this.removeAt(index);\n }\n }\n }\n value.forEach((newValue, index) => {\n if (this.at(index)) {\n this.at(index)\n .patchValue(\n newValue,\n {\n ...(options ?? {}),\n onlySelf: true,\n },\n );\n } else if (options?.coerce) {\n this.insertAt(index, value[index]);\n }\n });\n this.updateValueAndValidity(options);\n }\n\n public override patchValue(\n valueOrObservable: any,\n options?: ControlEventOptions,\n ): Subscription | void {\n if (isObservable(valueOrObservable)) {\n return (valueOrObservable as Observable<T[]>).subscribe((value: T[]) => {\n this._patchValue(value, options);\n });\n }\n\n this._patchValue(valueOrObservable as T[], options);\n }\n\n public hasErrorAndDirty(\n errorCode: ExtractStrings<E>,\n path?: ControlPath,\n ): boolean {\n return hasErrorAndDirty(this, errorCode, path);\n }\n\n public override removeAt(index: number) {\n if (isDevMode()) {\n console.warn('It is not recommend to use the FormArray.removeAt method');\n }\n super.removeAt(index);\n this._controlRemovedFn(index);\n }\n\n public setEnable(enable = true, opts?: ControlEventOptions) {\n enableControl(this, enable, opts);\n }\n\n public override push(control: AbstractControl<T>, options?: ControlEventOptions): void {\n if (isDevMode()) {\n console.warn('It is not recommend to use the FormArray.push method');\n }\n return super.push(control, options);\n }\n\n public setDisable(disable = true, opts?: ControlEventOptions) {\n disableControl(this, disable, opts);\n }\n\n public override setControl(index: number, control: AbstractControl<T>, options?: ControlEventOptions): void {\n if (isDevMode()) {\n console.warn('It is not recommend to use the FormArray.setControl method');\n }\n return super.setControl(index, control, options);\n }\n\n public override markAsTouched(opts?: OnlySelf): void {\n super.markAsTouched(opts);\n this.touchChanges.next(true);\n }\n\n public override markAsUntouched(opts?: OnlySelf): void {\n super.markAsUntouched(opts);\n this.touchChanges.next(false);\n }\n\n public override markAsPristine(opts?: OnlySelf): void {\n super.markAsPristine(opts);\n this.dirtyChanges.next(false);\n }\n\n public override markAsDirty(opts?: OnlySelf): void {\n super.markAsDirty(opts);\n this.dirtyChanges.next(true);\n }\n\n public override reset(value?: T[], options?: ControlEventOptions): void {\n super.reset(value, options);\n }\n\n public override setValidators(\n newValidator: Validator,\n updateValueAndValidity = true,\n ): void {\n super.setValidators(newValidator);\n if (updateValueAndValidity) {\n super.updateValueAndValidity();\n }\n }\n\n public override setAsyncValidators(\n newValidator: AsyncValidator,\n updateValueAndValidity = true,\n ): void {\n super.setAsyncValidators(newValidator);\n if (updateValueAndValidity) {\n super.updateValueAndValidity();\n }\n }\n\n public override hasError(errorCode: ExtractStrings<E>, path?: ControlPath) {\n return super.hasError(errorCode, path);\n }\n\n public override setErrors(errors: Partial<E> | null, opts: EmitEvent = {}) {\n return super.setErrors(errors, opts);\n }\n\n public override getError<K extends ExtractStrings<E>>(\n errorCode: K,\n path?: ControlPath,\n ) {\n return super.getError(errorCode, path) as E[K] | null;\n }\n}\n","import {\n isObservable,\n Observable,\n Subject,\n Subscription,\n} from 'rxjs';\nimport { distinctUntilChanged } from 'rxjs/operators';\nimport { UntypedFormGroup } from '@angular/forms';\nimport {\n AbstractControl,\n AsyncValidator,\n ControlEventOptions,\n ControlOptions,\n ControlState,\n EmitEvent,\n ExtractAbstractControl,\n ExtractStrings,\n KeyValueControls,\n OnlySelf,\n ValidationErrors,\n Validator,\n} from './types';\nimport {\n controlDisabled$,\n controlDisabledWhile,\n controlEnabled$,\n controlEnabledWhile,\n controlErrorChanges$,\n controlStatusChanges$,\n controlValueChanges$,\n disableControl,\n enableControl,\n hasErrorAndDirty,\n hasErrorAndTouched,\n markAllDirty,\n markAllPristine,\n markAllUntouched,\n mergeControlValidators,\n selectControlValue$,\n validateControlOn,\n} from './control-actions';\nimport { coerceArray } from '@rxap/utilities';\nimport {\n FormDefinition,\n FormGroupOptions,\n FormType,\n} from './model';\n\nexport class RxapFormGroup<\n T = any,\n E extends ValidationErrors = any\n> extends UntypedFormGroup {\n /**\n * @internal\n */\n public get rxapFormDefinition():\n | (FormType<T> & FormDefinition<T>)\n | undefined {\n if (!this.parent) {\n return this._rxapFormDefinition;\n }\n if (this._rxapFormDefinition) {\n return this._rxapFormDefinition;\n }\n return (this.parent as any).rxapFormDefinition;\n }\n\n private _readonly = false;\n\n public get readonly(): boolean {\n return (this.parent as any)?.readonly ?? this._readonly;\n }\n\n public set readonly(value: boolean) {\n this._readonly = value;\n Object.values(this.controls ?? {}).forEach(control => (control as any).stateChanges?.next());\n }\n\n /**\n * @internal\n */\n private _rxapFormDefinition?: FormType<T> & FormDefinition<T>;\n\n override readonly value!: T;\n override readonly errors!: E | null;\n override readonly valueChanges!: Observable<T>;\n // @ts-expect-error overwrite the public type\n override readonly status!: ControlState;\n override readonly statusChanges!: Observable<ControlState>;\n\n private touchChanges = new Subject<boolean>();\n private dirtyChanges = new Subject<boolean>();\n\n touch$ = this.touchChanges.asObservable().pipe(distinctUntilChanged());\n dirty$ = this.dirtyChanges.asObservable().pipe(distinctUntilChanged());\n\n readonly value$: Observable<T> = controlValueChanges$<T>(this);\n readonly disabled$ = controlDisabled$<T>(this);\n readonly enabled$ = controlEnabled$<T>(this);\n readonly status$ = controlStatusChanges$<T>(this);\n readonly errors$ = controlErrorChanges$<E>(this);\n\n readonly controlId: string;\n\n public get controlPath(): string {\n const parent: any = this.parent;\n if (parent) {\n if (parent.controlPath) {\n if (parent === this.root) {\n return this.controlId;\n } else {\n return [ parent.controlPath, this.controlId ].join('.');\n }\n }\n }\n return '';\n }\n\n public get fullControlPath(): string {\n const parent: any = this.parent;\n if (parent) {\n if (parent.fullControlPath) {\n return [ parent.fullControlPath, this.controlId ].join('.');\n }\n }\n return this.controlId;\n }\n\n constructor(\n public override controls: ExtractAbstractControl<KeyValueControls<T>, T>,\n options: FormGroupOptions,\n ) {\n super(controls, options);\n this.controlId = options.controlId;\n }\n\n public select<R>(mapFn: (state: T) => R): Observable<R> {\n return selectControlValue$<T, R>(this, mapFn);\n }\n\n public override getRawValue(): T {\n return super.getRawValue();\n }\n\n public override get<K1 extends keyof T>(path: [ K1 ]): AbstractControl<T[K1]>;\n public override get<K1 extends keyof T, K2 extends keyof T[K1]>(\n path: [ K1, K2 ],\n ): AbstractControl<T[K1][K2]>;\n public override get<\n K1 extends keyof T,\n K2 extends keyof T[K1],\n K3 extends keyof T[K1][K2]\n >(path: [ K1, K2, K3 ]): AbstractControl<T[K1][K2][K3]>;\n public override get(path: string): AbstractControl;\n public override get(path: any): AbstractControl | null {\n return super.get(path);\n }\n\n public getControl<P1 extends keyof T>(prop1: P1): AbstractControl<T[P1]>;\n public getControl<P1 extends keyof T, P2 extends keyof T[P1]>(\n prop1: P1,\n prop2: P2,\n ): AbstractControl<T[P1][P2]>;\n public getControl<\n P1 extends keyof T,\n P2 extends keyof T[P1],\n P3 extends keyof T[P1][P2]\n >(prop1: P1, prop2: P2, prop3: P3): AbstractControl<T[P1][P2][P3]>;\n public getControl<\n P1 extends keyof T,\n P2 extends keyof T[P1],\n P3 extends keyof T[P1][P2],\n P4 extends keyof T[P1][P2][P3]\n >(\n prop1: P1,\n prop2: P2,\n prop3: P3,\n prop4: P4,\n ): AbstractControl<T[P1][P2][P3][P4]>;\n public getControl(...names: any): AbstractControl<any> {\n return this.get(names.join('.'));\n }\n\n public over