Menu

Making Angular reactive forms easier (or better) with @rxweb/reactive-form-validators

I have been a big fan of Angular reactive form from Angular RC2, never liked the template-driven form. Reactive forms give more control – validation, value changes, etc.

But things are really can be a bit messy when you deal with big forms or complicated forms. That is where the @rxweb/reactive-form-validators can come in handy.

in your Angular project install @rxweb/reactive-form-validators

npm i @rxweb/reactive-form-validators

in your module please include the RxReactiveFormsModule as part of the imports

@NgModule({
    imports: [
      ....,
      RxReactiveFormsModule      
    ],

if you want to configure the default message for all the validation, you can use ReactiveFormConfig.set method to do that like below. I did it as part of my module constructor (not sure if this is the best way to do it.)

 export class SharedModule {

    constructor(){  
      ReactiveFormConfig.set({
        "internationalization": {
            "dateFormat": "dmy",
            "seperator": "/"
        },
        "validationMessage": {
            "alpha": "Only alphabelts are allowed.",
            "alphaNumeric": "Only alphabet and numbers are allowed.",
            "compare":"Inputs are not matched.",
            "contains":"Value is not contains in the input",
            "creditcard":"Credit card number is not correct",
            "digit":"Only digit are allowed",
            "email":"Email is not valid",
            "greaterThanEqualTo":"Please enter greater than or equal to the joining age",
            "greaterThan":"Please enter greater than to the joining age",
            "hexColor":"Please enter hex code",
            "json":"Please enter valid json",
            "lessThanEqualTo":"Please enter less than or equal to the current experience",
            "lessThan":"Please enter less than or equal to the current experience",
            "lowerCase":"Only lowercase is allowed",
            "maxLength":"Maximum length is 10 digit",
            "maxNumber":"Enter value less than equal to 3",
            "minNumber":"Enter value greater than equal to 1",
            "password":"Please enter valid password",
            "pattern":"Please enter valid zip code",
            "range":"Please enter age between 18 to 60",
            "required":"This field is required",
            "time":"Only time format is allowed",
            "upperCase":"Only uppercase is allowed",
            "url":"Only url format is allowed",
            "zipCode":"Enter valid zip code",
            "minLength":"Minimum length is 10 digit"
        }
    });
    }
   }

if you don’t do this you can do it when you define the form model, and if you do it in the form model these default values will get overwritten.

Now let’s look at the form model

export class CustomerForm { 
    customerId?: number;
    rowGuid?: string;

    @maxLength({ value: 128 })
    companyName?: string;

    @maxLength({ value: 50 })
    emailAddress?: string;

    @required()
    @maxLength({ value: 50 })
    firstName?: string;

    @required()
    @maxLength({ value: 50 })
    lastName?: string;

    @maxLength({ value: 50 })
    middleName?: string;

    @maxLength({ value: 25 })
    phone?: string;

    @maxLength({ value: 256 })
    salesPerson?: string;

    @maxLength({ value: 10 })
    suffix?: string;

    @maxLength({ value: 8 })
    title?: string;
}

All the fields you need to surface need to be defined here.

Now on the actual component that you need do like below

import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ReactiveFormConfig, RxFormBuilder } from '@rxweb/reactive-form-validators';
import { CustomerForm } from './customer-form';

@Component({
  ....
  ....
})
export class CustomerDetailsComponent implements OnInit { 
  customerForm: FormGroup;
  constructor(private formBuilder: RxFormBuilder) {
    this.customerForm = this.formBuilder.formGroup(new CustomerForm());
  }
}

Now the component HTML looks like this

<form *ngIf="customerForm" [formGroup]="customerForm">
        <div class="form-group">
          <label>Title</label>
          <input type="text" formControlName="title" class="form-control" />
          <app-form-field-errors [errors]="customerForm.controls.title.errors"></app-form-field-errors>
        </div>

        <ul class="form-text text-danger" *ngIf="errors">
          <li *ngIf="errors.maxLength">{{errors.maxLength.message}}</li>
          <li *ngIf="errors.required">{{errors.required.message}}</li>
        </ul>

This errors list can be created as a separate component to handle all the errors.

if you are wondering how to set the values, there are a few ways to do that, but I took the below approach, not sure if it will cause issue, but we’ll see.

this.customerForm = this.formBuilder.formGroup(new CustomerForm(customerData));

Then you can go back to CustomerForm object and update it to support the constructor like below

export class CustomerForm {
    constructor(customer?: ICustomer) {
        if (customer) {
            ....
            this.firstName = customer.firstName;
            ....
        }
    }

    customerId?: number;
    rowGuid?: string;

    @maxLength({ value: 128 })
    companyName?: string;
    .....

That was pretty sweet.

Leave a comment