Angular中响应式表单

发布于:2023-10-04 ⋅ 阅读:(87) ⋅ 点赞:(0)

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#
转载请备注来源


网站公告

今日签到

点亮在社区的每一天
去签到