在 Angular2 中实现自定义校验指令(确认密码)的方法


Posted in Javascript onJanuary 23, 2017

我们会在本文中探索 Angular 2 内建的自定义验证。

# 介绍

Angular 2 原生就支持一些有用的验证器:

  1. required: 验证字段必须存在
  2. minlength: 验证字段值的最小长度有效
  3. maxlength: 验证字段值的最大长度有效
  4. pattern: 验证输入的值是否匹配给定的模板,比如 email

我们会基于下面的接口创建一个表单来获取用户信息。

// user.interface.ts
export interface User {
 username: string; // required, must be 5-8 characters
 email: string; // required, must be valid email format
 password: string; // required, value must be equal to confirm password.
 confirmPassword: string; // required, value must be equal to password.
}

需求

仅在字段数据不正确或提交表单的时候,为每个字段 显示错误消息 。

UI 展示:

在 Angular2 中实现自定义校验指令(确认密码)的方法 

# App 配置

这是我们的文件结构:

|- app/
 |- app.component.html
 |- app.component.ts
 |- app.module.ts
 |- equal-validator.directive.ts
 |- main.ts
 |- user.interface.ts
|- index.html
|- styles.css
|- tsconfig.json

为了使用新的表单模块,我们需要用 npm install @ angular/forms 指令调用 npm 包,并在应用程序模块中导入最新的表单模块。

$ npm install @angular/forms --save

下面是我们应用程序的 app.module.ts 模块:

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

# App 组件

让我们继续创建 App 组件。

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { User } from './user.interface';
@Component({
 moduleId: module.id,
 selector: 'app-root',
 templateUrl: 'app.component.html',
 styleUrls: ['app.component.css']
})
export class AppComponent implements OnInit {
 public user: User;
 ngOnInit() {
  // initialize model here
  this.user = {
   username: '',
   email: '',
   password: '',
   confirmPassword: ''
  }
 }
 save(model: User, isValid: boolean) {
  // 调用API保存customer
  console.log(model, isValid);
 }
}

# HTML 视图

这是我们的 HTML 视图的样子。

<!-- app.component.html -->
<div>
 <h1>Add user</h1>
 <form #f="ngForm" novalidate (ngSubmit)="save(f.value, f.valid)">
  <!-- 我们将把验证的字段放在这里 -->
  <button type="submit" [disabled]="!myForm.valid">Submit</button>
 </form>
</div>

现在来一个个添加控件。

用户名

需求: 必填,长度在 5-8 个字符之间

<!-- app.component.html -->
...
<div>
 <label>Username</label>
 <input type="text" name="username" [ngModel]="user.username" 
  required minlength="5" maxlength="8" #username="ngModel">
 <small [hidden]="username.valid || (username.pristine && !f.submitted)">
  Username is required (minimum 5 characters).
 </small>
</div>
<pre *ngIf="username.errors">{{ username.errors | json }}</pre>
...

required、minlength、maxlength 都是内置的验证器,所以很容易使用。

我们只会在用户名无效、获得焦点和提交表单的情况下显示错误消息。最后一条的 pre 标签在开发过程中对调试很有用。它会显示字段的所有验证错误。

电子邮件地址

需求: 必填,必须是有效的电子邮件地址格式

<!-- app.component.html -->
...
<div>
 <label>Email</label>
 <input type="email" name="email" [ngModel]="user.email" 
  required pattern="^[a-zA-Z0?9_.+-]+@[a-zA-Z0?9-]+.[a-zA-Z0?9-.]+$" #email="ngModel" >
 <small [hidden]="email.valid || (email.pristine && !f.submitted)">
  Email is required and format should be <i>john@doe.com</i>.
 </small>
</div>
...

我们把 email 设置为必填,然后使用内建的模板验证器,通过正则表达式来检查 email 的值:^[a-zA-Z0?9_.+-]+@[a-zA-Z0?9-]+.[a-zA-Z0?9-.]+$.

密码和确认密码

需求:

  1. 密码: 必填,值必须与确认密码的值相同。
  2. 确认密码: 必填,值必须与密码的值相同。
<!-- app.component.html -->
...
<div>
 <label>Password</label>
 <input type="password" name="password" [ngModel]="user.password" 
  required #password="ngModel">
 <small [hidden]="password.valid || (password.pristine && !f.submitted)">
  Password is required
 </small>
</div>
<div>
 <label>Retype password</label>
 <input type="password" name="confirmPassword" [ngModel]="user.confirmPassword" 
  required validateEqual="password" #confirmPassword="ngModel">
 <small [hidden]="confirmPassword.valid || (confirmPassword.pristine && !f.submitted)">
  Password mismatch
 </small>
</div>
...

validateEqual 是我们自定义的验证器。它会将当前输入的值与输入的密码值进行对比验证。

# 自定义确认密码验证器

我们将制定一个 validate equal 指令。

