精华内容
下载资源
问答
  • 本篇文章主要介绍了强大的 Angular 表单验证功能详细介绍,使用 Angular 的内置表单校验能够完成绝大多数的业务场景的校验需求,有兴趣的可以了解一下
  • Angular表单验证

    千次阅读 2017-11-13 10:35:24
    Angular表单验证分为两种验证:1.内置验证(required,minlength等);2.自定义验证(正则表达式)。 接下来我们用一个注册账号的demo来看一下这两种验证是如何实现的。 项目界面 一、内置验证 其中账户名有...

    Angular表单验证分为两种验证:1.内置验证(required,minlength等);2.自定义验证(正则表达式)。

    接下来我们用一个注册账号的demo来看一下这两种验证是如何实现的。

    项目界面


    一、内置验证

    其中账户名有required验证和最短长度验证,其他两个只有required验证

    1.项目目录

    ----------app.component.ts

    ----------app.component.html

    ----------app.component.css

    ----------app.module.ts

    2.项目代码

    app.module.ts

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule,ReactiveFormsModule  }   from '@angular/forms';//表单验证必须导入这两个模块
    
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        FormsModule,    //注册模块
        ReactiveFormsModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { } 
    app.component.ts

    import { Component,OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators } from '@angular/forms';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'app';
      Form:FormGroup;
      data={
      	name:"",
      	email:"",
      	tel:""
      }
      ngOnInit(): void {
    	  this.Form = new FormGroup({
    		    'name': new FormControl(this.data.name, [
    		      Validators.required,
    		      Validators.minLength(4)
    		    ]),
    		    'email': new FormControl(this.data.email, Validators.required),
    		    'tel': new FormControl(this.data.tel, Validators.required)
    		  });
    	}
    
    	get name() { return this.Form.get('name'); }
    	get email() { return this.Form.get('email'); }
    	get tel() { return this.Form.get('tel'); }
    }
    简单来说,在使用验证表单的时候,大致分为四步:

    (1)导入相关模块FormGroup, FormControl, Validators;

    (2)声明表单验证变量From:FromGroup;

    (3)定义验证规则;

    (4)通过它所属的控件组(FormGroup)的get方法来访问表单控件

    app.component.html

    <div class="wrapper">
    	<div class="row">
    		<p class="title-wrapper">注册账号</p>
    	</div>
        <div class="row">
    	    <div class="contain-wrapper"  [formGroup]="Form">
    	        <label for="name">账户名:</label>
    	        <input type="text" id="name" formControlName="name"><br/>
    	        <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
                    <div *ngIf="name.errors.required">
                        请输入长度账户名!
                    </div>
                    <div *ngIf="name.errors.minlength">
                        账户名长度不小于4!
                    </div>
                </div>
    	        <label for="email">邮箱:</label>
    	        <input type="text" id="email" formControlName="email"><br/>
    	        <div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
                    <div *ngIf="email.errors.required">
                        请输入邮箱!
                    </div>
                </div>
    	        <label for="tel">电话:</label>
    	        <input type="text" id="tel" formControlName="tel">
    	        <div *ngIf="tel.invalid && (tel.dirty || tel.touched)" class="alert alert-danger">
                    <div *ngIf="tel.errors.required">
                        请输入电话!
                    </div>
                </div>
    	    </div>
        </div>
        <div class="row">
    		<button class="btn btn-primary confirm">确认</button>
        </div>
    </div>
    app.component.css

    *{
    	font-size: 18px;
    }
    .wrapper{
    	margin: 0 auto;
    	margin-top:10%;
    	width:30%;
    	height: 20%;
    	border:1px solid black;
    	border-radius: 10px;
    }
    
    .title-wrapper{
    	margin: 0 auto;
    	padding-top: 20px; 
    	padding-bottom: 20px;
    	width:370px;
    	text-align: center;
    	font-size: 20px;
    	font-weight: 800;
    }
    label{
    	display: inline-block;
    	width:72px;
    }
    .contain-wrapper{
    	width: 300px;
    	margin:0 auto;
    }
    .confirm{
    	margin-top:20px;
    	width:100%;
    
    }

    3.项目效果


    二、自定义验证

    自定义表单验证,需要创建自定义验证器,我们接下来更改邮箱的验证,将其改为有格式的验证,而不是单纯的存在验证,首先我们来看一下项目目录的更改

    1.项目目录

    ----------app.component.ts

    ----------app.component.html

    ----------app.component.css

    ----------app.module.ts

    ----------emailAuthentication.ts

    2.项目代码

    app.module.ts

    注册自定义验证器EmailValidatorDirective

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule,ReactiveFormsModule  }   from '@angular/forms';
    import { EmailValidatorDirective } from './emailAuthentication';
    
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent,
        EmailValidatorDirective
      ],
      imports: [
        BrowserModule,
        FormsModule,
        ReactiveFormsModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    emailAuthentication.ts

    import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
    import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } from '@angular/forms';
    
    /** A hero's name can't match the given regular expression */
    export function emailValidator(nameRe: RegExp): ValidatorFn {
        return (control: AbstractControl): { [key: string]: any } => {
            const forbidden = !nameRe.test(control.value);
            return forbidden ? { 'forbiddenName': { value: control.value } } : null;
        };
    }
    
    @Directive({
        selector: '[appForbiddenName]',
        providers: [{ provide: NG_VALIDATORS, useExisting: EmailValidatorDirective, multi: true }]
    })
    export class EmailValidatorDirective implements Validator {
        @Input() forbiddenName: string;
    
        validate(control: AbstractControl): { [key: string]: any } {
            return this.forbiddenName ? emailValidator(new RegExp(this.forbiddenName, 'i'))(control)
                : null;
        }
    }
    
    app.component.ts

    import { Component,OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators } from '@angular/forms';
    import { emailValidator } from './emailAuthentication'; //导入emailValidator自定义验证器
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'app';
      //email的正则表达式
      emailExp = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/ ;
      Form:FormGroup;
      data={
      	name:"",
      	email:"",
      	tel:""
      }
      ngOnInit(): void {
    	  this.Form = new FormGroup({
    		    'name': new FormControl(this.data.name, [
    		      Validators.required,
    		      Validators.minLength(4)
    		    ]),
    		    'email': new FormControl(this.data.email, [
    		    	Validators.required,
    				emailValidator(this.emailExp) //自定义验证器
    		    	]),
    		    'tel': new FormControl(this.data.tel, Validators.required)
    		  });
    	}
    
    	get name() { return this.Form.get('name'); }
    	get email() { return this.Form.get('email'); }
    	get tel() { return this.Form.get('tel'); }
    }
    
    app.component.html

    <div class="wrapper">
    	<div class="row">
    		<p class="title-wrapper">注册账号</p>
    	</div>
        <div class="row">
    	    <div class="contain-wrapper"  [formGroup]="Form">
    	        <label for="name">账户名:</label>
    	        <input type="text" id="name" formControlName="name"><br/>
    	        <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
                    <div *ngIf="name.errors.required">
                        请输入账户名!
                    </div>
                    <div *ngIf="name.errors.minlength">
                        账户名长度不小于4!
                    </div>
                </div>
    	        <label for="email">邮箱:</label>
    	        <input type="text" id="email" formControlName="email" required><br/>
    	        <div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
                    <div *ngIf="email.errors.forbiddenName">
                      请输入正确格式的邮箱!
                    </div>
                </div>
    	        <label for="tel">电话:</label>
    	        <input type="text" id="tel" formControlName="tel">
    	        <div *ngIf="tel.invalid && (tel.dirty || tel.touched)" class="alert alert-danger">
                    <div *ngIf="tel.errors.required">
                        请输入电话!
                    </div>
                </div>
    	    </div>
        </div>
        <div class="row">
    		<button class="btn btn-primary confirm" [disabled]="Form.invalid" >确认</button>
        </div>
    </div>

    在最后确认的时候,我们设置一下按钮的disabled属性,在表单验证不通过的时候,确认按钮是点击不了的,显示不可点击状态。[disabled]="Form.invalid"。

    3.项目效果



    三、注意

    在使用表单验证的时候,拿我们上面的例子来说,

    1.要记得在相对应的范围标签里面加入[formGroup]="From",否则会出现下面错误:


    2.要时刻注意要验证的表单控件有没有被包含在<div class="contain-wrapper"  [formGroup]="Form">里面,如果没有被包含在相应的验证范围里面,表单的验证是不生效的。




    展开全文
  • angular表单验证

    2021-04-08 14:09:44
    表单验证 通常,我们都需要对用户的表单输入做验证,以保证数据的整体质量。 Angular也有两种验证表单的形式: 使用属性验证,用于模板驱动表单; 使用验证器函数进行验证,用于响应式表单。 验证器(Validator)...

    表单验证

    通常,我们都需要对用户的表单输入做验证,以保证数据的整体质量。

    Angular也有两种验证表单的形式:

    • 使用属性验证,用于模板驱动表单;
    • 使用验证器函数进行验证,用于响应式表单。

    验证器(Validator)函数

    验证器函数可以是同步函数,也可以是异步函数。

    • 同步验证器:接受控件实例,然后返回验证错误信息或 null。在实例化一个 FormControl时把它作为构造函数的第二个参数传进去;
    • 异步验证器 :接受实例并返回一个 PromiseObservable,稍后会发出一组验证错误或 null。也是在实例化 FormControl时,作为第三个参数传入。

    内置验证器函数

    Angular内置了一些基础功能的验证器,在日常开发中可以直接使用:

    在这里插入图片描述

    我们来简单使用一下内置的验证器:

    // reactive-forms.component.ts
    import {Validators} from '@angular/forms';
    ...
    profileForm = this.fb.group({
      firstName: ['', Validators.required], // 必填
      lastName: ['',
        [Validators.required, Validators.minLength(4)]
      ], // 必填并且最小长度为4
      ...
    });
    
    // 因为我们可能多次获取表单中元素,所以先获取
    get firstName() { return this.profileForm.get('firstName'); }
    get lastName() { return this.profileForm.get('lastName'); }
    
    <!-- reactive-forms.component.html -->
    <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label>
          First Name: <input type="text" formControlName="firstName" class="form-control form-control-sm">
        </label>
        <div class="alert alert-danger">
          valid: {{firstName.valid | json}} <br>
          errors: {{firstName.errors | json}}
        </div>
      </div>
      <div class="form-group">
        <label>
          Last Name: <input type="text" formControlName="lastName" class="form-control form-control-sm">
        </label>
        <div class="alert alert-danger">
          valid: {{lastName.valid | json}} <br>
          errors: {{lastName.errors | json}}
        </div>
      </div>
      
    </form>
    

    效果是这样的:

    在这里插入图片描述

    可以看出:必填验证一开始都没有通过,验证顺序是跟添加验证器的顺序一致。

    上一节最后我们不是介绍过 Angular跟踪控件状态吗,那就可以根据状态去控制错误提示。

    在日常开发中,当我们表单是必填项,初始化页面时,我们是不应该提示错误,并且,如果用户聚焦后并没有输入任何值的时候,也是不应该提示错误的。

    <!-- reactive-forms.component.html -->
    <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label>
          First Name: <input type="text" formControlName="firstName" class="form-control form-control-sm">
        </label>
        <div *ngIf="firstName.dirty && firstName.errors" class="alert alert-danger">
          <span>请填写first name</span>
        </div>
      </div>
      <div class="form-group">
        <label>
          Last Name: <input type="text" formControlName="lastName" class="form-control form-control-sm">
        </label>
        <div *ngIf="lastName.dirty && lastName.errors as errors" class="alert alert-danger">
          <span *ngIf="errors.required">请填写last name</span>
          <span *ngIf="errors.minlength">last name应该至少4个字符</span>
        </div>
      </div>
    </form>
    

    在这里插入图片描述

    上面的dirtyAbstractControl类的属性,用于判定控件是否修改过控件的值。

    AbstractControl类还有其他监控用户操作控件的属性:

    • pristine: boolean如果用户尚未修改 UI中的值,则该控件是 pristine(原始状态)的。
    • touched: boolean一旦用户在控件上触发了 blur事件,则会将其标记为 touched
    • untouched: boolean如果用户尚未在控件上触发过 blur事件,则该控件为 untouched

    定义自定义验证器

    内置的验证器并不是总能适用于我们的需求,因此需要创建自定义验证器。

    创建自定义验证器,我们需要遵从 Angular中的创建规则:

    • 验证器函数的返回值必须是 ValidatorFn类型;

      ValidatorFn实际上是一个接口,里面只有一个方法,将一个表单控件传入,返回验证错误信息( ValidationErrors)或者 null

    在这里插入图片描述

    • ValidationErrors是一个 key: value的对象

    在这里插入图片描述

    搞清楚了规则,我们的验证器函数至少应该是这样的:

    function validatorName(): ValidatorFn {
      return (control: AbstractControl): ValidationErrors | null => {
        const isValid: boolean;
        return isValid ? null : {validateName: 'error info'};
      };
    }
    

    按照上面的样子,我们来写一个通过正则验证字符串中不能包含特定字符的验证器函数(新建validators.ts):

    // validators.ts
    import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
    
    // 传入一个正则以及错误提示
    export function forbiddenNameValidator(reg: RegExp, errorTips: string): ValidatorFn {
      return (control: AbstractControl): ValidationErrors | null => {
        const forbidden = reg.test(control.value);
        return forbidden ? {forbiddenName: {value: errorTips}} : null;
      };
    }
    

    添加验证器:

    // reactive-forms.component.ts
    import {forbiddenNameValidator} from '../validators';
    ...
    lastName: ['',
      [
        Validators.required,
        Validators.minLength(4),
        forbiddenNameValidator(/bob/i, '不能包含\"bob\"')
      ]
    ],
    ...
    

    使用自定义验证器:

    <!-- reactive-forms.component.html -->
    <div class="form-group">
      <label>
        Last Name: <input type="text" formControlName="lastName" class="form-control form-control-sm">
      </label>
      <div *ngIf="lastName.dirty && lastName.errors as errors" class="alert alert-danger">
        <span *ngIf="errors.required">请填写last name</span>
        <span *ngIf="errors.minlength">last name应该至少4个字符</span>
        <span *ngIf="errors.forbiddenName">{{errors.forbiddenName.value}}</span>
      </div>
    </div>
    

    在这里插入图片描述

    跨字段交叉验证

    跨字段交叉验证其实就是对一组字段进行的验证,后面的值依赖于前面的值,实质也是一个自定义验证器。最常见的使用场景就是密码相同验证。

    先将密码的基础架子搭起来:

    // reactive-forms.component.ts
    profileForm = this.fb.group({
      ...
      newPass: this.fb.group({
        password: ['', Validators.required],
        rePassword: ['', [Validators.required]]
      }),
    });
    get newPass() { return this.profileForm.get('newPass'); }
    get password() { return this.profileForm.get('newPass').get('password'); }
    get rePassword() { return this.profileForm.get('newPass').get('rePassword'); }
    
    <!-- reactive-forms.component.html -->
    <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
      ...
      <div formGroupName="newPass">
        <div class="form-group">
          <label>
            输入密码: <input type="text" formControlName="password" class="form-control form-control-sm">
          </label>
          <div *ngIf="password.dirty && password.errors" class="alert alert-danger">
            <span>请填写密码</span>
          </div>
        </div>
        <div class="form-group">
          <label>
            再次输入密码: <input type="text" formControlName="rePassword" class="form-control form-control-sm">
          </label>
          {{rePassword}}
          <div *ngIf="rePassword.dirty && rePassword.errors" class="alert alert-danger">
            <span>请再次填写密码</span>
          </div>
        </div>
      </div>
    </form>
    

    自定义密码一致验证器函数:

    // validators.ts
    export function equalValidator(ctrl: AbstractControl): Validators | null {
      const password = ctrl.get('password');
      const rePassword = ctrl.get('rePassword');
      return password?.value === rePassword?.value ? null : {equal: '两次密码不一致'};
    }
    

    使用跨字段交叉验证:

    FormBuilder下面的 group方法可以接收第二个参数,我们将验证器添加到里面:

    在这里插入图片描述

    // reactive-forms.component.ts
    import {equalValidator} from '../validators';
    newPass: this.fb.group({
      password: ['', Validators.required],
      rePassword: ['', [Validators.required]]
    }, {validators: equalValidator}),
    

    模板中错误提示:

    <!-- reactive-forms.component.html -->
    <div *ngIf="password.dirty && rePassword.dirty && newPass.errors" class="alert alert-danger">
      <span>{{newPass.errors.equal}}</span>
    </div>
    

    在这里插入图片描述

    异步验证器

    有的时候,用户填写的内容需要从服务器上的数据进行匹配,从而得出验证结果。这个时候,就需要使用异步验证器。

    比如用户注册时候,需要验证用户手机号码是否已经使用。我们也将通过这个需求来介绍异步验证器。

    先搭好静态的结构:

    // reactive-forms.component.ts
    profileForm = this.fb.group({
      mobile: ['',
        [
          Validators.required,
          Validators.pattern(/^\d{3}$/) // 这里的正则就简写成3位数字
        ]
      ],
    });
    get mobile() { return this.profileForm.get('mobile'); }
    
    <!-- reactive-forms.component.html -->
    <div *ngIf="password.dirty && rePassword.dirty && newPass.errors" class="alert alert-danger">
      <span>{{newPass.errors.equal}}</span>
    </div>
    

    通常情况下,我们获取服务器数据是在一个服务里面,那将异步验证器也放在服务里。

    新建一个 hasMobile服务:

    ng g s forms-study/hasMobile
    

    在服务中实现 AsyncValidator接口:

    // has-mobile.service.ts
    export class HasMobileService implements AsyncValidator{
      // 我们这里直接模拟一下,实际项目中需要拿到服务器数据进行匹配
      validate(control: AbstractControl): Observable<ValidationErrors | null> {
        const random = Math.random();
        return iif(
          () => random > 0.5,
          of({ exists: '该手机已被注册'}),
          of(null)
        );
      }
    }
    

    使用异步验证器:

    // reactive-forms.component.ts
    constructor(private fb: FormBuilder, private hasMobileServer: HasMobileService) { }
    profileForm = this.fb.group({
      mobile: ['',
        [
          Validators.required,
          Validators.pattern(/^\d{3}$/) // 这里的正则就简写成3位数字
        ],
        this.hasMobileServer.validate // 第三个参数接收一个异步验证器
      ],
    });
    get mobile() { return this.profileForm.get('mobile'); }
    
    <!-- reactive-forms.component.html -->
    <div class="form-group">
      <label>
        手机号: <input type="text" formControlName="mobile" class="form-control form-control-sm">
      </label>
      <div *ngIf="mobile.dirty && mobile.errors as errors" class="alert alert-danger">
        <span *ngIf="errors.required">请填写手机号</span>
        <span *ngIf="errors.pattern">手机号格式不正确</span>
        <span *ngIf="errors.exists">{{errors.exists}}</span>
      </div>
    </div>
    

    在这里插入图片描述

    出于性能方面的考虑,只有在所有同步验证器都通过之后,Angular才会运行异步验证器。

    Angular默认在输入框值发生变化时进行校验。如果我们的验证器中有异步验证器,每次变化都会向服务器发送请求,这是一个很消耗性能的操作。所以我们可以修改触发验证的时机为失去焦点时。

    // reactive-forms.component.ts
    mobile: ['', {
      validators: [
        Validators.required,
        Validators.pattern(/^\d{3}$/) // 这里的正则就简写成3位数字
      ],
      asyncValidators: this.hasMobileServer.validate,
      updateOn: 'blur'
    }],
    

    属性验证器

    顾名思义,属性验证器是使用属性的形式来进行验证。同样也有内置验证器与自定义验证器。

    我们将在上一节的模版驱动组件中演示属性验证器。

    内置验证器

    同样的,我们还是限定 firstName为必填项目,lastName为必填且最小长度为4的验证规则。

    修改模版文件,给需要验证的控件添加必要的属性并拷贝前面的错误提示:

    <!-- template-forms.component.html -->
    <div class="form-group" >
      <label>
        <!-- 添加规则 required: 必填  -->
        First Name: <input type="text" required [(ngModel)]="model.firstName" name="firstName" class="form-control form-control-sm">
      </label>
      <!-- 拷贝的是下面的内容 -->
      <div *ngIf="firstName.dirty && firstName.errors" class="alert alert-danger">
        <span>请填写first name</span>
      </div>
    </div>
    <div class="form-group">
      <label>
        <!-- 添加规则 required: 必填 minlength="4" -->
        Last Name: <input type="text" required minlength="4" [(ngModel)]="model.lastName" name="lastName" required class="form-control form-control-sm">
      </label>
      <!-- 拷贝的是下面的内容 -->
      <div *ngIf="lastName.dirty && lastName.errors as errors" class="alert alert-danger">
        <span *ngIf="errors.required">请填写last name</span>
        <span *ngIf="errors.minlength">last name应该至少4个字符</span>
      </div>
    </div>
    

    这样,很显然是会报错,因为根本就没有 firstNamelastName对象。
    想要跟响应式表单一样使用 FormControl对象,我们还需要在模版控件上把 ngModel导出成局部模板变量:

    <!-- template-forms.component.html -->
    ...
    <label>First Name:
      <input
        type="text"
        required
        [(ngModel)]="model.firstName"
        #firstName="ngModel"
        name="firstName"
        class="form-control form-control-sm">
    </label>
    ...
    <label>Last Name:
      <input 
        type="text" 
        required
        minlength="4"
        [(ngModel)]="model.lastName"
        #lastName="ngModel"
        name="lastName"
        class="form-control form-control-sm">
    </label>
    ...
    

    具体效果跟前面没任何差别,所以就不用图示范了。

    自定义验证器

    在模板驱动表单中,要使用自定义验证器,就要为模板添加一个指令,该指令包含了 validator函数。

    新建 ForbiddenValidator指令:

    ng g d forms-study/ForbiddenValidator
    

    同样的,需要实现 Validator接口:

    // forbidden-validator.directive.ts
    import {Directive, Input} from '@angular/core';
    import {AbstractControl, ValidationErrors, Validator} from '@angular/forms';
    import {forbiddenNameValidator} from './validators';
    
    @Directive({
      // 选择器修改成与输入属性一致
      selector: '[appForbiddenName]',
      // 把自己注册成了 NG_VALIDATORS 提供者,提供同步验证器。 multi表示一个令牌可以提供多个服务
      providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
    })
    export class ForbiddenValidatorDirective implements Validator {
      // 定义输入属性,正则表达式  
      @Input('appForbiddenName') forbiddenName: string;
      // 错误提示
      @Input('appForbiddenNameTips') forbiddenNameTips: string;
      // 实现 validate 方法
      validate(control: AbstractControl): ValidationErrors | null {
        return this.forbiddenName ?
          forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'), this.forbiddenNameTips)(control) :
          null;
      }
    }
    

    使用自定义验证器:

    <!-- template-forms.component.html -->
    ...
    <div class="form-group">
      <label>Last Name:
        <input type="text"
               required
               minlength="4"
               [(ngModel)]="model.lastName"
               #lastName="ngModel"
               appForbiddenName="bob"
               appForbiddenNameTips="不能包含bob"
               name="lastName"
               class="form-control form-control-sm">
      </label>
      <div *ngIf="lastName.dirty && lastName.errors as errors" class="alert alert-danger">
        ...
        <span *ngIf="errors.forbiddenName">{{errors.forbiddenName.value}}</span>
      </div>
    </div>
    

    这样,我们就实现了跟上面一致效果的自定义验证器。

    跨字段交叉验证

    我们同样使用密码的例子进行示范。修改代码:

    <!-- template-forms.component.html -->
    <div class="form-group">
      <label>
        输入密码: <input type="text" required [(ngModel)]="model.password" #password="ngModel" name="password" class="form-control form-control-sm">
      </label>
      <div *ngIf="password.dirty && password.errors" class="alert alert-danger">
        <span>请填写密码</span>
      </div>
    </div> 
    <div class="form-group">
      <label>
        再次输入密码: <input type="text" required [(ngModel)]="model.rePassword" #rePassword="ngModel" name="rePassword" class="form-control form-control-sm">
      </label>
      <div *ngIf="rePassword.dirty && rePassword.errors" class="alert alert-danger">
        <span>请填写密码</span>
      </div>
    </div>    
    

    前面已经说过,交叉验证也是一种自定义验证器,所以,我们还是需要新建一个指令:

    ng g d forms-study/PsdEqualValidator
    

    同样需要实现 Validator接口:

    // psd-equal-validator.directive.ts
    import { Directive } from '@angular/core';
    import {AbstractControl, NG_VALIDATORS, ValidationErrors, Validator} from '@angular/forms';
    import {equalValidator} from './validators';
    
    @Directive({
      selector: '[appPsdEqualValidator]',
      providers: [{provide: NG_VALIDATORS, useExisting: PsdEqualValidatorDirective, multi: true}]
    })
    export class PsdEqualValidatorDirective implements Validator{
      validate(control: AbstractControl): ValidationErrors | null {
        return equalValidator(control);
      }
    }
    

    因为模版驱动这里没有表单嵌套一说,所以我们需要在顶级表单中使用交叉验证器:

    <!-- template-forms.component.html -->
    <form #profileForm="ngForm" appPsdEqualValidator (ngSubmit)="onSubmit(profileForm)">
      ...
      <div *ngIf="password.dirty && rePassword.dirty && profileForm.errors" class="alert alert-danger">
        <span>{{profileForm.errors.equal}}</span>
      </div>
    </form>
    

    异步验证器

    同理,创建一个 hasMobile指令并实现 AsyncValidator接口:

    // has-mobile-validator.directive.ts
    import { Directive } from '@angular/core';
    import {AbstractControl, AsyncValidator, NG_ASYNC_VALIDATORS, ValidationErrors} from '@angular/forms';
    import {Observable} from 'rxjs';
    import {HasMobileService} from './has-mobile.service';
    
    @Directive({
      selector: '[appHasMobileValidator]',
      // 注意:这里是 NG_ASYNC_VALIDATORS
      providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: HasMobileValidatorDirective, multi: true}]
    })
    export class HasMobileValidatorDirective implements AsyncValidator{
      constructor(private hasMobileServer: HasMobileService) { }
      validate(control: AbstractControl): Observable<ValidationErrors | null> {
        return this.hasMobileServer.validate(control);
      }
    }
    

    使用验证器,并设置验证时机为失去焦点时触发:

    <!-- template-forms.component.html -->
    <div class="form-group">
      <label>
        手机号: <input
        type="text"
        required
        pattern="\d{3}"
        [(ngModel)]="model.mobile"
        [ngModelOptions]="{updateOn: 'blur'}"
        #mobile="ngModel"
        appHasMobileValidator
        name="mobile"
        class="form-control form-control-sm">
      </label>
      <div *ngIf="mobile.dirty && mobile.errors as errors" class="alert alert-danger">
        <span *ngIf="errors.required">请填写手机号</span>
        <span *ngIf="errors.pattern">手机号格式不正确</span>
        <span *ngIf="errors.exists">{{errors.exists}}</span>
      </div>
    </div>
    

    总结

    这一节我们介绍了表单验证大部分知识,其他未提到的在使用时查找官方文档即可。

    1. 本节主要介绍了响应式表单的验证及模板驱动表单的验证;
    2. 他们都可以使用内置验证器、自定义验证器、交叉验证器、异步验证器;
    3. 创建自定义验证器需要遵从 Angular相关规则,实现对应接口以及返回 ValidationErrors或者 null;
    4. 响应式表单都异步验证器通过服务实现,模版驱动表单的自定义验证器是通过指令实现。

    欢迎关注我的公众号,公众号将第一时间更新angular教程:
    在这里插入图片描述

    展开全文
  • angular-w5c-validator, Angular 表单验证的简单插件 w5c-validatordemo演示:http://why520crazy.github.io/angular-w5c-validator更改日志:https://github.com/why520crazy/angular-w5
  • 主要为大家详细介绍了Angular实现表单验证功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 表单验证是经常用到一个东西,这篇文章主要介绍了angular 表单验证器验证的同时限制输入的实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Angular 表单验证

    2020-03-16 18:29:42
    Angular表单验证学习记录

    模板驱动验证

    使用模板驱动验证需要依赖于原生的HTML表单验证器 Angular 会用指令来匹配具有验证功能的这些属性。

    原生的HTMl验证器主要分两种

    1. 通过语义类型来进行定义
    2. 通过验证相关的属性来进行定义

    语义类型

    Input typeConstraint descriptionAssociated violation
    <input type="URL">The value must be an absolute URL, as defined in the URL Living Standard.TypeMismatch constraint violation
    <input type="email">The value must be a syntactically valid email address, which generally has the format username@hostname.tld.TypeMismatch constraint violation

     验证相关属性

    AttributeInput types supporting the attributePossible valuesConstraint descriptionAssociated violation
    patterntext, search, url, tel, email, passwordA JavaScript regular expression (compiled with the ECMAScript 5 global, ignoreCase, and multiline flags disabled)The value must match the pattern.patternMismatch constraint violation
    minrange, numberA valid numberThe value must be greater than or equal to the value.rangeUnderflow constraint violation
    date, month, weekA valid date
    datetime, datetime-local, timeA valid date and time
    maxrange, numberA valid numberThe value must be less than or equal to the valuerangeOverflow constraint violation
    date, month, weekA valid date
    datetime, datetime-local, timeA valid date and time
    requiredtext, search, url, tel, email, password, date, datetime, datetime-local, month, week, time, number, checkbox, radio, file; also on the <select> and <textarea> elementsnone as it is a Boolean attribute: its presence means true, its absence means falseThere must be a value (if set).valueMissing constraint violation
    stepdateAn integer number of daysUnless the step is set to the any literal, the value must be min + an integral multiple of the step.stepMismatch constraint violation
    monthAn integer number of months
    weekAn integer number of weeks
    datetime, datetime-local, timeAn integer number of seconds
    range, numberAn integer
    minlengthtext, search, url, tel, email, password; also on the <textarea> elementAn integer lengthThe number of characters (code points) must not be less than the value of the attribute, if non-empty. All newlines are normalized to a single character (as opposed to CRLF pairs) for <textarea>.tooShort constraint violation
    maxlengthtext, search, url, tel, email, password; also on the <textarea> elementAn integer lengthThe number of characters (code points) must not exceed the value of the attribute.tooLong constraint violation

     

     每当表单控件中的值发生变化时,Angular就会进行验证,并生成一个验证错误的列表(对应着INVALID状态)或者null(对应着VALID状态);

    可以通过吧ngModel导出成局部模板变量来查看控件的状态,比如像下面这样

    <input id="name" name="name" class="form-control" required minlength="4" appForbiddenName="bob"
          [(ngModel)]="hero.name" #name="ngModel" >
    
    <div *ngIf="name.invalid && (name.dirty || name.touched)"
        class="alert alert-danger">
    
      <div *ngIf="name.errors.required">
        Name is required.
      </div>
      <div *ngIf="name.errors.minlength">
        Name must be at least 4 characters long.
      </div>
      <div *ngIf="name.errors.forbiddenName">
        Name cannot be Bob.
      </div>
    </div>

     #name="ngModel"NgModel 导出成了一个名叫 name 的局部变量。NgModel 把自己控制的 FormControl 实例的属性映射出去,让你能在模板中检查控件的状态,比如 validdirty。要了解完整的控件属性,参见 API 参考手册中的AbstractControl

    响应式表单的验证

    响应式表单控制的源头在组件类,就不能通过模板上的属性来添加验证器了,而是直接在组件类中直接把验证器函数添加到表单控件模型(FormControl)上。当控件发生变化的时候就会调用这些函数。

    ngOnInit(): void {
      this.heroForm = new FormGroup({
        'name': new FormControl(this.hero.name, [
          Validators.required,
          Validators.minLength(4),
          forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
        ]),
        'alterEgo': new FormControl(this.hero.alterEgo),
        'power': new FormControl(this.hero.power, Validators.required)
      });
    
    }
    
    <input id="name" class="form-control" formControlName="name" required >
    
    <div *ngIf="name.invalid && (name.dirty || name.touched)"
        class="alert alert-danger">
    
      <div *ngIf="name.errors.required">
        Name is required.
      </div>
      <div *ngIf="name.errors.minlength">
        Name must be at least 4 characters long.
      </div>
      <div *ngIf="name.errors.forbiddenName">
        Name cannot be Bob.
      </div>
    </div>
    • 该表单不再导出任何指令,而是使用组件类中定义的 name 读取器。
    • required 属性仍然存在,虽然验证不再需要它,但你仍然要在模板中保留它,以支持 CSS 样式或可访问性。 

    从验证过程上看有两种验证器函数:同步验证器和异步验证器。

    • 同步验证器函数接受一个控件实例,然后返回一组验证错误或null,可以在实例化FormControl的时候把函数作为构造函数的第二个参数传递进去。
    • 异步验证器函数同样也接收一个控件实例,并返回一个承诺(Promise)或可观察对象(Observable),最终函数会返回一组验证错误或者null,可以在实例化FormControl的时候把她当做构造函数的第三个函数传递进去。

    出于性能方面的考虑,只有在所有同步验证器都通过之后,Angular 才会运行异步验证器。当每一个异步验证器都执行完之后,才会设置这些验证错误。

    从验证器来源来看也有两种验证器:内置验证器和自定义验证器

    • 内置验证器与表单中使用的验证器类似,Validators都对应实现了其同名函数,具体如下 详见API
    class Validators {
      static min(min: number): ValidatorFn
      static max(max: number): ValidatorFn
      static required(control: AbstractControl): ValidationErrors | null
      static requiredTrue(control: AbstractControl): ValidationErrors | null
      static email(control: AbstractControl): ValidationErrors | null
      static minLength(minLength: number): ValidatorFn
      static maxLength(maxLength: number): ValidatorFn
      static pattern(pattern: string | RegExp): ValidatorFn
      static nullValidator(control: AbstractControl): ValidationErrors | null
      static compose(validators: ValidatorFn[]): ValidatorFn | null
      static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn | null
    }  
    • 自定义验证器:DEMO 如下,写在一起就能达到即给模板使用又给组件类使用的目的。
    // 声明成指令给模板验证用 shared/forbidden-name.directive.ts (directive) 
    @Directive({
      selector: '[appForbiddenName]',
      providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
    })
    export class ForbiddenValidatorDirective implements Validator {
      @Input('appForbiddenName') forbiddenName: string;
    
      validate(control: AbstractControl): {[key: string]: any} | null {
        return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) : null;
      }
    }
    // 定义函数给验证方法用
    export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
      return (control: AbstractControl): {[key: string]: any} | null => {
        const forbidden = nameRe.test(control.value);
        return forbidden ? {'forbiddenName': {value: control.value}} : null;
      };
    }

    这个函数实际上是一个工厂,它接受一个用来检测指定名字是否已被禁用的正则表达式,并返回一个验证器函数。

    forbiddenNameValidator 工厂函数返回配置好的验证器函数。 该函数接受一个 Angular 控制器对象,并在控制器值有效时返回 null,或无效时返回验证错误对象。 验证错误对象通常有一个名为验证秘钥(forbiddenName)的属性。其值为一个任意词典,你可以用来插入错误信息({name})。

    自定义异步验证器和同步验证器很像,只是它们必须返回一个稍后会输出 null 或“验证错误对象”的承诺(Promise)或可观察对象,如果是可观察对象,那么它必须在某个时间点被完成(complete),那时候这个表单就会使用它输出的最后一个值作为验证结果。(译注:HTTP 服务是自动完成的,但是某些自定义的可观察对象可能需要手动调用 complete 方法)

    表示控件状态的CSS类

    Angular 会自动把很多控件属性作为 CSS 类映射到控件所在的元素上。你可以使用这些类来根据表单状态给表单控件元素添加样式。目前支持下列类:

    .ng-valid

    .ng-invalid

    .ng-pending

    .ng-pristine

    .ng-dirty

    .ng-untouched

    .ng-touched

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


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

    跨字段交叉验证

    除了单独的控件验证之外,有时候还需要多控件的联合验证,这个时候就需要用到跨字段的交叉验证方式。先粘代码

    • 这个身份验证器实现了 ValidatorFn 接口。它接收一个 Angular 表单控件对象作为参数,当表单有效时,它返回一个 null,否则返回 ValidationErrors 对象。
    • 我们先通过调用 FormGroupget 方法来获取子控件。然后,简单地比较一下 namealterEgo 控件的值。
    // 先定义指令给模板验证使用,指令调用导出的验证方法
    @Directive({
      selector: '[appIdentityRevealed]',
      providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
    })
    export class IdentityRevealedValidatorDirective implements Validator {
      validate(control: AbstractControl): ValidationErrors {
        return identityRevealedValidator(control)
      }
    }
    // 下面的方法给指令使用也可以给组件类使用
    export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
      const name = control.get('name');
      const alterEgo = control.get('alterEgo');
    
      return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
    };

     响应式表单验证:需要在创建FormGroup时把一个新的验证器传给他的第二个参数.

    const heroForm = new FormGroup({
      'name': new FormControl(),
      'alterEgo': new FormControl(),
      'power': new FormControl()
    }, { validators: identityRevealedValidator });

     模板驱动表单验证:我们要把该指令添加到 HTML 模板中。由于验证器必须注册在表单的最高层,所以我们要把该指令放在 form 标签上。

    <form #heroForm="ngForm" appIdentityRevealed>
    
    <!-- 错误输出-->
    <div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" 
    class="cross-validation-error-message alert alert-danger">
        Name cannot match alter ego.
    </div>

    异步验证器

    就像同步验证器有 ValidatorFnValidator 接口一样,异步验证器也有自己对应的接口:AsyncValidatorFnAsyncValidator

    它们非常像,但是有下列不同:

    • 它们必须返回承诺(Promise)或可观察对象(Observable),

    • 返回的可观察对象必须是有限的,也就是说,它必须在某个时间点结束(complete)。要把无尽的可观察对象转换成有限的,可以使用 firstlasttaketakeUntil 等过滤型管道对其进行处理。

    注意!异步验证总是会在同步验证之后执行,并且只有当同步验证成功了之后才会执行。如果更基本的验证方法已经失败了,那么这能让表单避免进行可能会很昂贵的异步验证过程,比如 HTTP 请求。

    在异步验证器开始之后,表单控件会进入 pending 状态。你可以监视该控件的 pending 属性,利用它来给用户一些视觉反馈,表明正在进行验证。

    常见的 UI 处理模式是在执行异步验证时显示一个旋转指示标(spinner)。下面的例子展示了在模板驱动表单中该怎么做:

    <input [(ngModel)]="name" #model="ngModel" appSomeAsyncValidator>
    <app-spinner *ngIf="model.pending"></app-spinner>

    实现自定义验证器

    @Injectable({ providedIn: 'root' })
    export class UniqueAlterEgoValidator implements AsyncValidator {
      constructor(private heroesService: HeroesService) {}
    
      validate(
        ctrl: AbstractControl
      ): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
        return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
          map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
          catchError(() => of(null))
        );
      }
    }
    // HeroesService 负责向英雄数据库发起一个 HTTP 请求
    interface HeroesService {
      isAlterEgoTaken: (alterEgo: string) => Observable<boolean>;
    }

    当验证开始的时候,UniqueAlterEgoValidator 把任务委托给 HeroesServiceisAlterEgoTaken() 方法,并传入当前控件的值。这时候,该控件会被标记为 pending 状态,直到 validate() 方法所返回的可观察对象完成(complete)了。

    isAlterEgoTaken() 方法会发出一个 HTTP 请求,并返回一个 Observable<boolean> 型结果。我们通过 map 操作符把响应对象串起来,并把它转换成一个有效性结果。 与往常一样,如果表单有效则返回 null,否则返回 ValidationErrors。我们还是用 catchError 操作符来确保对任何潜在错误都进行了处理。

    这里,我们决定将 isAlterEgoTaken() 中的错误视为成功验证。你也可以将其视为失败,并返回 ValidationError 对象。

    一段时间之后,可观察对象完成了,异步验证也就结束了。这时候 pending 标志就改成了 false,并且表单的有效性也更新了。

    性能上的注意事项

    默认情况下,每当表单值变化之后,都会执行所有验证器。对于同步验证器,没有什么会显著影响应用性能的地方。不过,异步验证器通常会执行某种 HTTP 请求来对控件进行验证。如果在每次按键之后都发出 HTTP 请求会给后端 API 带来沉重的负担,应该尽量避免。

    我们可以把 updateOn 属性从 change(默认值)改成 submitblur 来推迟表单验证的更新时机。

    对于模板驱动表单:

    <input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">

    对于响应式表单:

    new FormControl('', {updateOn: 'blur'});

     

    展开全文
  • 我去,我感觉我这个人其实还是一个很傻逼的一个人,老是因为拼错了一个单词或者怎么样就浪费我很长时间,这样真的不行不行,要正确对待这个问题,好了,说正题吧,angular也有表单验证minlength,maxlength,required...
  • 最近手上维护的组件剩下的BUG都是表单验证,而且公司的表单验证那块代码经历的几代人,里面的逻辑开始变得不清晰,而且代码结构不是很angular。 是很有必要深入了解表单验证。 <body ng-controller=...
  • 原文出处:https://www.jb51.net/article/114488.htm (原文更新阅读)概念请参考:Angular2 自定义validators 注意: 如果在ionic中使用时请在constructor中对buildForm进行初始化 constructor(public navCtrl: ...

    原文出处:https://www.jb51.net/article/114488.htm (原文更新阅读
    概念请参考:
    Angular2 自定义validators
    注意:
    如果在ionic中使用时请在constructor中对buildForm进行初始化

      constructor(public navCtrl: NavController, public navParams: NavParams, private fb: FormBuilder) {
        
        // 初始化时构建表单
        
        this.buildForm();
      
      }
    



    本篇文章主要介绍了强大的 Angular 表单验证功能详细介绍,使用 Angular 的内置表单校验能够完成绝大多数的业务场景的校验需求,有兴趣的可以了解一下

    Angular 支持非常强大的内置表单验证,maxlength、minlength、required 以及 pattern。使用 Angular 的内置表单校验能够完成绝大多数的业务场景的校验需求,但有时我们还需要实现更为复杂的表单校验功能,这时可以使用 Angular 提供的表单自定义校验(Custom Validator)。下面,我们就来了解一下如何使用 Angular 的自定义表单校验

    效果图:

    1、首先,来创建我们的注册组件(register),并在模版中显示一个简单的表单

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    <h3 class="text-center">注册</h3>

     

    <form>

     

     <div class="form-group">

     <label for="username">用户名:</label>

     <input type="text" id="username" class="form-control" >

     </div>

     

    </form>

    为了使表单看上去能够漂亮一些,在 index.html 中引入 bootstrap 样式文件:

    复制代码 代码如下:
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

     

    2、接下来确定我们的验证需求:

     我们希望用户名只能包含数字、字母和下划线,且不能以下划线开头

    首先为 form 标签添加 formGroup 指令:

    1

    <form [formGroup]="registerForm" >

    并且为 input 标签添加 formControlName 指令:

     

    复制代码 代码如下:
    < input formControlName="username" type="text" id="username" class="form-control" >

    3、在代码中定义验证规则:

    从内置表单模块中导入以下类:

    1

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

    其中:

          1. formBuilder 用来构建表单数据
          2. formGroup 表示表单类型
          3. Validators 包含了表单内置的验证规则,如: Validators.required

    定义表单属性

    registerForm: FormGroup;

    定义表单验证不通过时每一项显示的错误消息(目前我们只有 username )

    1

    2

    3

    formErrors = {

    username: ''

    };

    为每一项验证规则定义验证失败时的说明文字(表单控件可能有多条验证规则,由不通过的验证说明构成一条错误消息)

    1

    2

    3

    4

    5

    6

    7

    validationMessage = {

    'username': {

     'minlength': '用户名长度最少为3个字符',

     'maxlength': '用户名长度最多为10个字符',

     'required': '请填写用户名'

    }

    };

    在构造函数中添加 fb 属性用来构建表单

    1

    constructor(private fb: FormBuilder) { }

    添加构建表单的方法

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    buildForm(): void {

    // 通过 formBuilder构建表单

    this.registerForm = this.fb.group({

    /* 为 username 添加3项验证规则:

    * 1.必填, 2.最大长度为10, 3.最小长度为3

    * 其中第一个空字符串参数为表单的默认值

    */

    'username': [ '', [

     Validators.required,

     Validators.maxLength(10),

     Validators.minLength(3)

    ]]

    });

    接下来我们添加一个方法用来更新错误信息

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    onValueChanged(data?: any) {

    // 如果表单不存在则返回

    if (!this.registerForm) return;

    // 获取当前的表单

    const form = this.registerForm;

     

    // 遍历错误消息对象

    for (const field in this.formErrors) {

     // 清空当前的错误消息

     this.formErrors[field] = '';

     // 获取当前表单的控件

     const control = form.get(field);

     

     // 当前表单存在此空间控件 && 此控件没有被修改 && 此控件验证不通过

     if (control && control.dirty && !control.valid) {

     // 获取验证不通过的控件名,为了获取更详细的不通过信息

     const messages = this.validationMessage[field];

     // 遍历当前控件的错误对象,获取到验证不通过的属性

     for (const key in control.errors) {

      // 把所有验证不通过项的说明文字拼接成错误消息

      this.formErrors[field] += messages[key] + '\n';

     }

     }

    }

    }

    下面只需要在表单构建结束后初始化错误消息,并且在每次表单数据更改时更新错误消息就可以了

    在 buildForm 方法中添加如下代码

    1

    2

    3

    4

    5

    6

    // 每次表单数据发生变化的时候更新错误信息

    this.registerForm.valueChanges

    .subscribe(data => this.onValueChanged(data));

     

    // 初始化错误信息

    this.onValueChanged();

    此时,我们已经很好的控制了错误信息,下面只需要在表单模版中添加错误信息的显示就可以了

    在 input 标签下方添加如下代码:

    1

    2

    <div *ngIf="formErrors.username"

    class="showerr alert alert-danger" >{{ formErrors.username }}</div>

    添加如下代码到表单模版的 css 中:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    form {

    width: 90%;

    max-width: 45em;

    margin: auto;

    }

     

    .showerr {

    white-space: pre-wrap;

    }

    现在我们就可以尝试运行了,在代码不报错的情况下已经能够看到非常好的效果了

    如果代码报错或没有出现想象中的效果则可以参照本文结尾的完整代码进行修改

    4、虽然我们已经搭建了整个布局,但是还没有实现我们的最终目的:实现自定义的表单验证
    接下来我们创建一个正则验证器,新建文件 validate-register.ts :

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    import { ValidatorFn, AbstractControl } from '@angular/forms';

     

    export function validateRex(type: string, validateRex: RegExp): ValidatorFn {

    return (control: AbstractControl): {[key: string]: any} => {

     // 获取当前控件的内容

     const str = control.value;

     // 设置我们自定义的验证类型

     const res = {};

     res[type] = {str}

     // 如果验证通过则返回 null 否则返回一个对象(包含我们自定义的属性)

     return validateRex.test(str) ? null : res;

    }

    }

    下面我们在代码中导入此函数:

    1

    import { validateRex } from './validate-register';

    修改 validationMessage 属性为:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    // 为每一项表单验证添加说明文字

    validationMessage = {

    'username': {

     'minlength': '用户名长度最少为3个字符',

     'maxlength': '用户名长度最多为10个字符',

     'required': '请填写用户名',

     'notdown': '用户名不能以下划线开头',

     'only': '用户名只能包含数字、字母、下划线'

    }

    };

    修改 buildForm 方法:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    // 通过 formBuilder构建表单

    this.registerForm = this.fb.group({

    /* 为 username 添加 5 项验证规则:

    * 1.必填, 2.最大长度为10, 3.最小长度为3, 4.不能以下划线开头, 5.只能包含数字、字母、下划线

    * 其中第一个空字符串参数为表单的默认值

    */

    'username': [ '', [

     Validators.required,

     Validators.maxLength(10),

     Validators.minLength(3),

     validateRex('notdown', /^(?!_)/),

     validateRex('only', /^[1-9a-zA-Z_]+$/)

    ]]

    });

    OK ! 大功告成了,赶紧运行代码尝试一下吧,我们可以随时添加各种验证规则,只需要修改 validationMessage 属性和 buildForm 方法即可!

    如果添加多个表单控件的话还需要修改 formErrors,例如添加 password 控件则修改 formErrors 为

    1

    2

    3

    4

    formErrors = {

     username: '',

     password: ''

    };

    大家可自行尝试一下!

    完整代码:

    register.component.html 

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    <h3 class="text-center">注册</h3>

     

     <form [formGroup]="registerForm" >

     

     <div class="form-group">

      <label for="username">用户名:</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>

    register.component.css:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    form {

    width: 90%;

    max-width: 45em;

    margin: auto;

    }

     

    .showerr {

    white-space: pre-wrap;

    }

    register.component.ts:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    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 {

     

    // 定义表单

    registerForm: FormGroup;

     

    // 表单验证不通过时显示的错误消息

    formErrors = {

     username: ''

    };

     

    // 为每一项表单验证添加说明文字

    validationMessage = {

     'username': {

     'minlength': '用户名长度最少为3个字符',

     'maxlength': '用户名长度最多为10个字符',

     'required': '请填写用户名',

     'notdown': '用户名不能以下划线开头',

     'only': '用户名只能包含数字、字母、下划线'

     }

    };

     

    // 添加 fb 属性,用来创建表单

    constructor(private fb: FormBuilder) { }

     

    ngOnInit() {

     // 初始化时构建表单

     this.buildForm();

    }

     

    // 构建表单方法

    buildForm(): void {

     // 通过 formBuilder构建表单

     this.registerForm = this.fb.group({

     /* 为 username 添加3项验证规则:

     * 1.必填, 2.最大长度为10, 3.最小长度为3, 4.不能以下划线开头, 5.只能包含数字、字母、下划线

     * 其中第一个空字符串参数为表单的默认值

     */

     'username': [ '', [

      Validators.required,

      Validators.maxLength(10),

      Validators.minLength(3),

      validateRex('notdown', /^(?!_)/),

      validateRex('only', /^[1-9a-zA-Z_]+$/)

     ]]

     });

     

     // 每次表单数据发生变化的时候更新错误信息

     this.registerForm.valueChanges

     .subscribe(data => this.onValueChanged(data));

     

     // 初始化错误信息

     this.onValueChanged();

    }

     

    // 每次数据发生改变时触发此方法

    onValueChanged(data?: any) {

     // 如果表单不存在则返回

     if (!this.registerForm) return;

     // 获取当前的表单

     const form = this.registerForm;

     

     // 遍历错误消息对象

     for (const field in this.formErrors) {

     // 清空当前的错误消息

     this.formErrors[field] = '';

     // 获取当前表单的控件

     const control = form.get(field);

     

     // 当前表单存在此空间控件 && 此控件没有被修改 && 此控件验证不通过

     if (control && control.dirty && !control.valid) {

      // 获取验证不通过的控件名,为了获取更详细的不通过信息

      const messages = this.validationMessage[field];

      // 遍历当前控件的错误对象,获取到验证不通过的属性

      for (const key in control.errors) {

      // 把所有验证不通过项的说明文字拼接成错误消息

      this.formErrors[field] += messages[key] + '\n';

      }

     }

     }

    }

     

    }

    validate-register.ts:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    import { ValidatorFn, AbstractControl } from '@angular/forms';

     

    export function validateRex(type: string, validateRex: RegExp): ValidatorFn {

    return (control: AbstractControl): {[key: string]: any} => {

     // 获取当前控件的内容

     const str = control.value;

     // 设置我们自定义的严重类型

     const res = {};

     res[type] = {str}

     // 如果验证通过则返回 null 否则返回一个对象(包含我们自定义的属性)

     return validateRex.test(str) ? null : res;

    }

    }

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

     

    展开全文
  • 表单内容校验 演示地址:http://zhtt.gitee.io/angular-demo/angular/dist/angular/#/form/template 示例代码:https://gitee.com/zhtt/angular-demo/repository/archive/master.zip 资源地址:...
  • 正常情况下我们的浏览器不支持两层的表单嵌套,所以,angular提供了ng-form来让我们达到这样的目的,主要用于部分表单提交,同时进行表单验证。 from> ng-from> ng-from> form> 这样就可以。 同时当我们使用...
  • Angular表单验证

    2018-12-13 01:39:29
    为什么使用验证器 用户输入的数据各式各样,并不总是正确的,如果用户输入了错误的数据格式,...表单验证 Angular支持的内置validate属性: required- 设置表单控件值是非空的 email - 设置表单控件的格式是email ...
  • angular表单验证及 提交功能实现

    千次阅读 2017-02-01 00:25:20
    本例通过Angular框架来实现简单的表单验证一、html结构 1、借助于bootstrap快速的编写了一个简单的表单 代码主要部分如下: ;" ng-controller="myCtrl"> 用户表单提交
  • Angular 支持非常强大的内置表单验证,maxlength、minlength、required 以及 pattern。使用 Angular 的内置表单校验能够完成绝大多数的业务场景的校验需求,但有时我们还需要实现更为复杂的表单校验功能,这时可以...
  • 慕课网angular表单验证课程源码
  • angular 表单验证

    2018-05-11 10:23:56
    最近在用angular表单验证时 , 不小心把ng-model全替换删掉了, 然后发现之前写的验证都失效, 在查阅资料和反复修改摸索后, 发现angular中的表单验证, 都是基于ng-model的. ng-model 指令为应用数据提供了以下状态值...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,220
精华内容 2,488
关键字:

angular表单验证