Angular中响应式表单

本文详细介绍了Angular中响应式表单与模板驱动表单的区别,包括FormControl和FormGroup的使用,内置和自定义校验器,以及异步校验的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Abstract

Angular提供了两种方式来进行表单管理,reactive表单和template-driven表单。后面简称为响应式表单,模板驱动表单。

响应式表单

提供对底层表单对象模型的直接,显示访问。与模板驱动表单相比,它们更加健壮:它们具有可伸缩性、可重用性和可测试性。如果表单是应用程序的关键部分,或者你已经在使用响应式模式构建应用程序,那么就应该使用响应式表单。

模板驱动表单

依赖模板中的指令来创建和操作底层对象模型。它们对于在应用程序中添加简单的表单很有用,比如电子邮件列表注册表单。它们可以直接添加到应用程序中,但它们不像响应式表单那样可扩展。如果您有非常基本的表单需求和逻辑,可以在模板中单独管理,那么模板驱动表单可能是一个很好的选择。

响应式表单详解

管理单个表单项-FormControl

demo.commponent.ts:

public colorControl = new FormControl('123');

demo.component.html:

<p>demo works!</p>
<label for="color">Color: </label>
<input id="color" type="text" [formControl]="colorControl" />

效果:
单个表单项管理

管理一组表单项-FormGroup

html:

<form [formGroup]="form">
  <label for="name">姓名: </label>
  <input id="name" formControlName="name" />
  <div *ngIf="form.get('name')?.errors" class="alert">请输入姓名!</div>
  <br />
  <label for="tel">电话: </label>
  <input formControlName="tel" id="tel" />
  <div *ngIf="form.get('tel')?.errors?.['minlength']" class="alert">电话号码长度不得小于11!</div>
  <div *ngIf="form.get('tel')?.errors?.['maxlength']" class="alert">电话号码长度不得超过11!</div>
</form>

css:

.ng-invalid:not(form){
    border: 2px solid #a94442;
}

.alert{
    color: #820000;
}

ts:

import { FormControl, FormGroup, Validators } from '@angular/forms';

public form = new FormGroup({
    name: new FormControl('', Validators.required),
    tel: new FormControl('xxx-01234567', [
        Validators.minLength(11),
        Validators.maxLength(11),
    ]),
});

效果:
一组表单项

表单校验-Built-in校验器

追加html:

<section>
    <button (click)="submit()">提交</button>
</section>

追加ts:

public submit() {
    console.log(this.form);
    if (this.form.invalid) {
        alert('表单校验失败!');
        return;
    }
    console.log('submit!', this.form.value);
}

效果:

请添加图片描述
请添加图片描述

表单校验-自定义校验器

html:

<form [formGroup]="form">
    <label for="name">姓名: </label>
    <input id="name" formControlName="name" />
    <div *ngIf="form.get('name')?.errors" class="alert">请输入姓名!</div>
    <br />
    <label for="tel">电话: </label>
    <input formControlName="tel" id="tel" />
    <div *ngIf="form.get('tel')?.errors?.['length']" class="alert">电话号码长度必须是11位!</div>
  </form>
<section>
    <button (click)="submit()">提交</button>
</section>

ts:

import {
  FormControl,
  FormGroup,
  Validators,
  AbstractControl,
  ValidationErrors,
} from '@angular/forms';

  public telCustomValidator = (
    control: AbstractControl
  ): ValidationErrors | null => {
    const tel = control.value;
    if (!tel) {
      return { required: true };
    }
    if (tel.length !== 11) {
      return { length: true };
    }
    return null;
  };
  
  public form = new FormGroup({
    name: new FormControl('', Validators.required),
    tel: new FormControl('xxx-01234567', [this.telCustomValidator]),
  });

  public submit() {
    console.log(this.form);
    if (this.form.invalid) {
      alert('表单校验失败!');
      return;
    }
    console.log('submit!', this.form.value);
  }

效果:

请添加图片描述

表单校验-自定义交叉校验器

html:

<form [formGroup]="pwdForm">
  <label for="pwd">密码: </label>
  <input id="pwd" formControlName="pwd" />
  <div *ngIf="pwdForm.get('pwd')?.errors" class="alert">请输入密码!</div>
  <br />
  <label for="confirmPwd">确认密码: </label>
  <input formControlName="confirmPwd" id="confirmPwd" />
  <div
    *ngIf="pwdForm.get('confirmPwd')?.dirty && pwdForm.errors?.['not-confirm']"
    class="alert"
  >
    两次密码输入不一致!
  </div>
</form>

ts:

  private confirmPwdValidator = (
    control: AbstractControl
  ): ValidationErrors | null => {
    const pwd = control.get('pwd')?.value;
    const confirmPwd = control.get('confirmPwd')?.value;
    if (pwd !== confirmPwd) {
      return { 'not-confirm': true };
    }
    return null;
  };

  public pwdForm = new FormGroup(
    {
      pwd: new FormControl('', Validators.required),
      confirmPwd: new FormControl('', Validators.required),
    },
    { validators: this.confirmPwdValidator }
  );

效果:

请添加图片描述

请添加图片描述

Tip: 普通自定义校验器的入参control是一个FormControl对象,交叉验证器中它则是一个FormGroup对象。

表单校验-异步校验器

html:

<form [formGroup]="pwdForm">
  <label for="pwd">密码: </label>
  <input id="pwd" formControlName="pwd" />
  <div *ngIf="pwdForm.get('pwd')?.errors?.['required']" class="alert">
    请输入密码!
  </div>
  <div *ngIf="pwdForm.get('pwd')?.errors?.['remote']" class="alert">
    {{pwdForm.get('pwd')?.errors?.['remote']}}
  </div>
  <br />
  <label for="confirmPwd">确认密码: </label>
  <input formControlName="confirmPwd" id="confirmPwd" />
  <div
    *ngIf="pwdForm.get('confirmPwd')?.dirty && pwdForm.errors?.['not-confirm']"
    class="alert"
  >
    两次密码输入不一致!
  </div>
</form>

ts:

import {
  FormControl,
  FormGroup,
  Validators,
  AbstractControl,
  ValidationErrors,
} from '@angular/forms';
import { Observable, catchError, map, of } from 'rxjs';
import { checkPwdFn } from 'src/app/request';

  private cunstomValidatePwd = (control: AbstractControl):Observable<ValidationErrors | null> => {
    return checkPwdFn(control.value).pipe(
      map(res=>{
        if(res === 'pass'){
          // 通过远程校验
          return null;
        }
        return {remote: res}
      }),
      catchError(()=>of({remote: 'Network error 密码校验失败!'}))
    )
  }

  private confirmPwdValidator = (
    control: AbstractControl
  ): ValidationErrors | null => {
    const pwd = control.get('pwd')?.value;
    const confirmPwd = control.get('confirmPwd')?.value;
    if (pwd !== confirmPwd) {
      return { 'not-confirm': true };
    }
    return null;
  };

  public pwdForm = new FormGroup(
    {
      pwd: new FormControl('',  {asyncValidators: [this.cunstomValidatePwd],validators: [Validators.required], updateOn :'blur', }),
      confirmPwd: new FormControl('', Validators.required),
    },
    { validators: this.confirmPwdValidator }
  );

效果:

请添加图片描述

想要源码的,可以来fork我的代码仓:
https://gitee.com/gao-hui007/my-blogs/blob/master/docs/angular/%E8%A1%A8%E5%8D%95.md#
转载请备注来源

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值