Angular reactive forms util which generates configuration and validators for constraints.
Inspired by Valdr for AngularJS.
Angular form creation and creating validators manually for each field for a model is too much boilerplate code. Having a configuration and a service which creates reactive form configuration helps to cut down on manual form creation, which is especially useful when working with large and complex forms.
Very similar to Valdr for AngularJS, there are few more advantages to this:
- Less form creation boilerplate
- Possibility to generate the constraints from the models that are present on your back-end (see valdr-bean-validation for Java)
- Compliant with Angular Reactive Forms
- Easy extension with custom validators
- Install
ValdrNg
npm i valdr-ng- Register the
ValdrNgModulein the application module.
@NgModule({
imports: [
ValdrNgModule
]
})
export class AppModule {
}- Set the constraints
export class AppModule {
constructor(private valdrNgService: ValdrNgService) {
valdrNgService.setConstraints({
'Person': {
'firstName': {
'required': {
'message': 'First name is required.'
},
'size': {
'min': 2,
'max': 20,
'message': 'First name must be between 2 and 20 characters.'
}
},
'username': {
'pattern': {
'value': '[a-zA-Z]{4,}',
'message': 'Username must be longer than 4 characters and match \'a-zA-Z\'.'
}
}
}
});
}
}- Use it to:
4.1. create form configuration from the model and model name:
class MyComponent implements OnInit {
personForm: FormGroup;
person = {
firstName: 'John',
username: ''
};
constructor(private valdrNgService: ValdrNgService,
private fb: FormBuilder) { }
ngOnInit(): void {
const controls = this.valdrNgService.createFormGroupControls(this.person, 'Person');
this.personForm = this.fb.group(controls);
}
}4.2. add validators to existing FormGroup:
class MyComponent implements OnInit {
personForm: FormGroup;
person = {
firstName: 'John',
username: ''
};
constructor(private valdrNgService: ValdrNgService,
private fb: FormBuilder) { }
ngOnInit(): void {
const controls = this.fb.group({
firstName: [this.person.firstName],
username: [this.person.username, [Validators.required]]
});
this.personForm = this.fb.group(controls);
this.valdrNgService.addValidators(this.personForm, 'Person');
}
}- The form is ready, now we can show messages for the fields
<form [formGroup]="personForm">
<label for="firstName">First name:</label>
<input id="firstName" formControlName="firstName">
<!-- Simple error handling -->
<div *ngIf="personForm.get('firstName')?.errors as err">
<p *ngIf="err['required'] as req">
{{req.message}}
</p>
</div>
<label for="username">Username:</label>
<input id="username" formControlName="username">
<!-- Other error handling component -->
</form>The JSON object which defines the validation rules has the following structure:
{
"TypeName": {
"ValidatorName": {
"message": "My Error Message"
}
}
}- TypeName - The type of the object
- FieldName - The field name
- ValidatorName - Name of the validator
- message - The message which should be attached on the validation error
Note: The ValidatorName object can contain other validator arguments besides message.
{
"Person": {
"firstName": {
"required": {
"message": "First name is required."
},
"size": {
"min": 2,
"max": 20,
"message": "First name must be between 2 and 20 characters."
}
},
"username": {
"pattern": {
"value": "[a-zA-Z]{4,}",
"message": "Username must be longer than 4 characters and match 'a-zA-Z'."
}
}
}
}Built-in validators:
- size
- min
- max
- minLength
- maxLength
- pattern
- url
NOTE: For more details on the built-in validators see valdr-ng lib readme.
- Create a validator by overriding
BaseValidatorFactory:
@Injectable()
class MyValidator extends BaseValidatorFactory {
getConstraintName() {
return 'validByValue';
}
createValidator(config: {value: string, message: string}): ValdrValidationFn[] {
const validateFn = ({value}: AbstractControl): ValidationErrors | null => {
if (value === null || value === config.value) {
return null;
}
return {
[this.getConstraintName()]: {
message
}
};
}
return [validateFn];
}
}- Register it in
ValdrNgModuleorValdrNgService:
- In the module
@NgModule({
imports: [
ValdrNgModule.forRoot([MyValidator])
],
providers: [
MyValidator
]
})
export class AppModule {
}- Directly in the service
@NgModule({
imports: [
ValdrNgModule
],
providers: [
MyValidator
]
})
export class AppModule {
constructor(valdrNgService: ValdrNgService,
myValidator: MyValidator) {
valdrNgService.addValidatorFactories([myValidator]);
}
}- Use it in constraints:
export class AppModule {
constructor(valdrNgService: ValdrNgService,
myValidator: MyValidator) {
valdrNgService.addValidatorFactories([myValidator]);
valdrNgService.setConstraints({
'Person': {
'password': {
'validByValue': {
'value': 'p455w0rd',
'message': 'Invalid password!'
}
}
}
});
}
}If you need to validate a value directly, without using a form control, you can use the following code:
class MyComponent implements OnInit {
person = {firstName: 'John', username: ''};
constructor(private valdrNgService: ValdrNgService) {
}
ngOnInit(): void {
this.valdrNgService.setConstraints({
'Person': {
'firstName': {
'required': {
'message': 'First name is required.'
},
},
'username': {
'pattern': {
'value': '[a-zA-Z]{4,}',
'message': 'Username must be longer than 4 characters and match \'a-zA-Z\'.'
}
}
}
})
const firstNameValidity = this.valdrNgService.validate('Person', 'firstName', this.person.firstName);
// firsNameValidity == null
const userNameValidity = this.valdrNgService.validate('Person', 'username', this.person.username);
// userNameValidity == {'message': 'Username must be longer than 4 characters and match \'a-zA-Z\'.'}
}
}ValdrNG uses the provided Angular validators
and wraps them by validator name, and returning the message along with the validation result.
That makes couple of things easier:
- Migration of the current forms to ValdrNG (validation result is extended)
- Easily accessible a dedicated message for each field (see the usage)