ngx-reactive-form-class-validator
Version:
A lightweight library for dynamically validate Angular reactive forms using class-validator library.
286 lines (278 loc) • 13.1 kB
JavaScript
import { FormArray, FormControl, Validators, FormGroup } from '@angular/forms';
import * as i0 from '@angular/core';
import { Injectable, NgModule } from '@angular/core';
import { validateSync } from 'class-validator';
import { CommonModule } from '@angular/common';
class ClassValidatorFormArray extends FormArray {
constructor(controls, validatorOrOpts, asyncValidator) {
super(controls, validatorOrOpts, asyncValidator);
}
}
class ClassValidatorFormControl extends FormControl {
/**
* Creates a new `ClassValidatorFormControl` instance.
*
* @param formState Initializes the control with an initial value,
* or an object that defines the initial value and disabled state.
*
* @param validatorOrOpts A synchronous validator function, or an array of
* such functions, or an `AbstractControlOptions` object that contains validation functions
* and a validation trigger.
*
* @param asyncValidator A single async validator or array of async validator functions
*
*/
constructor(formState, validatorOrOpts, asyncValidator) {
super(formState, validatorOrOpts, asyncValidator);
this.dynamicValidator = (control) => {
this.formGroupClassValue[this.name] = control.value;
const validationErrors = validateSync(this.formGroupClassValue)
.find(error => error.property === this.name);
return validationErrors ? validationErrors.constraints : undefined;
};
this.setValidatorsWithDynamicValidation(validatorOrOpts);
}
/**
* @internal
*/
setNameAndFormGroupClassValue(name, value) {
this.name = name;
this.formGroupClassValue = value;
}
/**
* Sets the synchronous validators that are active on this control as well as resetting the dynamic `class-validator`. Calling
* this overwrites any existing sync validators.
*
* When you add or remove a validator at run time, you must call
* `updateValueAndValidity()` for the new validation to take effect.
*
*/
setValidatorsWithDynamicValidation(newValidator) {
this.setValidators(newValidator
? [this.composeValidators(newValidator), this.dynamicValidator]
: this.dynamicValidator);
}
composeValidators(validator) {
if (validator instanceof Array) {
return Validators.compose(validator);
}
if (validator.validators) {
return this.composeValidators(validator.validators);
}
return validator;
}
}
class ClassValidatorFormGroup extends FormGroup {
/**
* Creates a new `ClassValidatorFormGroup` instance.
*
* @param formClassType the `classType` containing `class-validator` decorators to be used to validate form
* @param controls A collection of child controls. The key for each child is the name
* under which it is registered.
*
* @param validatorOrOpts A synchronous validator function, or an array of
* such functions, or an `AbstractControlOptions` object that contains validation functions
* and a validation trigger.
*
* @param asyncValidator A single async validator or array of async validator functions
*
*/
constructor(formClassType, controls, validatorOrOpts, asyncValidator) {
super(controls, validatorOrOpts, asyncValidator);
this.formClassType = formClassType;
this.assignFormValueToClassValue();
this.setClassValidatorControlsContainerGroupClassValue();
}
/**
* Add a control to this group.
*
* This method also updates the value and validity of the control.
*
* @param name The control name to add to the collection
* @param control Provides the control for the given name
* @param options Specifies whether this FormGroup instance should emit events after a new
* control is added.
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
* `valueChanges` observables emit events with the latest status and value when the control is
* added. When false, no events are emitted.
*
*/
addControl(name, control, options) {
super.addControl(name, control, options);
this.assignFormValueToClassValue();
this.setClassValidatorControlsContainerGroupClassValue();
}
/**
* Remove a control from this group.
*
* @param name The control name to remove from the collection
* @param options Specifies whether this FormGroup instance should emit events after a
* control is removed.
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
* `valueChanges` observables emit events with the latest status and value when the control is
* removed. When false, no events are emitted.
*/
removeControl(name, options = {}) {
super.removeControl(name, options);
this.assignFormValueToClassValue();
this.setClassValidatorControlsContainerGroupClassValue();
}
setClassValidatorControlsContainerGroupClassValue() {
Object.entries(this.controls).forEach(([controlName, control]) => {
if (control instanceof ClassValidatorFormControl) {
this.controls[controlName]
.setNameAndFormGroupClassValue(controlName, this.classValue);
}
});
}
assignFormValueToClassValue() {
this.classValue = Object.assign(new this.formClassType(), this.value);
}
}
// Coming from https://github.com/angular/angular/blob/3b0b7d22109c79b4dceb4ae069c3927894cf1bd6/packages/forms/src/form_builder.ts#L14
const isAbstractControlOptions = (options) => options.asyncValidators !== undefined ||
options.validators !== undefined ||
options.updateOn !== undefined;
class ClassValidatorFormBuilderService {
/**
* @description
* Construct a new `FormGroup` instance.
*
* @param formClassType the `classType` containing `class-validator` decorators to be used to validate form
* @param controlsConfig A collection of child controls. The key for each child is the name
* under which it is registered.
*
* @param options Configuration options object for the `FormGroup`. The object can
* have two shapes:
*
* 1) `AbstractControlOptions` object (preferred), which consists of:
* * `validators`: A synchronous validator function, or an array of validator functions
* * `asyncValidators`: A single async validator or array of async validator functions
* * `updateOn`: The event upon which the control should be updated (options: 'change' | 'blur' |
* submit')
*
* 2) Legacy configuration object, which consists of:
* * `validator`: A synchronous validator function, or an array of validator functions
* * `asyncValidator`: A single async validator or array of async validator functions
*
*/
group(formClassType, controlsConfig, options) {
// Coming from https://github.com/angular/angular/blob/3b0b7d22109c79b4dceb4ae069c3927894cf1bd6/packages/forms/src/form_builder.ts#L59
const controls = this.reduceControls(controlsConfig);
let validators = null;
let asyncValidators = null;
let updateOn;
if (options) {
if (isAbstractControlOptions(options)) {
// `options` are `AbstractControlOptions`
validators = options.validators ? options.validators : null;
asyncValidators = options.asyncValidators ? options.asyncValidators : null;
updateOn = options.updateOn ? options.updateOn : undefined;
}
else {
// `options` are legacy form group options
validators = options['validator'] !== null ? options['validator'] : null;
asyncValidators = options['asyncValidator'] !== null ? options['asyncValidator'] : null;
}
}
return new ClassValidatorFormGroup(formClassType, controls, { asyncValidators, updateOn, validators });
}
/**
* Constructs a new `FormArray` from the given array of configurations,
* validators and options.
*
* @param controlsConfig An array of child controls or control configs. Each
* child control is given an index when it is registered.
*
* @param validatorOrOpts A synchronous validator function, or an array of
* such functions, or an `AbstractControlOptions` object that contains
* validation functions and a validation trigger.
*
* @param asyncValidator A single async validator or array of async validator
* functions.
*/
array(controlsConfig, validatorOrOpts, asyncValidator) {
const controls = controlsConfig.map(control => this.createControl(control));
return new ClassValidatorFormArray(controls, validatorOrOpts, asyncValidator);
}
/**
* @description
* Construct a new `FormControl` with the given state, validators and options.
*
* @param formState Initializes the control with an initial state value, or
* with an object that contains both a value and a disabled status.
*
* @param validatorOrOpts A synchronous validator function, or an array of
* such functions, or an `AbstractControlOptions` object that contains
* validation functions and a validation trigger.
*
* @param asyncValidator A single async validator or array of async validator
* functions.
*
* @usageNotes
*
* ### Initialize a control as disabled
*
* The following example returns a control with an initial value in a disabled state.
*
* <code-example path="forms/ts/formBuilder/form_builder_example.ts" region="disabled-control">
* </code-example>
*/
control(formState, validatorOrOpts, asyncValidator) {
return new ClassValidatorFormControl(formState, validatorOrOpts, asyncValidator);
}
// Coming from https://github.com/angular/angular/blob/3b0b7d22109c79b4dceb4ae069c3927894cf1bd6/packages/forms/src/form_builder.ts#L133
reduceControls(controlsConfig) {
const controls = {};
Object.keys(controlsConfig).forEach(controlName => {
controls[controlName] = this.createControl(controlsConfig[controlName]);
});
return controls;
}
createControl(controlConfig) {
if (controlConfig instanceof FormControl
|| controlConfig instanceof FormGroup
|| controlConfig instanceof FormArray) {
return controlConfig;
}
else if (Array.isArray(controlConfig)) {
const value = controlConfig[0];
const validator = controlConfig.length > 1 ? controlConfig[1] : null;
const asyncValidator = controlConfig.length > 2 ? controlConfig[2] : null;
return this.control(value, validator, asyncValidator);
}
else {
return this.control(controlConfig);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderService, decorators: [{
type: Injectable
}] });
class ClassValidatorFormBuilderModule {
static forRoot() {
return {
ngModule: ClassValidatorFormBuilderModule,
providers: [
ClassValidatorFormBuilderService,
],
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderModule, imports: [CommonModule] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderModule, providers: [ClassValidatorFormBuilderService], imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ClassValidatorFormBuilderModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
providers: [ClassValidatorFormBuilderService],
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ClassValidatorFormArray, ClassValidatorFormBuilderModule, ClassValidatorFormBuilderService, ClassValidatorFormControl, ClassValidatorFormGroup };
//# sourceMappingURL=ngx-reactive-form-class-validator.mjs.map