Overview

The third chapter introduces angular form controls, how to create a form in Angular, and how to validate data against form controls.

Corresponding official document address:

  • Introduction to Angular Forms
  • Reactive form
  • Template-driven forms
  • Form validation

Angular-practice/SRC /forms-overview

Contents

  1. Angular from pit to pit – Getting Started with Angular usage
  2. Angular From Pothole to Pothole – Component eating Guide
  3. Angular From pothole to Pothole – Form controls overview

Knowledge Graph

Step by Step

Introduction of the form

It is used to process the user’s input. By capturing the user’s input event from the view and verifying whether the user’s input meets the conditions, the data model in the form model modification component is created to obtain the user’s input data

Template-driven forms Reactive form
Set up the form Implicitly create form control instances by components Create control instances that are displayed in the component class
Form validation instruction function

Template-driven forms update their data by modifying the ngModel bound data model when the form data changes, whereas reactive forms return a new data model when the form data changes, rather than directly modifying the original data model

Template-driven forms

Interaction with the user is accomplished by binding data values and behavior constraints on the user (a field must be filled in, a field is too long) to the component’s template using form-specific directives (such as ngModel for two-way data binding)

Bidirectional data binding for template-driven forms

Import FormsModule in the root module and add it to the imports array of the root module

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

/ / introduce FormsModule
import { FormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TemplateDrivenFormsComponent } from './template-driven-forms/template-driven-forms.component';

