@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,388 lines (1,359 loc) • 121 kB
JavaScript
import { isEmail, isIP, isMobilePhone, isPort, isURL, isUUID } from '@rxap/validator';
import * as i0 from '@angular/core';
import { Directive, Host, SkipSelf, Input, HostListener, forwardRef, Optional, Self, Inject, InjectionToken, isDevMode, Injector, input, EventEmitter, ChangeDetectorRef, Output, HostBinding, TemplateRef, ViewContainerRef, NgModule } from '@angular/core';
import * as i1 from '@angular/forms';
import { FormControlName, NG_VALIDATORS, NG_ASYNC_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, FormGroupName, ControlContainer, UntypedFormArray, AbstractControl, UntypedFormGroup, UntypedFormControl, Validators, FormGroupDirective } from '@angular/forms';
import { startWith, filter, tap, map, distinctUntilChanged, debounceTime, take } from 'rxjs/operators';
import { hasIndexSignature, equals, coerceArray, HasRxapOnInitMethod, assertIsArray, assertIsObject, assertIsFunction, isPromise, isObject, clone, coerceBoolean } from '@rxap/utilities';
import { ToggleSubject } from '@rxap/rxjs';
import * as i2 from '@rxap/services';
import { LoadingIndicatorService } from '@rxap/services';
import { merge, defer, of, EMPTY, Subject, isObservable, BehaviorSubject, Subscription } from 'rxjs';
import { GetDefinitionMetadata, DefinitionMetadata } from '@rxap/definition';
import { getMetadata, setMetadataMapSet, setMetadataMap } from '@rxap/reflect-metadata';
import { ConfirmClick } from '@rxap/directives';
import * as i2$1 from '@angular/router';
import { Router } from '@angular/router';
function IsArray({ message } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!Array.isArray(control.value)) {
return {
isArray: {
expected: 'A array value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsBoolean({ message } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(control.value instanceof Boolean || typeof control.value === 'boolean')) {
return {
isBoolean: {
expected: 'A boolean value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsString({ message } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(control.value instanceof String || typeof control.value === 'string')) {
return {
isString: {
expected: 'A string value',
actual: control.value,
message,
},
};
}
return null;
};
}
/**
* Checks if a given value is a number.
*/
function isNumber(value, options = {}) {
if (typeof value !== 'number') {
return false;
}
if (value === Infinity || value === -Infinity) {
return options.allowInfinity ?? false;
}
if (Number.isNaN(value)) {
return options.allowNaN ?? false;
}
if (options.maxDecimalPlaces !== undefined) {
let decimalPlaces = 0;
if (value % 1 !== 0) {
decimalPlaces = value.toString().split('.')[1].length;
}
if (decimalPlaces > options.maxDecimalPlaces) {
return false;
}
}
return Number.isFinite(value);
}
/**
* @deprecated use RxapValidators.isNumber() instead
* @param control
* @constructor
*/
function IsNumber(control) {
if (control.value === null) {
return null;
}
if (isNaN(Number(control.value))) {
return {
isNumber: {
expected: 'A number or a string representing a number',
actual: control.value,
},
};
}
return null;
}
function _IsNumber({ message, options, } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!isNumber(!options?.strict && typeof control.value === 'string' ? Number(control.value) : control.value, options)) {
return {
isNumber: {
expected: 'A number value',
actual: control.value,
message,
},
};
}
return null;
};
}
const regexOptions = {
uppercase: '.*[A-Z]',
special: '.*[^A-Za-z0-9]',
digit: '.*[0-9]',
lowercase: '.*[a-z]',
upperLower: '.*[a-zA-Z]',
alphaNumeric: '.*[a-zA-Z0-9]',
};
const lengthOptions = {
min: '.{n,}',
max: '.{0,n}',
range: '.{min,max}',
exact: '.{n}',
no_limit: '.*',
};
function inOperator(k, o) {
return k in o;
}
function create(options) {
let regex = '^';
for (const [key, value] of Object.entries(regexOptions)) {
if (inOperator(key, options) && isNumber(options[key])) {
regex += '(?=' + value.repeat(options[key]) + ')';
}
}
if (isNumber(options.min) && isNumber(options.max)) {
regex += lengthOptions.range.replace('min', options.min.toFixed(0)).replace('max', options.max.toFixed(0));
}
else if (isNumber(options.max)) {
regex += lengthOptions.max.replace('n', options.max.toFixed(0));
}
else if (isNumber(options.min)) {
regex += lengthOptions.min.replace('n', options.min.toFixed(0));
}
else if (isNumber(options.exact)) {
regex += lengthOptions.exact.replace('n', options.exact.toFixed(0));
}
else {
regex += lengthOptions.no_limit;
}
regex += '$';
return regex;
}
function check(str, regexStringOrOptions) {
let regexString;
if (typeof regexStringOrOptions === 'object') {
regexString = create(regexStringOrOptions);
}
else {
regexString = regexStringOrOptions;
}
return new RegExp(regexString).test(str);
}
function checkError(str, options) {
const returnObject = {};
for (const [key, value] of Object.entries(options)) {
returnObject[key] = check(str, { [key]: value });
}
return returnObject;
}
function IsComplex({ message, options, }) {
return (control) => {
if (control.value === null) {
return null;
}
const isNotString = IsString({ message })(control);
if (isNotString !== null) {
return isNotString;
}
const errors = checkError(control.value, options);
if (Object.keys(errors).length && Object.values(errors).some(item => !item)) {
return {
isComplex: {
expected: options,
actual: errors,
message,
},
};
}
return null;
};
}
function IsDate({ message } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(control.value instanceof Date && !isNaN(control.value.getTime()))) {
return {
isDate: {
expected: 'A date value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsEmail({ message, options, } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(typeof control.value === 'string' && isEmail(control.value, options))) {
return {
isEmail: {
expected: 'A email value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsEnum({ message, entity, }) {
return (control) => {
if (control.value === null) {
return null;
}
if (Object.keys(entity).map(k => entity[k]).indexOf(control.value) === -1) {
return {
isEnum: {
expected: 'A enum value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsInt({ message, options, } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!isNumber(control.value, options) || Number.isInteger(control.value)) {
return {
isInt: {
expected: 'A int value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsIP({ message, version, } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(typeof control.value === 'string' && isIP(control.value, version))) {
return {
isIp: {
expected: `A valid IPv${version ?? '4'} value`,
actual: control.value,
message,
},
};
}
return null;
};
}
function IsObject({ message } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(control.value !==
undefined &&
(typeof control.value === 'object' || typeof control.value === 'function') &&
!Array.isArray(control.value))) {
return {
isObject: {
expected: 'A object value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsPhoneNumber({ message, locale, options, } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!isMobilePhone(control.value, locale, options)) {
return {
isPhoneNumber: {
expected: 'A phone number value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsPort({ message } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(typeof control.value === 'string' && isPort(control.value))) {
return {
isPort: {
expected: `A valid port number between 1 and 65535`,
actual: control.value,
message,
},
};
}
return null;
};
}
function IsUrl({ message, options, } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(typeof control.value === 'string' && isURL(control.value, options))) {
return {
isURL: {
expected: 'A url value',
actual: control.value,
message,
},
};
}
return null;
};
}
function IsUUID({ message, version = 'all', } = {}) {
return (control) => {
if (control.value === null) {
return null;
}
if (!(typeof control.value === 'string' && isUUID(control.value, version))) {
return {
isUuid: {
expected: version === 'all' ? `A valid uuid` : `A valid uuid version ${version}`,
actual: control.value,
message,
},
};
}
return null;
};
}
const RxapValidators = {
IsNumber: _IsNumber,
IsBoolean,
IsArray,
IsDate,
IsEnum,
IsInt,
IsObject,
IsString,
IsComplex,
IsPhoneNumber,
IsEmail,
IsUrl,
IsIP,
IsPort,
IsUUID,
};
/**
* @deprecated removed use the rxapControlError or rxapControlErrors directive
*/
class FormControlErrorDirective {
constructor(template, parent, viewContainerRef) {
this.template = template;
this.parent = parent;
this.viewContainerRef = viewContainerRef;
}
ngOnInit() {
const control = this.parent.control?.get(this.name);
if (!control) {
throw new Error('Could not extract form control instance');
}
this.subscription = control.statusChanges.pipe(startWith(control.status), filter(status => status === 'INVALID'), tap(() => {
this.viewContainerRef.clear();
if (control.hasError(this.errorKey)) {
this.viewContainerRef.createEmbeddedView(this.template, { $implicit: control.getError(this.errorKey) });
}
})).subscribe();
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlErrorDirective, deps: [{ token: i0.TemplateRef }, { token: i1.ControlContainer, host: true, skipSelf: true }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormControlErrorDirective, isStandalone: true, selector: "[rxapFormControlError]", inputs: { name: ["rxapFormControlErrorFrom", "name"], errorKey: ["rxapFormControlErrorIf", "errorKey"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlErrorDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormControlError]',
standalone: true,
}]
}], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i1.ControlContainer, decorators: [{
type: Host
}, {
type: SkipSelf
}] }, { type: i0.ViewContainerRef }], propDecorators: { name: [{
type: Input,
args: [{
required: true,
alias: 'rxapFormControlErrorFrom',
}]
}], errorKey: [{
type: Input,
args: [{
required: true,
alias: 'rxapFormControlErrorIf',
}]
}] } });
class FormControlMarkDirtyDirective {
constructor(parent) {
this.parent = parent;
}
onClick() {
const control = this.parent.control;
if (control && hasIndexSignature(control) && typeof control['markAllAsDirty'] === 'function') {
control['markAllAsDirty']();
}
else {
control?.markAsDirty();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkDirtyDirective, deps: [{ token: i1.ControlContainer }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormControlMarkDirtyDirective, isStandalone: true, selector: "[rxapFormControlMarkDirty]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkDirtyDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormControlMarkDirty]',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.ControlContainer }], propDecorators: { onClick: [{
type: HostListener,
args: ['click']
}] } });
class FormControlMarkPristineDirective {
constructor(parent) {
this.parent = parent;
}
onClick() {
const control = this.parent.control;
if (control && hasIndexSignature(control) && typeof control['markAllAsPristine'] === 'function') {
control['markAllAsPristine']();
}
else {
control?.markAsPristine();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkPristineDirective, deps: [{ token: i1.ControlContainer }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormControlMarkPristineDirective, isStandalone: true, selector: "[rxapFormControlMarkPristine]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkPristineDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormControlMarkPristine]',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.ControlContainer }], propDecorators: { onClick: [{
type: HostListener,
args: ['click']
}] } });
class FormControlMarkTouchedDirective {
constructor(parent) {
this.parent = parent;
}
onClick() {
this.parent.control?.markAllAsTouched();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkTouchedDirective, deps: [{ token: i1.ControlContainer }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormControlMarkTouchedDirective, isStandalone: true, selector: "[rxapFormControlMarkTouched]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkTouchedDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormControlMarkTouched]',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.ControlContainer }], propDecorators: { onClick: [{
type: HostListener,
args: ['click']
}] } });
class FormControlMarkUntouchedDirective {
constructor(parent) {
this.parent = parent;
}
onClick() {
const control = this.parent.control;
if (control && hasIndexSignature(control) && typeof control['markAllAsUntouched'] === 'function') {
control['markAllAsUntouched']();
}
else {
control?.markAsUntouched();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkUntouchedDirective, deps: [{ token: i1.ControlContainer }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormControlMarkUntouchedDirective, isStandalone: true, selector: "[rxapFormControlMarkUntouched]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlMarkUntouchedDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormControlMarkUntouched]',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.ControlContainer }], propDecorators: { onClick: [{
type: HostListener,
args: ['click']
}] } });
/**
* A full exertion of FormControlName from @angular/forms. The only change is the
* ability to access the control container outside of the current component
*
* @deprecated use the ParentControlContainerDirective
*/
class FormControlNameDirective extends FormControlName {
constructor(parent, validators, asyncValidators, valueAccessors) {
super(parent, validators, asyncValidators, valueAccessors, null);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlNameDirective, deps: [{ token: i1.ControlContainer, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: NG_VALUE_ACCESSOR, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormControlNameDirective, isStandalone: true, selector: "[rxapFormControlName]", inputs: { name: ["rxapFormControlName", "name"] }, providers: [
{
provide: NgControl,
useExisting: forwardRef(() => FormControlNameDirective),
},
], exportAs: ["rxapFormControl"], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormControlNameDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormControlName]',
providers: [
{
provide: NgControl,
useExisting: forwardRef(() => FormControlNameDirective),
},
],
exportAs: 'rxapFormControl',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.ControlContainer, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }, { type: Array, decorators: [{
type: Optional
}, {
type: Self
}, {
type: Inject,
args: [NG_VALIDATORS]
}] }, { type: Array, decorators: [{
type: Optional
}, {
type: Self
}, {
type: Inject,
args: [NG_ASYNC_VALIDATORS]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Self
}, {
type: Inject,
args: [NG_VALUE_ACCESSOR]
}] }], propDecorators: { name: [{
type: Input,
args: ['rxapFormControlName']
}] } });
/**
* A full exertion of FormGroupName from @angular/forms. The only change is the
* ability to access the control container outside of the current component
*
* @deprecated use the ParentControlContainerDirective
*/
class FormGroupNameDirective extends FormGroupName {
get control() {
// TODO : add type check
return this.formDirective.getFormGroup(this);
}
constructor(parent, validators, asyncValidators) {
super(parent, validators, asyncValidators);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormGroupNameDirective, deps: [{ token: i1.ControlContainer, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: FormGroupNameDirective, isStandalone: true, selector: "[rxapFormGroupName]", inputs: { name: ["rxapFormGroupName", "name"] }, providers: [
{
provide: ControlContainer,
useExisting: forwardRef(() => FormGroupNameDirective),
},
], exportAs: ["rxapFormGroup"], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: FormGroupNameDirective, decorators: [{
type: Directive,
args: [{
selector: '[rxapFormGroupName]',
providers: [
{
provide: ControlContainer,
useExisting: forwardRef(() => FormGroupNameDirective),
},
],
exportAs: 'rxapFormGroup',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.ControlContainer, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Self
}, {
type: Inject,
args: [NG_VALIDATORS]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Self
}, {
type: Inject,
args: [NG_ASYNC_VALIDATORS]
}] }], propDecorators: { name: [{
type: Input,
args: ['rxapFormGroupName']
}] } });
const RXAP_FORM_DEFINITION = new InjectionToken('rxap/forms/definition');
const RXAP_FORM_DEFINITION_BUILDER = new InjectionToken('rxap/forms/definition-builder');
const RXAP_FORM_SUBMIT_METHOD = new InjectionToken('rxap/form/submit-method');
const RXAP_FORM_SUBMIT_FAILED_METHOD = new InjectionToken('rxap/form/submit-failed-method');
const RXAP_FORM_SUBMIT_SUCCESSFUL_METHOD = new InjectionToken('rxap/form/submit-successful-method');
const RXAP_FORM_LOAD_METHOD = new InjectionToken('rxap/form/load-method');
const RXAP_FORM_LOAD_FAILED_METHOD = new InjectionToken('rxap/form/load-failed-method');
const RXAP_FORM_LOAD_SUCCESSFUL_METHOD = new InjectionToken('rxap/form/load-successful-method');
var MetadataKeys;
(function (MetadataKeys) {
MetadataKeys["CONTROL_VALIDATORS"] = "rxap/forms/control-validators";
MetadataKeys["CONTROL_ASYNC_VALIDATORS"] = "rxap/forms/control-async-validators";
MetadataKeys["CONTROL_CHANGES"] = "rxap/forms/control-changes";
MetadataKeys["CONTROL_SET_VALUE"] = "rxap/forms/control-set-value";
MetadataKeys["FORM_ARRAY_GROUPS"] = "rxap/forms/form-array-groups";
MetadataKeys["FORM_ARRAY_CONTROLS"] = "rxap/forms/form-array-controls";
MetadataKeys["FORM_GROUPS"] = "rxap/forms/form-groups";
MetadataKeys["FORM_CONTROLS"] = "rxap/forms/form-controls";
})(MetadataKeys || (MetadataKeys = {}));
function getControlValue(control) {
if (control.getRawValue) {
return control.getRawValue();
}
return control.value;
}
function controlValueChanges$(control) {
return merge(defer(() => of(getControlValue(control))), control.valueChanges.pipe(map(() => getControlValue(control))));
}
function controlDisabled$(control) {
return merge(defer(() => of(control.disabled)), merge(control.statusChanges, control.stateChanges?.asObservable() ?? EMPTY).pipe(map(() => control.disabled), distinctUntilChanged()));
}
function controlEnabled$(control) {
return merge(defer(() => of(control.enabled)), merge(control.statusChanges, control.stateChanges?.asObservable() ?? EMPTY).pipe(map(() => control.enabled), distinctUntilChanged()));
}
function controlReadonly$(control) {
return merge(defer(() => of(control.readonly ?? false)), merge(control.statusChanges, control.stateChanges?.asObservable() ?? EMPTY).pipe(map(() => control.readonly ?? false), distinctUntilChanged()));
}
function controlStatusChanges$(control) {
return merge(defer(() => of(control.status)), control.statusChanges.pipe(map(() => control.status), distinctUntilChanged()));
}
function controlErrorChanges$(control) {
return merge(defer(() => of(control.errors)), control.valueChanges.pipe(map(() => control.errors)), control.statusChanges.pipe(map(() => control.errors))).pipe(distinctUntilChanged((a, b) => equals(a, b)));
}
function enableControl(control, enabled, opts) {
if (enabled) {
control.enable(opts);
}
else {
control.disable(opts);
}
}
function disableControl(control, disabled, opts) {
enableControl(control, !disabled, opts);
}
function controlDisabledWhile(control, observable, opts) {
return observable.subscribe(isDisabled => disableControl(control, isDisabled, opts));
}
function controlEnabledWhile(control, observable, opts) {
return observable.subscribe(isEnabled => enableControl(control, isEnabled, opts));
}
function mergeControlValidators(control, validators) {
control.setValidators([...coerceArray(control.validator), ...coerceArray(validators)]);
control.updateValueAndValidity();
}
function validateControlOn(control, validation) {
return validation.subscribe(maybeError => {
control.setErrors(maybeError);
});
}
function hasErrorAndTouched(control, error, path) {
const hasError = control.hasError(error, !path || path.length === 0 ? undefined : path);
return hasError && control.touched;
}
function hasErrorAndDirty(control, error, path) {
const hasError = control.hasError(error, !path || path.length === 0 ? undefined : path);
return hasError && control.dirty;
}
function markAllDirty(control) {
control.markAsDirty({ onlySelf: true });
control._forEachChild((_control) => _control.markAllAsDirty());
}
function markAllPristine(control) {
control.markAsPristine({ onlySelf: true });
control._forEachChild((_control) => _control.markAllAsPristine());
}
function markAllUntouched(control) {
control.markAsUntouched({ onlySelf: true });
control._forEachChild((_control) => _control.markAllAsUntouched());
}
function selectControlValue$(control, mapFn) {
return control.value$.pipe(map(mapFn), distinctUntilChanged());
}
class RxapFormArray extends UntypedFormArray {
get readonly() {
return this.parent?.readonly ?? this._readonly;
}
set readonly(value) {
this._readonly = value;
this.controls.forEach(control => control.stateChanges?.next());
}
/**
* @internal
*/
get rxapFormDefinition() {
return this.parent.rxapFormDefinition;
}
get controlPath() {
const parent = this.parent;
if (parent) {
if (parent.controlPath) {
if (parent === this.root) {
return this.controlId;
}
else {
return [parent.controlPath, this.controlId].join('.');
}
}
}
return this.controlId;
}
get fullControlPath() {
const parent = this.parent;
if (parent) {
if (parent.fullControlPath) {
return [parent.fullControlPath, this.controlId].join('.');
}
}
return this.controlId;
}
constructor(controls, options) {
super(controls, options);
this.controls = controls;
this._readonly = false;
this.value$ = controlValueChanges$(this);
this.disabled$ = controlDisabled$(this);
this.enabled$ = controlEnabled$(this);
this.status$ = controlStatusChanges$(this);
this.errors$ = controlErrorChanges$(this);
this.touchChanges = new Subject();
this.touch$ = this.touchChanges
.asObservable()
.pipe(distinctUntilChanged());
this.dirtyChanges = new Subject();
this.dirty$ = this.dirtyChanges
.asObservable()
.pipe(distinctUntilChanged());
this.controlId = options.controlId;
this._builder = options.builder;
this._controlInsertedFn = options.controlInsertedFn;
this._controlRemovedFn = options.controlRemovedFn;
}
select(mapFn) {
return this.value$.pipe(map(mapFn), distinctUntilChanged());
}
at(index) {
return super.at(index);
}
getRawValue() {
return super.getRawValue();
}
insert(index, control) {
if (isDevMode()) {
console.warn('It is not recommend to use the FormArray.insert method');
}
return super.insert(index, control);
}
/**
* inserts a new control at the specified index. If the index is undefined
* the new control will be added to the end.
*
* @param index (optional) the index where the control should be created
* @param state (optional) the initial state of the new control
* @param options (optional) ControlEventOptions
*/
insertAt(index, state, options) {
const insertIndex = index ?? this.controls.length;
const controlOrDefinition = this._builder(state, {
controlId: insertIndex.toFixed(0),
});
this._controlInsertedFn(insertIndex, controlOrDefinition);
if (insertIndex < this.controls.length) {
// update the control ids for all controls, that are moved.
for (let i = insertIndex; i < this.controls.length; i++) {
Reflect.set(this.controls[i], 'controlId', (i + 1).toFixed(0));
}
}
// call the super insert after the update, bc the insert method will
// trigger a change detection
if (controlOrDefinition instanceof AbstractControl) {
super.insert(insertIndex, controlOrDefinition, options);
}
else {
super.insert(insertIndex, controlOrDefinition.rxapFormGroup, options);
}
}
disabledWhile(observable, options) {
return controlDisabledWhile(this, observable, options);
}
enabledWhile(observable, options) {
return controlEnabledWhile(this, observable, options);
}
mergeValidators(validators) {
mergeControlValidators(this, validators);
}
mergeAsyncValidators(validators) {
this.setAsyncValidators([
...coerceArray(this.asyncValidator),
...coerceArray(validators),
]);
this.updateValueAndValidity();
}
markAllAsDirty() {
markAllDirty(this);
}
markAllAsPristine() {
markAllPristine(this);
}
setValue(valueOrObservable, options) {
if (isObservable(valueOrObservable)) {
return valueOrObservable.subscribe((value) => {
super.setValue(value, options);
});
}
super.setValue(valueOrObservable, options);
}
markAllAsUntouched() {
markAllUntouched(this);
}
validateOn(observableValidation) {
return observableValidation.subscribe((maybeError) => {
this.setErrors(maybeError);
});
}
hasErrorAndTouched(errorCode, path) {
return hasErrorAndTouched(this, errorCode, path);
}
_patchValue(value, options) {
// Even though the `value` argument type doesn't allow `null` and `undefined` values, the
// `patchValue` can be called recursively and inner data structures might have these values, so
// we just ignore such cases when a field containing FormArray instance receives `null` or
// `undefined` as a value.
if (value == null /* both `null` and `undefined` */) {
return;
}
if (options?.strict) {
if (this.length > value.length) {
for (let index = this.length - 1; index >= value.length; index--) {
this.removeAt(index);
}
}
}
value.forEach((newValue, index) => {
if (this.at(index)) {
this.at(index)
.patchValue(newValue, {
...(options ?? {}),
onlySelf: true,
});
}
else if (options?.coerce) {
this.insertAt(index, value[index]);
}
});
this.updateValueAndValidity(options);
}
patchValue(valueOrObservable, options) {
if (isObservable(valueOrObservable)) {
return valueOrObservable.subscribe((value) => {
this._patchValue(value, options);
});
}
this._patchValue(valueOrObservable, options);
}
hasErrorAndDirty(errorCode, path) {
return hasErrorAndDirty(this, errorCode, path);
}
removeAt(index) {
if (isDevMode()) {
console.warn('It is not recommend to use the FormArray.removeAt method');
}
super.removeAt(index);
this._controlRemovedFn(index);
}
setEnable(enable = true, opts) {
enableControl(this, enable, opts);
}
push(control, options) {
if (isDevMode()) {
console.warn('It is not recommend to use the FormArray.push method');
}
return super.push(control, options);
}
setDisable(disable = true, opts) {
disableControl(this, disable, opts);
}
setControl(index, control, options) {
if (isDevMode()) {
console.warn('It is not recommend to use the FormArray.setControl method');
}
return super.setControl(index, control, options);
}
markAsTouched(opts) {
super.markAsTouched(opts);
this.touchChanges.next(true);
}
markAsUntouched(opts) {
super.markAsUntouched(opts);
this.touchChanges.next(false);
}
markAsPristine(opts) {
super.markAsPristine(opts);
this.dirtyChanges.next(false);
}
markAsDirty(opts) {
super.markAsDirty(opts);
this.dirtyChanges.next(true);
}
reset(value, options) {
super.reset(value, options);
}
setValidators(newValidator, updateValueAndValidity = true) {
super.setValidators(newValidator);
if (updateValueAndValidity) {
super.updateValueAndValidity();
}
}
setAsyncValidators(newValidator, updateValueAndValidity = true) {
super.setAsyncValidators(newValidator);
if (updateValueAndValidity) {
super.updateValueAndValidity();
}
}
hasError(errorCode, path) {
return super.hasError(errorCode, path);
}
setErrors(errors, opts = {}) {
return super.setErrors(errors, opts);
}
getError(errorCode, path) {
return super.getError(errorCode, path);
}
}
class RxapFormGroup extends UntypedFormGroup {
/**
* @internal
*/
get rxapFormDefinition() {
if (!this.parent) {
return this._rxapFormDefinition;
}
if (this._rxapFormDefinition) {
return this._rxapFormDefinition;
}
return this.parent.rxapFormDefinition;
}
get readonly() {
return this.parent?.readonly ?? this._readonly;
}
set readonly(value) {
this._readonly = value;
Object.values(this.controls ?? {}).forEach(control => control.stateChanges?.next());
}
get controlPath() {
const parent = this.parent;
if (parent) {
if (parent.controlPath) {
if (parent === this.root) {
return this.controlId;
}
else {
return [parent.controlPath, this.controlId].join('.');
}
}
}
return '';
}
get fullControlPath() {
const parent = this.parent;
if (parent) {
if (parent.fullControlPath) {
return [parent.fullControlPath, this.controlId].join('.');
}
}
return this.controlId;
}
constructor(controls, options) {
super(controls, options);
this.controls = controls;
this._readonly = false;
this.touchChanges = new Subject();
this.dirtyChanges = new Subject();
this.touch$ = this.touchChanges.asObservable().pipe(distinctUntilChanged());
this.dirty$ = this.dirtyChanges.asObservable().pipe(distinctUntilChanged());
this.value$ = controlValueChanges$(this);
this.disabled$ = controlDisabled$(this);
this.enabled$ = controlEnabled$(this);
this.status$ = controlStatusChanges$(this);
this.errors$ = controlErrorChanges$(this);
this.controlId = options.controlId;
}
select(mapFn) {
return selectControlValue$(this, mapFn);
}
getRawValue() {
return super.getRawValue();
}
get(path) {
return super.get(path);
}
getControl(...names) {
return this.get(names.join('.'));
}
addControl(name, control) {
super.addControl(name, control);
}
removeControl(name) {
super.removeControl(name);
}
contains(controlName) {
return super.contains(controlName);
}
setControl(name, control) {
super.setControl(name, control);
}
setValue(valueOrObservable, options) {
if (isObservable(valueOrObservable)) {
return valueOrObservable.subscribe((value) =>
// TODO : refactor RxapFormGroup to typed FormGroup
super.setValue(value, options));
}
super.setValue(valueOrObservable, options);
}
_patchValue(value, options) {
// Even though the `value` argument type doesn't allow `null` and `undefined` values, the
// `patchValue` can be called recursively and inner data structures might have these values, so
// we just ignore such cases when a field containing FormGroup instance receives `null` or
// `undefined` as a value.
if (value == null /* both `null` and `undefined` */) {
return;
}
Object.keys(value).forEach(name => {
// TODO : resolve type issue
const controls = this.controls;
if (controls[name]) {
controls[name].patchValue(value[name], {
...(options ?? {}),
onlySelf: true,
});
}
});
this.updateValueAndValidity(options);
}
patchValue(valueOrObservable, options) {
if (isObservable(valueOrObservable)) {
return valueOrObservable.subscribe((value) =>
// TODO : refactor RxapFormGroup to typed FormGroup
super.patchValue(value, options));
}
this._patchValue(valueOrObservable, options);
}
disabledWhile(observable, options) {
return controlDisabledWhile(this, observable, options);
}
enabledWhile(observable, options) {
return controlEnabledWhile(this, observable, options);
}
mergeValidators(validators) {
mergeControlValidators(this, validators);
}
mergeAsyncValidators(validators) {
this.setAsyncValidators([
...coerceArray(this.asyncValidator),
...coerceArray(validators),
]);
this.updateValueAndValidity();
}
markAsTouched(opts) {
super.markAsTouched(opts);
this.touchChanges.next(true);
}
markAsUntouched(opts) {
super.markAsUntouched(opts);
this.touchChanges.next(false);
}
markAsPristine(opts) {
super.markAsPristine(opts);
this.dirtyChanges.next(false);
}
markAsDirty(opts) {
super.markAsDirty(opts);
this.dirtyChanges.next(true);
}
markAllAsDirty() {
markAllDirty(this);
}
markAllAsPristine() {
markAllPristine(this);
}
markAllAsUntouched() {
markAllUntouched(this);
}
reset(formState, options) {
super.reset(formState, options);
}
setValidators(newValidator, updateValueAndValidity = true) {
super.setValidators(newValidator);
if (updateValueAndValidity) {
super.updateValueAndValidity();
}
}
setAsyncValidators(newValidator, updateValueAndValidity = true) {
super.setAsyncValidators(newValidator);
if (updateValueAndValidity) {
super.updateValueAndValidity();
}
}
validateOn(observableValidation) {
return validateControlOn(this, observableValidation);
}
hasError(errorCode, path) {
return super.hasError(errorCode, path);
}
setErrors(errors, opts = {}) {
return super.setErrors(errors, opts);
}
getError(errorCode, path) {
return super.getError(errorCode, path);
}
hasErrorAndTouched(error, ...path) {
return hasErrorAndTouched(this, error, ...path);
}
hasErrorAndDirty(error, ...path) {
return hasErrorAndDirty(this, error, ...path);
}
setEnable(enable = true, opts) {
enableControl(this, enable, opts);
}
setDisable(disable = true, opts) {
disableControl(this, disable, opts);
}
}
class RxapFormControl extends UntypedFormControl {
/**
* @internal
*/
get rxapFormDefinition() {
return this.parent.rxapFormDefinition;
}
get readonly() {
return this.parent?.readonly ?? this._readonly;
}
set readonly(value) {
this._readonly = value;
this.stateChanges.next();
}
get controlPath() {
const parent = this.parent;
if (parent) {
if (parent.controlPath) {
if (parent === this.root) {
return this.controlId;
}
else {
return [parent.controlPath, this.controlId].join('.');
}
}
}
return this.controlId;
}
get fullControlPath() {
const parent = this.parent;
if (parent) {
if (parent.fullControlPath) {
return [parent.fullControlPath, this.controlId].join('.');
}
}
return this.controlId;
}
constructor(formState, options) {
super(formState, options);
this._readonly = false;
this.touchChanges = new Subject();
this.dirtyChanges = new Subject();
this.touch$ = this.touchChanges
.asObservable()
.pipe(distinctUntilChanged());
this.dirty$ = this.dirtyChanges
.asObservable()
.pipe(distinctUntilChanged());
this.value$ = controlValueChanges$(this);
this.disabled$ = controlDisabled$(this);
this.enabled$ = controlEnabled$(this);
this.status$ = controlStatusChanges$(this);
this.errors$ = controlErrorChanges$(this);
this._onSetValue = [];
this.stateChanges = new Subject();
this.readonly$ = controlReadonly$(this);
this.controlId = options.controlId;
if (options.disabled) {
this.disable({ emitEvent: false });
}
if (options.readonly) {
this.readonly = true;
}
this.initialState = formState;
}
setValue(valueOrObservable, options) {
if (isObservable(valueOrObservable)) {
return valueOrObservable.subscribe((value) => {
super.setValue(value, options);
this._onSetValue.forEach((setValueFn) => setValueFn(value, options));
});
}
super.setValue(valueOrObservable, options);
this._onSetValue.forEach((setValueFn) => setValueFn(valueOrObservable, options));
}
patchValue(valueOrObservable, options) {
if (isObservable(valueOrObservable)) {
return valueOrObservable.subscribe((value) => super.patchValue(value, options));
}
super.patchValue(valueOrObservable, options);
}
disabledWhile(observable, options) {
return controlDisabledWhile(this, observable, options);
}
enabledWhile(observable, options) {
return controlEnabledWhile(this, observable, options);
}
mergeValidators(validators) {
mergeControlValidators(this, validators);
}
mergeAsyncValidators(validators) {
this.setAsyncValidators([
// TODO : remove 'as any' if solution for the type overwrite issue is found (above)
this.asyncValidator,
...coerceArray(validators),
]);
this.updateValueAndValidity();
}
markAsTouched(opts) {
super.markAsTouched(opts);
this.touchChanges.next(true);
}
markAsUntouched(opts) {
super.markAsUntouched(opts);
this.touchChanges.next(false);
}
markAsPristine(opts) {
super.markAsPristine(opts);
this.dirtyChanges.next(false);
}
markAsDirty(opts) {
super.markAsDirty(opts);
this.dirtyChanges.next(true);
}
markAllAsDirty() {
this.markAsDirty({ onlySelf: true });
}
reset(formState, options) {
const newState = formState ?? this.initialState;
if (typeof newState === 'function') {
super.reset(newState(), options);
}
else {
super.reset(newState, options);
}
}
setValidators(newValidator, updateValueAndValidity = true) {
super.setValidators(newValidator);
if (updateValueAndValidity) {
super.updateValueAndValidity();
}
}
setAsyncValidators(newValidator, updateValueAndValidity = true) {
super.setAsyncValidators(newValidator);
if (updateValueAndValidity) {
super.updateValueAndValidity();
}
}
validateOn(observableValidation) {
return validateControlOn(this, observableValidation);
}
getError(errorCode) {
return super.getError(errorCode);
}
hasError(errorCode) {
return super.hasError(errorCode);
}
setErrors(errors, opts = {}) {
return super.setErrors(errors, opts);
}
setError(key, value, opts = {}) {
super.setErrors({ [key]: value }, opts);
}
hasErrorAndTouched(err