// equal-validator.directive.ts
import { Directive, forwardRef, Attribute } from '@angular/core';
import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
@Directive({
 selector: '[validateEqual][formControlName],[validateEqual][formControl],[validateEqual][ngModel]',
 providers: [
  { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator), multi: true }
 ]
})
export class EqualValidator implements Validator {
 constructor( @Attribute('validateEqual') public validateEqual: string) {}
 validate(c: AbstractControl): { [key: string]: any } {
  // self value (e.g. retype password)
  let v = c.value;
  // control value (e.g. password)
  let e = c.root.get(this.validateEqual);
  // value not equal
  if (e && v !== e.value) return {
   validateEqual: false
  }
  return null;
 }
}

代码很长,让我们把它拆开一部分一部分地看。

申明指令

// equal-validator.directive.ts
@Directive({
 selector: '[validateEqual][formControlName],[validateEqual] 
 [formControl],[validateEqual][ngModel]',
 providers: [
  { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator), multi: true }
 ]
})

首先,我们使用 @Directive 注解定义指令。然后我们指定 selector。selector 是必须的。我们会扩展内建验证器集合 NG_VALIDATORS 来将我们的等值验证器用于 providers.

// equal-validator.directive.ts
export class EqualValidator implements Validator {
 constructor( @Attribute('validateEqual') public validateEqual: string) {}
 validate(c: AbstractControl): { [key: string]: any } {}
}

我们的指令类必须实现 Validator 接口。Validator 接口需要 avalidate 函数。在构建函数中,我们通过 @Attribute('validateEqual') 注解注入属性值,并将其赋值给 validateEqual 变量。在我们的示例中, validateEqual 的值是 "password" 。

实现验证

// equal-validator.directive.ts
validate(c: AbstractControl): { [key: string]: any } {
 // 自己的值 (如 retype password)
 let v = c.value;
 // 控件的值 (如 password)
 let e = c.root.get(this.validateEqual);
 // 值不等旱
 if (e && v !== e.value) return {
  validateEqual: false
 }
 return null;
}

首先我们从输入控件读入值,赋给 v。然后我们在表单中找到 password 控件的值赋值给 e。之后检查值是否相等,如果不等就返回错。

# 在应用模块中导入自定义验证器

要使用自定义验证器,需要先将其导入到应用程序模块中。

// app.module.ts
...
import { EqualValidator } from './equal-validator.directive'; // 导入验证器
import { AppComponent } from './app.component';
@NgModule({
 imports:  [ BrowserModule, FormsModule ],
 declarations: [ AppComponent, EqualValidator ], // 导入到应用模块
 bootstrap: [ AppComponent ],
})
...

好了!假如你在 password 字段中输入 "123",在 retype password 字段中输入"xyz",就会显示一个密码不匹配的错误。

# 看起来挺好,但是……

现在一切都挺好,直到你 在 retype passowrd 中输入文本之后 又修改了 password 字段的值。

比如,你在 password 字段中输入 "123",在 retype password 字段中也输入 "123", 然后将 password 字段的值改为 "1234" 。验证仍然通过。 为什么?

因为我们只把等值验证器应用到 retype password 字段。只有当 retype password 的值发生变化时才会触发验证。

解决办法

有几种方法可以解决这个问题。我们这里只讨论其中一种,你自己可以去找到其它办法。我们会再次使用 validateEqual 验证器并 添加 一个 reverse 属性。

<!-- app.component.html -->
...
<input type="password" class="form-control" name="password" 
 [ngModel]="user.password" 
 required validateEqual="confirmPassword" reverse="true">
<input type="password" class="form-control" name="confirmPassword" 
 [ngModel]="user.confirmPassword" 
 required validateEqual="password">
...
  • reverse 是 false 或者没有设置的情况下,我们会像前一节提到的那样执行等值验证器。
  • reverse 是 true 的时候,我们仍然会执行等值验证器,但它不会为当前控件添加错误消息,而是 为指定 会把的目标控件添加错误消息 。

在我们的例子中,我们设置 password 验证的 reverse 为 true。只要 password 与 retype password 的 值不等,我们会为确证密码字段添加一个错误消息,而不是重置 password 字段。

完整的自定义验证器代码如下:

// equal-validator.directive.ts
import { Directive, forwardRef, Attribute } from '@angular/core';
import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
@Directive({
 selector: '[validateEqual][formControlName],[validateEqual][formControl],[validateEqual][ngModel]',
 providers: [
  { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator), multi: true }
 ]
})
export class EqualValidator implements Validator {
 constructor(@Attribute('validateEqual') public validateEqual: string,
 @Attribute('reverse') public reverse: string) {
 }
 private get isReverse() {
  if (!this.reverse) return false;
  return this.reverse === 'true' ? true: false;
 }
 validate(c: AbstractControl): { [key: string]: any } {
  // self value
  let v = c.value;
  // control vlaue
  let e = c.root.get(this.validateEqual);
  // value not equal
  if (e && v !== e.value && !this.isReverse) {
   return {
    validateEqual: false
   }
  }
  // value equal and reverse
  if (e && v === e.value && this.isReverse) {
   delete e.errors['validateEqual'];
   if (!Object.keys(e.errors).length) e.setErrors(null);
  }
  // value not equal and reverse
  if (e && v !== e.value && this.isReverse) {
   e.setErrors({ validateEqual: false });
  }
  return null;
 }
}