@NgModule({
  declarations: [
    AppComponent,
    ReactiveFormsComponent,
    DynamicFormsComponent,
    TemplateDrivenFormsComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule // Add it to the application module
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Copy the code

Create a new class file to hold data information for two-way data binding between components and templates

ng g class classes/hero
Copy the code
export class Hero {

  /** * ctor * @param name Name * @param age * @param gender * @param location Address */
  constructor(public name: string.public age: number.public gender: string.public location: string) {}}Copy the code

Create the form information that holds the data in the component’s template, and use ngModel to complete the two-way data binding between the component and the template

<form>
  <div class="form-group">
    <label for="name">Name:</label>
    <input type="text" name="name" id="name" [(ngModel)] ="hero.name" class="form-control" autocomplete="off" required minlength="4">
  </div>
  <div class="form-group">
    <label for="age">Age:</label>
    <input type="number" name="age" id="age" [(ngModel)] ="hero.age" class="form-control" required>
  </div>
  <div class="form-group">
    <label for="gender">Gender:</label>
    <div class="form-check" *ngFor="let gender of genders">
      <input class="form-check-input" type="radio" name="gender" id="{{gender.id}}" value="{{gender.value}}"
        [(ngModel)] ="hero.gender">
      <label class="form-check-label" for="{{gender.id}}">
        {{gender.text}}
      </label>
    </div>
  </div>
  <div class="form-group">
    <label for="location">Address:</label>
    <select name="location" id="location" [(ngModel)] ="hero.location" class="form-control" required>
      <option value="{{location}}" *ngFor="let location of locations">{{location}}</option>
    </select>
  </div>
  <button type="submit" (click) ="submit()" class="btn btn-primary">Submit</button>
</form>

<p>Form of data message: {{hero | json}}</p>
Copy the code
import { Component, OnInit } from '@angular/core';

import { Hero } from '. /.. /classes/hero';

@Component({
  selector: 'app-template-driven-forms',
  templateUrl: './template-driven-forms.component.html',
  styleUrls: ['./template-driven-forms.component.scss']})export class TemplateDrivenFormsComponent implements OnInit {
  constructor() {}// Gender option
  public genders = [{
    id: 'male', text: 'male', value: true
  }, {
    id: 'female', text: 'woman', value: false
  }];

  /** * Address drop down */
  public locations: Array<string> = ['beijing'.'shanghai'.'hangzhou'.'wuhan'];

  hero = new Hero(' '.18.'true'.'beijing');

  ngOnInit(): void {
  }

  submit() {

  }
}
Copy the code

When using ngModel for template binding, Angular automatically appends an NgForm directive to the form tag because the NgForm directive controls elements in the form with the ngModel directive and the name attribute. The name property is the key angular uses to register controls, so it must be added when using ngModel for two-way data binding in forms

Tracks the state of form controls

After using ngModel in the form, the ngModel directive reflects the state of the control by updating its CSS class

state The CSS class when this occurs CSS classes that did not occur
Control accessed ng-touched ng-untouched
The value of the control changes ng-dirty ng-pristine
Control whether the value is valid ng-valid ng-invalid

Using CSS class styles for these controls, you can add custom CSS styles to prompt users when their input does not meet the criteria

.ng-valid[required]..ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}
Copy the code

Validation of data

When you need to validate user input, add a native HTML form validator to the control to set the validation criteria. When the form control’s data changes, Angular instructs the data to generate a list of error messages

During validation of user input data, ngModel is exposed by adding a template reference variable to the control to get state information about the specified control in the template, which can then be fed back by getting a list of error messages

<div class="form-group">
    <label for="name">Name:</label>
    <! Get the state of the control by exposing the ngModel directive as a template reference variable.
    <input type="text" name="name" id="name" [(ngModel)] ="hero.name" class="form-control" autocomplete="off" required
      minlength="4" #name="ngModel">
    <! Verify the validity of data only after the user has changed the data or accessed the control.
    <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
      <div *ngIf="name.errors.required">The name cannot be empty</div>
      <div *ngIf="name.errors.minlength">The name cannot contain less than 4 characters</div>
    </div>
  </div>
Copy the code

In the case of data verification failure, the form is not allowed to submit for the system. Therefore, the submission event can be bound to the ngSubmit event attribute of the form, and the data validity can be judged at the submit button in the form of template reference variables. If invalid, the submit button of the form will be disabled

<form (ngSubmit) ="submit()" #heroForm="ngForm">
  <div class="form-group">
    <label for="name">Name:</label>
    <! Get the state of the control by exposing the ngModel directive as a template reference variable.
    <input type="text" name="name" id="name" [(ngModel)] ="hero.name" class="form-control" autocomplete="off" required
      minlength="4" #name="ngModel">
    <! Verify the validity of data only after the user has changed the data or accessed the control.
    <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
      <div *ngIf="name.errors.required">The name cannot be empty</div>
      <div *ngIf="name.errors.minlength">The name cannot contain less than 4 characters</div>
    </div>
  </div>
  <div class="form-group">
    <label for="age">Age:</label>
    <input type="number" name="age" id="age" [(ngModel)] ="hero.age" class="form-control" required>
  </div>
  <div class="form-group">
    <label for="gender">Gender:</label>
    <div class="form-check" *ngFor="let gender of genders">
      <input class="form-check-input" type="radio" name="gender" id="{{gender.id}}" value="{{gender.value}}"
        [(ngModel)] ="hero.gender">
      <label class="form-check-label" for="{{gender.id}}">
        {{gender.text}}
      </label>
    </div>
  </div>
  <div class="form-group">
    <label for="location">Address:</label>
    <select name="location" id="location" [(ngModel)] ="hero.location" class="form-control" required>
      <option value="{{location}}" *ngFor="let location of locations">{{location}}</option>
    </select>
  </div>
  <button type="submit" [disabled] =! "" heroForm.form.valid" class="btn btn-primary">Submit</button>
</form>
Copy the code

Reactive form

Quick learning

Reactive forms rely on the ReactiveFormsModule module, so they need to be introduced in the root module before they can be used

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

/ / introduce ReactiveFormsModule
import { ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReactiveFormsComponent } from './reactive-forms/reactive-forms.component';

@NgModule({
  declarations: [
    AppComponent,
    ReactiveFormsComponent,
    DynamicFormsComponent,
    TemplateDrivenFormsComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule // Add it to the application module
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Copy the code

When reactive forms are used, an instance of the FormControl class corresponds to a FormControl. When used, you assign the instance of the control to a property, and you can later track the value and state of the FormControl by listening on this custom property

import { Component, OnInit } from '@angular/core';

// Introduce the FormControl object
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  // Define attributes to take on FormControl instances
  public name = new FormControl(' ');

  constructor() { }

  ngOnInit(): void{}}Copy the code

Once the control instance is created in the component, you associate the control instance with the form controls in the template by adding the formControl property binding to the form controls on the view template

<form>
  <div class="form-group">
    <label for="name">Name:</label>
    <input type="text" id="name" [formControl] ='name' class="form-control" autocomplete="off">
  </div>
</form>


<div>The name controls data values: {{name | json}}</div>
Copy the code

You can get a copy of the data values of the current FormControl by using the value property of the FormControl, and update the FormControl values by using the setValue method

import { Component, OnInit } from '@angular/core';

// Introduce the FormControl object
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  // Define attributes to take on FormControl instances
  public name = new FormControl('12345');

  constructor() { }

  ngOnInit(): void {
  }

  getName() {
    alert(this.name.value);
  }

  setName() {
    this.name.setValue(1111111); }}Copy the code

Compose multiple controls through FomGroup

A form cannot have only one control, so the unified management of multiple form controls can be achieved by constructing a FormGroup instance in the component

With FormGroup, you also define a property in the component to host the control group instance, and then add each control in the control group as a property value to the instance

import { Component, OnInit } from '@angular/core';

// Introduce FormControl and FormGroup objects
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  // Define object attributes to hold the FormGroup instance
  public profileForm = new FormGroup({
    name: new FormControl('La la la la'),
    age: new FormControl(12)});constructor() { }

  ngOnInit(): void{}}Copy the code

In the view template, bind the properties of the following FormGroup instance to the form element using the FormGroup directive, and then bind each property of the control group to the corresponding form control using the formControlName

<form [formGroup] ='profileForm'>
  <div class="form-group">
    <label for="name">Name:</label>
    <input type="text" id="name" formControlName='name' class="form-control" autocomplete="off" required minlength="4">
  </div>
  <div class="form-group">
    <label for="age">Age:</label>
    <input type="number" id="age" formControlName='age' class="form-control" autocomplete="off" required step="1"
      max="100" min="1">
  </div>
</form>

<div>FormGroup group controls the value of the form: {{profileForm. Value | json}}</div>
Copy the code

When building complex forms, you can make the form more structured by nesting the FormGroup within the FormGroup

import { Component, OnInit } from '@angular/core';

// Introduce FormControl and FormGroup objects
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  // Define object attributes to hold the FormGroup instance
  public profileForm = new FormGroup({
    name: new FormControl('La la la la'),
    age: new FormControl(12),
    address: new FormGroup({
      province: new FormControl('Beijing'),
      city: new FormControl('Beijing'),
      district: new FormControl('Chaoyang district'),
      street: new FormControl('Sanlitun Street')})});constructor() { }

  ngOnInit(): void {
  }

  submit() {
    alert(JSON.stringify(this.profileForm.value)); }}Copy the code

In the view template, bind the FormGroup instance in the FormGroup control group to the control by using the formGroupName property

<form [formGroup] ='profileForm' (ngSubmit) ='submit()'>
  <div class="form-group">
    <label for="name">Name:</label>
    <input type="text" id="name" formControlName='name' class="form-control" autocomplete="off" required minlength="4">
  </div>
  <div class="form-group">
    <label for="age">Age:</label>
    <input type="number" id="age" formControlName='age' class="form-control" autocomplete="off" required step="1"
      max="100" min="1">
  </div>

  <div formGroupName='address'>
    <div class="form-group">
      <label for="province">Save:</label>
      <input type="text" id="province" formControlName='province' class="form-control" autocomplete="off" required>
    </div>
    <div class="form-group">
      <label for="city">City:</label>
      <input type="text" id="city" formControlName='city' class="form-control" autocomplete="off" required>
    </div>
    <div class="form-group">
      <label for="district">Area:</label>
      <input type="text" id="district" formControlName='district' class="form-control" autocomplete="off" required>
    </div>
    <div class="form-group">
      <label for="street">Street:</label>
      <input type="text" id="street" formControlName='street' class="form-control" autocomplete="off" required>
    </div>
  </div>

  <button type="submit" class="btn btn-primary" [disabled] =! "" profileForm.valid">The data submitted</button>
</form>

<div>FormGroup group controls the value of the form: {{profileForm. Value | json}}</div>
Copy the code

For forms that use FormGroup, when using setValue to update data, you must ensure that the new data structure is the same as the original structure, otherwise an error will be reported

import { Component, OnInit } from '@angular/core';

// Introduce FormControl and FormGroup objects
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  // Define object attributes to hold the FormGroup instance
  public profileForm = new FormGroup({
    name: new FormControl('La la la la'),
    age: new FormControl(12),
    address: new FormGroup({
      province: new FormControl('Beijing'),
      city: new FormControl('Beijing'),
      district: new FormControl('Chaoyang district'),
      street: new FormControl('Sanlitun Street')})});constructor() { }

  ngOnInit(): void {
  }

  submit() {
    alert(JSON.stringify(this.profileForm.value));
  }

  updateProfile() {
    this.profileForm.setValue({
      name: '423'}); }}Copy the code

