Angular supports very powerful built-in form validation, maxLength, MinLength, Required, and Pattern. Most business scenarios require validation using Angular’s built-in form validators, but sometimes you need to implement more complex form validations using Angular’s Custom Validator. Let’s see how to use Angular custom form validation

Effect:





QQ screenshots 20170428193800. PNG

  1. First, create our register and display a simple form in the template

    <h3 class="text-center"> Register </h3> <form>
    
     <div class="form-group">
       <label for="username"> User name: </label>
       <input type="text" id="username" class="form-control" >
     </div>
    
    </form>Copy the code

    To make the form look nice, introduce the bootstrap style file in index.html:

    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">Copy the code
  2. Next we define our validation requirements:

    We want the user name to contain only numbers, letters, and underscores and not start with an underscoreCopy the code
    • First add the formGroup directive to the form tag:
      <form [formGroup]="registerForm" >Copy the code
    • And add the formControlName directive to the input tag:
      <input formControlName="username" type="text" 
        id="username" class="form-control" >Copy the code
  3. Define validation rules in code: Import the following classes from the built-in forms module:

    import { FormBuilder, FormGroup, Validators } from '@angular/forms';Copy the code

    Among them:

          1.FormBuilder is used to build form data2.FormGroup indicates the form type3.Validators contains form-built validation rules, such as validators.requiredCopy the code

    Defining form properties

    registerForm: FormGroup;Copy the code

    Define error messages for each entry when the form fails to validate (currently we only have username)

     formErrors = {
       username: ' '
     };Copy the code

    Define a description for each validation rule when validation fails. (A form control may have multiple validation rules, with failed validation instructions forming an error message.)

     validationMessage = {
       'username': {
         'minlength': 'Username must be at least 3 characters in length'.'maxlength': 'User name length up to 10 characters'.'required': 'Please fill in user name'}};Copy the code

    Add the fb attribute to the constructor to build the form

    constructor(private fb: FormBuilder) {}Copy the code

    Add a method to build the form

     buildForm(): void {
     // Build the form with formBuilder
     this.registerForm = this.fb.group({
       /* Add three authentication rules for username: * 1. Mandatory, 2. The maximum length is 10, 3. The minimum length is 3 * where the first empty string argument is the default value of the form */
       'username': [ ' ', [
         Validators.required,
         Validators.maxLength(10),
         Validators.minLength(3)]]});Copy the code

    Next we add a method to update the error message

     onValueChanged(data? : any) {// Return if the form does not exist
       if (!this.registerForm) return;
       // Get the current form
       const form = this.registerForm;
    
       // Iterate over the error message object
       for (const field in this.formErrors) {
         // Clear the current error message
         this.formErrors[field] = ' ';
         // Get the control for the current form
         const control = form.get(field);
    
         // The current form has this space control && this control has not been modified && this control does not pass verification
         if(control && control.dirty && ! control.valid) {// Get the name of the control that failed validation to get more detailed failed information
           const messages = this.validationMessage[field];
           // Iterate through the error object of the current control and get the failed property
           for (const key in control.errors) {
             // Concatenate the description of all failed items into error messages
             this.formErrors[field] += messages[key] + '\n'; }}}}Copy the code

    Just initialize the error message after the form is built and update the error message every time the form data changes. Add the following code to the buildForm method

     // Update the error message every time the form data changes
     this.registerForm.valueChanges
       .subscribe(data= >this.onValueChanged(data));
    
     // Initialize the error message
     this.onValueChanged();Copy the code

    Now that we have a good handle on error messages, we just need to add the following code to the form template below the input tag:

     <div *ngIf="formErrors.username" 
       class="showerr alert alert-danger" >{{ formErrors.username }}</div>Copy the code

    Add the following code to the CSS of the form template:

     form {
       width: 90%;
       max-width: 45em;
       margin: auto;
     }
    
     .showerr {
       white-space: pre-wrap;
     }Copy the code

    Now we’re ready to try it out, and we can see pretty good results with no errors in the code. If the code fails or doesn’t work as expected, you can change it by referring to the complete code at the end of this article

  4. Now that we have the layout set up, we have not yet achieved our final goal: to implement custom form validation. Next we create a regular validator and create a new validate-register.ts file:

     import { ValidatorFn, AbstractControl } from '@angular/forms';
    
     export function validateRex(type: string, validateRex: RegExp) :ValidatorFn {
       return (control: AbstractControl): {[key: string] :any} = > {// Gets the contents of the current control
         const str = control.value;
         // Set our own validation type
         const res = {};
         res[type] = {str}
         // Return null if the validation is successful otherwise return an object (containing our custom attributes)
         return validateRex.test(str) ? null: res; }}Copy the code

    Import {validateRex} from ‘./validate-register’; Change the validationMessage property to:

    // Add the caption validationMessage = {for each form validation.'username': {
         'minlength': 'Username must be at least 3 characters in length'.'maxlength': 'User name length up to 10 characters'.'required': 'Please fill in user name'.'notdown': 'User name cannot start with an underscore'.'only': 'User name can contain only numbers, letters, and underscores'}};Copy the code

    Modify the buildForm method:

     // Build the form with formBuilder
     this.registerForm = this.fb.group({
       /* Add 5 authentication rules for username: * 1. The maximum length is 10. The minimum length is 3. The minimum length cannot start with an underscore. Contains only numbers, letters, and underscores * where the first empty string argument is the default value of the form */
       'username': [ ' ', [
         Validators.required,
         Validators.maxLength(10),
         Validators.minLength(3),
         validateRex('notdown'./ ^ (? ! _) /),
         validateRex('only'./^[1-9a-zA-Z_]+$/)]]});Copy the code

    OK ! Now that you’re done, run the code and try it out. You can add any validation rules you want, just by changing the validationMessage property and the buildForm method! If you add multiple form controls, you also need to modify formErrors. For example, if you add a password control, change formErrors to

    formErrors = {
     username: ' ',
     password: ' '
    };Copy the code

    You can try it yourself!

  5. Complete code: register.component.html:

     <h3 class="text-center"> Register </h3> <form [formGroup]="registerForm" >
    
       <div class="form-group">
         <label for="username"> User name: </label> <input formControlName="username"
           type="text" id="username" #username
           class="form-control" >
         <div *ngIf="formErrors.username" class="showerr alert alert-danger" >{{ formErrors.username }}</div>
       </div>
    
     </form>Copy the code

    register.component.css:

     form {
       width: 90%;
       max-width: 45em;
       margin: auto;
     }
    
     .showerr {
       white-space: pre-wrap;
     }Copy the code

    register.component.ts:

     import { Component, OnInit } from '@angular/core';
     import { FormBuilder, FormGroup, Validators } from '@angular/forms';
     import { validateRex } from './validate-register';
    
     @Component({
       selector: 'app-register',
       templateUrl: './register.component.html',
       styleUrls: ['./register.component.css']})
     export class RegisterComponent implements OnInit {
    
       // Define the form
       registerForm: FormGroup;
    
       // Error message displayed when the form cannot be verified
       formErrors = {
         username: ' '
       };
    
       // Add caption for each form validation item
       validationMessage = {
         'username': {
           'minlength': 'Username must be at least 3 characters in length'.'maxlength': 'User name length up to 10 characters'.'required': 'Please fill in user name'.'notdown': 'User name cannot start with an underscore'.'only': 'User name can contain only numbers, letters, and underscores'}};// Add fb attribute to create form
       constructor(private fb: FormBuilder) { }
    
       ngOnInit() {
         // Build the form at initialization
         this.buildForm();
       }
    
       // Build the form method
       buildForm(): void {
         // Build the form with formBuilder
         this.registerForm = this.fb.group({
           /* Add three authentication rules for username: * 1. Mandatory, 2. Maximum length: 10, 3. Minimum length: 3, 4. Cannot start with an underscore, 5. Contains only numbers, letters, and underscores * where the first empty string argument is the default value of the form */
           'username': [ ' ', [
             Validators.required,
             Validators.maxLength(10),
             Validators.minLength(3),
             validateRex('notdown', / ^ (? ! _)/), validateRex('only', / ^ [19 -a-zA-Z_]+$/)
           ]]
         });
    
         // Update the error message every time the form data changes
         this.registerForm.valueChanges
           .subscribe(data= >this.onValueChanged(data));
    
         // Initialize the error message
         this.onValueChanged();
       }
    
       // This method is triggered every time data changes
       onValueChanged(data? : any) {// Return if the form does not exist
         if (!this.registerForm) return;
         // Get the current form
         const form = this.registerForm;
    
         // Iterate over the error message object
         for (const field in this.formErrors) {
           // Clear the current error message
           this.formErrors[field] = ' ';
           // Get the control for the current form
           const control = form.get(field);
    
           // The current form has this space control && this control has not been modified && this control does not pass verification
           if(control && control.dirty && ! control.valid) {// Get the name of the control that failed validation to get more detailed failed information
             const messages = this.validationMessage[field];
             // Iterate through the error object of the current control and get the failed property
             for (const key in control.errors) {
               // Concatenate the description of all failed items into error messages
               this.formErrors[field] += messages[key] + '\n';
             }
           }
         }
       }
    
     }Copy the code

    validate-register.ts:

     import { ValidatorFn, AbstractControl } from '@angular/forms';
    
     export function validateRex(type: string, validateRex: RegExp) :ValidatorFn {
       return (control: AbstractControl): {[key: string] :any} = > {// Gets the contents of the current control
         const str = control.value;
         // Set our own severity type
         const res = {};
         res[type] = {str}
         // Return null if the validation is successful otherwise return an object (containing our custom attributes)
         return validateRex.test(str) ? null: res; }}Copy the code

This article ends, thanks for reading!