当然,还有其他方法也能解决密码和确认密码验证问题。有些人建议在组( stack overflow )中添加密码和确认密码的机制,然后验证它。

方法没有绝对的好与坏,适合自己的才是最好的。

以上所述是小编给大家介绍的在 Angular2 中实现自定义校验指令(确认密码)的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Firefox 无法获取cssRules 的解决办法
Oct 11 Javascript
斜45度寻路实现函数
Aug 20 Javascript
ext 同步和异步示例代码
Sep 18 Javascript
Mootools 图片展示插件(lightbox,ImageMenu)收集集合
May 21 Javascript
jquery向上向下取整适合分页查询
Sep 06 Javascript
jquery实现公告翻滚效果
Feb 27 Javascript
基于JavaScript实现焦点图轮播效果
Mar 27 Javascript
判断滚动条滑到底部触发事件(实例讲解)
Nov 15 Javascript
深入理解 JS 垃圾回收
Jun 03 Javascript
简单了解vue中的v-if和v-show的区别
Oct 08 Javascript
React Native中ScrollView组件轮播图与ListView渲染列表组件用法实例分析
Jan 06 Javascript
JavaScript中的this妙用实例分析
May 09 Javascript
jQuery.cookie.js实现记录最近浏览过的商品功能示例
Jan 23 #Javascript
利用JS实现简单的日期选择插件
Jan 23 #Javascript
获取IE浏览器Cookie信息的方法
Jan 23 #Javascript
jQuery实现的简单悬浮层功能完整实例
Jan 23 #Javascript
原生和jQuery的ajax用法详解
Jan 23 #Javascript
基于MVC方式实现三级联动(JavaScript)
Jan 23 #Javascript
利用javascript实现的三种图片放大镜效果实例(附源码)
Jan 23 #Javascript
You might like
利用js调用后台php进行数据处理原码
2006/10/09 PHP
一个PHP针对数字的加密解密类
2014/03/20 PHP
php中print(),print_r(),echo()的区别详解
2014/12/01 PHP
Symfony模板的快捷变量用法实例
2016/03/17 PHP
php 截取utf-8格式的字符串实例代码
2016/10/30 PHP
Smarty模板常见的简单应用分析
2016/11/15 PHP
ThinkPHP使用getlist方法实现数据搜索功能示例
2017/05/08 PHP
使用 PHP Masked Package 屏蔽敏感数据的实现方法
2019/10/15 PHP
JSON 编辑器实现代码
2009/12/06 Javascript
Javascript图像处理—虚拟边缘介绍及使用方法
2012/12/27 Javascript
将字符串转换成gb2312或者utf-8编码的参数(js版)
2013/04/10 Javascript
防止登录页面出现在frame中js代码
2014/07/22 Javascript
jquery实现input框获取焦点的简单实例
2017/01/26 Javascript
Vue.js 插件开发详解
2017/03/29 Javascript
详解webpack import()动态加载模块踩坑
2018/07/17 Javascript
玩转vue的slot内容分发
2018/09/22 Javascript
jQuery实现的鼠标拖动浮层功能示例【拖动div等任何标签】
2018/12/29 jQuery
vue页面跳转实现页面缓存操作
2020/07/22 Javascript
Vue 事件的$event参数=事件的值案例
2021/01/29 Vue.js
[43:43]完美世界DOTA2联赛PWL S2 LBZS vs Forest 第三场 11.29
2020/12/02 DOTA
使用python遍历指定城市的一周气温
2017/03/31 Python
Python编程实现数学运算求一元二次方程的实根算法示例
2017/04/02 Python
Django内容增加富文本功能的实例
2017/10/17 Python
PyQt5 QSerialPort子线程操作的实现
2018/04/21 Python
Python 获取numpy.array索引值的实例
2019/12/06 Python
Django 博客实现简单的全文搜索的示例代码
2020/02/17 Python
Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)
2021/01/28 Python
HTML5 Canvas实现图片缩放、翻转、颜色渐变的代码示例
2016/02/28 HTML / CSS
美国Randolph太阳镜官网:美国制造的飞行员太阳镜和射击眼镜
2018/06/15 全球购物
北卡罗来纳州豪华家具和家居装饰店:Carolina Rustica
2018/10/30 全球购物
网上开商店的创业计划书
2014/01/19 职场文书
四群教育工作实施方案
2014/03/26 职场文书
学习方法演讲稿
2014/05/10 职场文书
交通事故和解协议书
2015/01/27 职场文书
法人身份证明书
2015/06/18 职场文书
如何利用STAR法则制作留学文书?
2019/08/26 职场文书