In some cases, we just want to update the data value of a certain control in the control group. In this case, we need to use patchValue to update

import { Component, OnInit } from '@angular/core';

// Introduce FormControl and FormGroup objects
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  // Define object attributes to hold the FormGroup instance
  public profileForm = new FormGroup({
    name: new FormControl('La la la la'),
    age: new FormControl(12),
    address: new FormGroup({
      province: new FormControl('Beijing'),
      city: new FormControl('Beijing'),
      district: new FormControl('Chaoyang district'),
      street: new FormControl('Sanlitun Street')})});constructor() { }

  ngOnInit(): void {
  }

  submit() {
    alert(JSON.stringify(this.profileForm.value));
  }

  updateProfile() {
    this.profileForm.patchValue({
      name: '12345'}); }}Copy the code

Use FormBuilder to generate form controls

When there are too many controls, it can be difficult to build the FormControl manually through FormGroup or FormControl, so we can use dependency injection FormBuilder class to simplify the form construction

The FormBuilder service has three methods: Control, Group, and Array to generate FormControl, FormGroup, and FormArray, respectively, in the component class

The value of each control name is an array. The first value is the default value of the control. The second and third items are synchronous and asynchronous validation methods for this value

import { Component, OnInit } from '@angular/core';

// Introduce FormBuilder to build the form control
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  /** * ctor * @param formBuilder */
  constructor(private formBuilder: FormBuilder) {}public profileForm = this.formBuilder.group({
    name: ['La la la la'],
    age: [12],
    address: this.formBuilder.group({
      province: ['Beijing'],
      city: ['Beijing'],
      district: ['Chaoyang district'],
      street: ['Sanlitun Street']})}); ngOnInit():void{}}Copy the code
