A lightweight library for dynamically validate Angular reactive forms using class-validator library.
- Table of Contents
- Installation
- Usage
- Available classes
- Stackblitz example
- Developer note
npm install --save ngx-reactive-form-class-validator
// OR
yarn add ngx-reactive-form-class-validator
"@angular/common": ">= 2.0.0 <= ^18.0.0",
"@angular/core": ">= 2.0.0 <= ^18.0.0",
"@angular/forms": ">= 2.0.0 <= ^18.0.0",
"class-validator": ">= 0.12.0 <= ^0.14.0"
While this library will function with any version of class-validator within this range, we strongly recommend using class-validator ^0.14.0 or later due to a critical security vulnerability addressed in versions 0.14.0 and beyond. This ensures the highest level of security for your application.
Please note that properties without a class-validator decorator will not be validated, see class-validator profile.ts
import { IsEmail, IsNotEmpty, ValidateNested } from 'class-validator';
class Profile {
@IsNotEmpty()
public firstName: string;
@IsNotEmpty()
public lastName: string;
@IsEmail()
public email: string;
@ValidateNested()
public address: Address;
}
address.ts
import { IsNotEmpty, IsOptional, ValidateNested } from 'class-validator';
class Address {
@IsNotEmpty()
public street: string;
@IsNotEmpty()
public city: string;
@IsOptional()
public state: string;
@IsNotEmpty()
public zip: string;
}
Untyped version of ngx-class-validator form classes exist in order to be backward compatible with angular untyped form classes
As described here to be able to use the ClassValidatorFormBuilderService
, you need to import ClassValidatorFormBuilderModule.
app.module.ts
imports: [
...
ClassValidatorFormBuilderModule.forRoot(),
...
],
Then in your component profile-form.component.ts
public constructor(
private fb: ClassValidatorFormBuilderService,
) { }
profileForm = this.fb.group(Profile,
{
firstName: [''],
lastName: [''],
email: [''],
address: this.fb.group(Address,
{
street: [''],
city: [''],
state: [''],
zip: ['']
}
),
});
As it's possible with angular FormGroup
class we can directly create a ClassValidatorFormGroup
using the constructor
export class ProfileFormComponent {
profileForm = new ClassValidatorFormGroup({
firstName: new ClassValidatorFormControl(''),
lastName: new ClassValidatorFormControl(''),
});
}
Now, setting value to any of form controls, will perfom the validator set in the corresponding class.
this.profileForm.controls.email.setValue('notEmailValue');
console.log(this.profileForm.controls.email) // { isEmail: 'email must be an email' }
this.profileForm.controls.email.setValue('email@email.com');
console.log(this.profileForm.controls.email) // null
It is possible as well to combine dynamic validation with custom validation. There are several ways to do it:
this.fb.group (Profile, {
email: ['', Validators.required],
...
}
)
// OR
new ClassValidatorFormGroup(Profile, {
email: new ClassValidatorFormControl('', Validators.required)
})
Both setValidators
and setValidatorsWithDynamicValidation
replace validators provided in parameter, the only one difference is that setValidatorsWithDynamicValidation
add given validators as well as re-enable dynamic validation, as the setValidators
method replace validators with given ones without re-enabling dynamic validation.
emailControl.setValidators(Validators.required); // there will be only Validators.required validator
emailControl.setValidatorsWithDynamicValidation(Validators.required) // there will be Validaros.required validator as well as dynamic validator
An Angular module that provides ClassValidatorFormBuilderService for dependency injection.
It can either be imported forRoot
or normally (We don't recommend importing it normally because that will create multiple instances of ClassValidatorFormBuilderService).
app.module.ts
imports: [
...
ClassValidatorFormBuilderModule.forRoot(),
...
],
An Angular injectable service having the same methods as Angular FormBuilder except a minor change of group
method signature, see below:
group(
classType: ClassType<any>, // The class type of the form group value.
// Angular FormBuilder group method parameters
controlsConfig: { [p: string]: any },
options?: AbstractControlOptions | { [p: string]: any } | null,
): ClassValidatorFormGroup;
We've introduced a new parameter called classType
(a class type containing class-validator decorators) that you should provide, to enable us to perform dynamic validations.
A typescript class extending angular FormGroup class, with a minor change of constructor
signature, the classType parameter.
export class ClassValidatorFormGroup extends FormGroup {
public constructor(
private readonly classType: ClassType<any>,
// Angular FormGroup constructor parameters
controls: {
[key: string]: AbstractControl;
},
validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null,
asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null,
) {
...
}
A typescript class extending angular FormControl class, that will use the classType instance to perform validations and assign validation errors to the ClassValidatorFormControl
.
As it extends angular FormControl class, it contains all FormControl
methods, with a custom new method:
setValidatorsWithDynamicValidation(newValidator: ValidatorFn | ValidatorFn[] | AbstractControlOptions | undefined): void
This method has the same signature as FormControl setValidators method. In addition it re-enables dynamic validation when disabled.
We are open for proposals, so please don't hesitate to create issues if you want to report any bugs/proposals/feature requests.