Validation of data

In the same way that template-driven forms validate data, you can use native form validators in reactive forms. When setting rules, you change the second parameter of the data value corresponding to the control name in the template to the validated rule

In reactive forms, the data source comes from the component class, so you should add the validator function directly to the corresponding FormControl constructor in the component class. Angular then calls these functions whenever control data changes

Getter methods are created for the specified control to get state information about the specified control in the template

import { Component, OnInit } from '@angular/core';

// Introduce FormBuilder to build the form control
import { FormBuilder } from '@angular/forms';

// Introduce the Validators validator
import { Validators } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  /** * ctor * @param formBuilder */
  constructor(private formBuilder: FormBuilder) {}public profileForm = this.formBuilder.group({
    name: [' ', [
      Validators.required,
      Validators.minLength(4)
    ]],
    age: [12],
    address: this.formBuilder.group({
      province: ['Beijing'],
      city: ['Beijing'],
      district: ['Chaoyang district'],
      street: ['Sanlitun Street']})});// Add the getter method to validate the control to get the state value in the template
  get name() {
    return this.profileForm.get('name');
  }

  ngOnInit(): void{}}Copy the code
<form [formGroup] ='profileForm' (ngSubmit) ='submit()'>
  <div class="form-group">
    <label for="name">Name:</label>
    <input type="text" id="name" formControlName='name' class="form-control" autocomplete="off" required minlength="4">

    <! Verify the validity of data only after the user has changed the data or accessed the control.
    <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
      <div *ngIf="name.errors.required">The name cannot be empty</div>
      <div *ngIf="name.errors.minlength">The name cannot contain less than 4 characters</div>
    </div>
  </div>
  <div class="form-group">
    <label for="age">Age:</label>
    <input type="number" id="age" formControlName='age' class="form-control" autocomplete="off" required step="1"
      max="100" min="1">
  </div>

  <div formGroupName='address'>
    <div class="form-group">
      <label for="province">Save:</label>
      <input type="text" id="province" formControlName='province' class="form-control" autocomplete="off" required>
    </div>
    <div class="form-group">
      <label for="city">City:</label>
      <input type="text" id="city" formControlName='city' class="form-control" autocomplete="off" required>
    </div>
    <div class="form-group">
      <label for="district">Area:</label>
      <input type="text" id="district" formControlName='district' class="form-control" autocomplete="off" required>
    </div>
    <div class="form-group">
      <label for="street">Street:</label>
      <input type="text" id="street" formControlName='street' class="form-control" autocomplete="off" required>
    </div>
  </div>

  <button type="button" class="btn btn-primary" (click) ="updateProfile()">Update the information</button> &nbsp;
  <button type="submit" class="btn btn-primary" [disabled] =! "" profileForm.valid">The data submitted</button>
</form>

<div>FormGroup group controls the value of the form: {{profileForm. Value | json}}</div>
Copy the code

Custom data validation for forms

Custom validators

In many cases, the native validation rules cannot meet our needs, so we need to create a custom validator to achieve

For reactive forms, you can define a method that validates the control’s data and then add the method as a parameter to the control definition

import { Component, OnInit } from '@angular/core';

// Introduce FormBuilder to build the form control
import { FormBuilder } from '@angular/forms';

// Introduce the Validators validator
import { Validators } from '@angular/forms';

/** * Custom validation method * @param name control information */
function validatorName(name: FormControl) {
  return name.value === 'lala' ? { nameinvalid: true } : null;
}

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {

  /** * ctor * @param formBuilder */
  constructor(private formBuilder: FormBuilder) {}public profileForm = this.formBuilder.group({
    name: [' ', [
      Validators.required,
      Validators.minLength(4),
      validatorName // Add a custom validation method
    ]],
    age: [12],
    address: this.formBuilder.group({
      province: ['Beijing'],
      city: ['Beijing'],
      district: ['Chaoyang district'],
      street: ['Sanlitun Street']})});// Add the getter method to validate the control to get the state value in the template
  get name() {
    return this.profileForm.get('name');
  }

  ngOnInit(): void{}}Copy the code

In the validation method, null is returned when the data is valid, and an object message is returned when the data is invalid. In this case, nameInvalid is the key value of the error message obtained in the template

<div class="form-group">
    <label for="name">Name:</label>
    <input type="text" id="name" formControlName='name' class="form-control" autocomplete="off" required minlength="4">

    <! Verify the validity of data only after the user has changed the data or accessed the control.
    <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
      <div *ngIf="name.errors.required">The name cannot be empty</div>
      <div *ngIf="name.errors.minlength">The name cannot contain less than 4 characters</div>
      <div *ngIf="name.errors.nameinvalid">Name is invalid</div>
    </div>
</div>
Copy the code

In template-driven forms, since you are not using a FormControl instance directly, you should add a custom directive to the template to validate the control data

Use the Angular CLI to create a directive for form validation

ng g directive direactives/hero-validate
Copy the code

After creating the directive, we need to add the Validator to the existing Validator collection, and to integrate the directive with Angular forms, we need to inherit the Validator interface

import { Directive, Input } from '@angular/core';
import { AbstractControl, Validator, ValidationErrors, NG_VALIDATORS } from '@angular/forms';

@Directive({
  selector: '[appHeroValidate]'.Use multi: true to add the validator to the existing set of validators
  providers: [{ provide: NG_VALIDATORS, useExisting: HeroValidateDirective, multi: true}]})export class HeroValidateDirective implements Validator {

  constructor() {}/** * Performs synchronous validation on the specified control * @param control */
  validate(control: AbstractControl): ValidationErrors | null {
    return control.value === 'lala' ? { 'nameInvalid': true } : null; }}Copy the code

When the inherited validate method is implemented, you can add this directive to the controls of the template

<div class="form-group">
    <label for="name">Name:</label>
    <! Get the state of the control by exposing the ngModel directive as a template reference variable.
    <input type="text" name="name" id="name" [(ngModel)] ="hero.name" class="form-control" autocomplete="off" required
      minlength="4" #name="ngModel" appHeroValidate>
    <! Verify the validity of data only after the user has changed the data or accessed the control.
    <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
      <div *ngIf="name.errors.required">The name cannot be empty</div>
      <div *ngIf="name.errors.minlength">The name cannot contain less than 4 characters</div>
      <div *ngIf="name.errors.nameInvalid">Name is invalid</div>
    </div>
</div>
Copy the code

Cross-validation across fields

When you need to cross-validate multiple control data in a form, you need to validate the entire FormGroup. So the validation method here needs to be passed in as a FormGroup parameter when the control group is defined

Similar to the validation of a single field, the ValidatorFn interface is implemented to return null if the form data is valid and ValidationErrors otherwise

import { Component, OnInit } from '@angular/core';

// Introduce FormControl and FormGroup objects
import { FormControl, FormGroup, ValidatorFn, ValidationErrors } from '@angular/forms';

// Introduce FormBuilder to build the form control
import { FormBuilder } from '@angular/forms';

// Introduce the Validators validator
import { Validators } from '@angular/forms';

/** * cross-field validation * @param controlGroup controlGroup */
const nameAgeCrossValidator: ValidatorFn = (controlGroup: FormGroup): ValidationErrors | null= > {

  // Get information about the child control
  //
  const name = controlGroup.get('name');
  const age = controlGroup.get('age');

  return name && age && name.value === 'lala' && age.value === 12 ? { 'nameAgeInvalid': true } : null;
};

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.scss']})export class ReactiveFormsComponent implements OnInit {
  /** * ctor * @param formBuilder */
  constructor(private formBuilder: FormBuilder) {}public profileForm = this.formBuilder.group({
    name: [' ', [
      Validators.required,
      Validators.minLength(4),
      validatorName
    ]],
    age: [12],
    address: this.formBuilder.group({
      province: ['Beijing'],
      city: ['Beijing'],
      district: ['Chaoyang district'],
      street: ['Sanlitun Street']
    })
  }, { validators: [nameAgeCrossValidator] }); // Add validators for control groups
  
  ngOnInit(): void{}}Copy the code

When cross-validation is performed against multiple fields, the template page needs to obtain the error information by obtaining the error object information of the entire form

<div class="form-group">
    <label for="name">Name:</label>
    <input type="text" id="name" formControlName='name' class="form-control" autocomplete="off" required minlength="4">

    <! Verify the validity of data only after the user has changed the data or accessed the control.
    <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
      <div *ngIf="name.errors.required">The name cannot be empty</div>
      <div *ngIf="name.errors.minlength">The name cannot contain less than 4 characters</div>
      <div *ngIf="name.errors.nameinvalid">Name is invalid</div>
    </div>
  </div>
  <div class="form-group">
    <label for="age">Age:</label>
    <input type="number" id="age" formControlName='age' class="form-control" autocomplete="off" required step="1"
      max="100" min="1">
    <div *ngIf="profileForm.errors? .nameAgeInvalid && (profileForm.touched || profileForm.dirty)"
      class="alert alert-danger">Lala can't be 12</div>
</div>
Copy the code

For template-driven forms, custom directives are used for cross-field validation, as opposed to validation for a single control. In this case, you need to add directives to the form tag and then use template reference variables to retrieve error information

import { Directive } from '@angular/core';
import { Validator, AbstractControl, ValidationErrors, ValidatorFn, FormGroup, NG_VALIDATORS } from '@angular/forms';

/** * cross-field validation * @param controlGroup controlGroup */
const nameAgeCrossValidator: ValidatorFn = (controlGroup: FormGroup): ValidationErrors | null= > {

  // Get information about the child control
  //
  const name = controlGroup.get('name');
  const age = controlGroup.get('age');

  return name && age && name.value === 'lala' && age.value === 12 ? { 'nameAgeInvalid': true } : null;
};

@Directive({
  selector: '[appCrossFieldValidate]',
  providers: [{ provide: NG_VALIDATORS, useExisting: CrossFieldValidateDirective, multi: true}]})export class CrossFieldValidateDirective implements Validator {

  constructor() { }

  validate(control: AbstractControl): ValidationErrors | null {
    returnnameAgeCrossValidator(control); }}Copy the code

Of the pit

Personal Profile: Born in 1996, born in a fourth-tier city in Anhui province, graduated from Top 10 million universities. .NET programmer, gunslinger, cat. It will begin in December 2016. NET programmer career, Microsoft. NET technology stalwart, aspired to be the cloud cat kid programming for Google the best. NET programmer. Personal blog: yuiter.com blog garden blog: www.cnblogs.com/danvic712