Angular2 表单校验

曾自己借助阿里云和hexo搭了个站点,现已废弃,过往写的博客暂挪到此处。


title: Angular2 表单校验
subtitle: 入门篇
date: 2016-10-05 00:02:04
tags:
- 技术
- Angular2


还是推荐直接阅读官方文档

简介

分两种方式:

  • 模板驱动 Template Driven

同angular1的方式
使用模板驱动需要导入FormsModule

  • Reactive Form

angular2更推崇的方式
使用Reactive Form需要导入ReactiveFormsModule;

实现

简单模板驱动

<label for="name">Name</label>

<input type="text" id="name" class="form-control"
       required minlength="4" maxlength="24"
       name="name" [(ngModel)]="hero.name"
       #name="ngModel" >

<div *ngIf="name.errors && (name.dirty || name.touched)"
     class="alert alert-danger">
    <div [hidden]="!name.errors.required">
      Name is required
    </div>
    <div [hidden]="!name.errors.minlength">
      Name must be at least 4 characters long.
    </div>
    <div [hidden]="!name.errors.maxlength">
      Name cannot be more than 24 characters long.
    </div>
</div>

<ol>
<li>为表单添加angular指令,angular将创建对应的内部control模型。可以说模板上内含control模型;</li>
<li>在表单元素上添加html校验属性;</li>
<li>angular暴露control的状态,于是你可以在模板中安排对应control状态的报错信息</li>
<li>需给表单元素设置name属性,1)angular才能跟踪这个表单元素 2)angular为该元素创建对应的内部从control名为该name属性的值 3)angular才能将该元素同2)中的control关联起来</li>
<li>ngModel双向绑定</li>
<li>给元素设置模板变量#name(该变量名是不是得等于如4.2中的control名?待验证)的值为ngModel,将该元素control与ngModel关联起来,所以我们可以在模板中检查control的状态,实现3的功能</li>
</ol>

如果没有或者不需要为该元素设置ngModel,那么在元素上写上一个空的ngModel即可,不需要设置属性值

复杂模板驱动(校验信息在组件中定义)

html

<form #heroForm="ngForm"  *ngIf="active"  (ngSubmit)="onSubmit()">
        <label for="name">Name</label>
        <input type="text" id="name" class="form-control"
               required minlength="4" maxlength="24" forbiddenName="bob"
               name="name" [(ngModel)]="hero.name" >
        <div *ngIf="formErrors.name" class="alert alert-danger">
          {{ formErrors.name }}
        </div>
</form>

component.ts

heroForm: NgForm;
@ViewChild('heroForm') currentForm: NgForm;

ngAfterViewChecked() {
  this.formChanged();
}

formChanged() {
  if (this.currentForm === this.heroForm) { return; }
  this.heroForm = this.currentForm;
  if (this.heroForm) {
    this.heroForm.valueChanges
      .subscribe(data => this.onValueChanged(data));
  }
}

<ol>
<li>在form元素上通过模板变量#heroForm是一个angular模板上的control模型的引用</li>
<li>通过@ViewChild将该模型注入到组件类中的currentForm属性</li>
<li>调用ngAfterViewChecked确认是否有heroForm对象以及做相应的操作</li>
<li>有heroForm对象则订阅它的valueChanged(observable属性)</li>
<li>onValueChanged处理校验错误信息,传入的data包含当前元素的值</li>
</ol>

onValueChanged(data?: any) {
  if (!this.heroForm) { return; }
  const form = this.heroForm.form;

  for (const field in this.formErrors) {
    // clear previous error message (if any)
    this.formErrors[field] = '';
    const control = form.get(field);

    if (control && control.dirty && !control.valid) {
      const messages = this.validationMessages[field];
      for (const key in control.errors) {
        this.formErrors[field] += messages[key] + ' ';
      }
    }
  }
}

formErrors = {
  'name': '',
  'power': ''
};
validationMessages = {
  'name': {
    'required':      'Name is required.',
    'minlength':     'Name must be at least 4 characters long.',
    'maxlength':     'Name cannot be more than 24 characters long.',
    'forbiddenName': 'Someone named "Bob" cannot be a hero.'
  },
  'power': {
    'required': 'Power is required.'
  }
};

Reactive表单

<ol>
<li>在constructor中注入FormBuilder</li>
<li>ngOnInit中使用FormBuilder定义表单control模型和在该control模型的valueChanges事件时处理报错信息</li>
<li>form标签中关联组件中定义的control模型,通过指令formGroup</li>
<li>关联form中的各元素和对应的control,通过指令formControlName</li>
<li>不需要双向绑定的ngModel</li>
</ol>

  heroForm: FormGroup;
  constructor(private fb: FormBuilder) { }
  ngOnInit(): void {
    this.buildForm();
  }
  buildForm(): void {
    this.heroForm = this.fb.group({
      'name': [this.hero.name, [
          Validators.required,
          Validators.minLength(4),
          Validators.maxLength(24),
          forbiddenNameValidator(/bob/i)
        ]
      ],
      'alterEgo': [this.hero.alterEgo],
      'power':    [this.hero.power, Validators.required]
    });
    this.heroForm.valueChanges
      .subscribe(data => this.onValueChanged(data));
    this.onValueChanged(); // (re)set validation messages now
  }

Reactive表单中使用的新对象和方法 简单使用方式:

  • FormBuilder声明对象指定例子表单中的三个control规格
  • 每个control规格的属性名为该元素control名,属性值为数组;
  • 该属性值数组的第一个值是该表单元素的当前值,第二个值一个校验函数或者一个校验函数的数组;
  • angular有与标准html表单校验属性对应的内置的校验,如Validators.required,Validators.minLength(4),Validators.maxLength(24)。
  • heroForm.value的值即该表单的值(heroForm为该表单form元素对应的control group);
  • heroForm.get('单个元素的control名')则得到该元素的control;
  • control.value为该元素的值

对比

  • 模板驱动的校验规则写在html模板里;Reactive表单的校验规则在组件中定义

  • 简单模板驱动的报错信息可以在html模板中定义;复杂模板驱动和Reactive表单的错误信息在组件中定义

错误信息在组件中定义则需要手动调用angular2生命周期及一些事件,来实现校验与错误信息的关联(这一点很不智能,记得bootstrap validation都可以自动关联,不知道angular会不会优化,还是有别的考虑);在模板中定义则不需要

  • 模板驱动不用创建control模型,只需进行关联angular在模板上创建的control模型和该元素,方式如下:

    • 元素的name属性关联angular的control
    • ngModel双向绑定
    • 给元素设置模板变量#name(该变量名是不是得等于如4.2中的control名?待验证)的值为ngModel,将control与ngModel关联起来,这样可以在模板中可以检查control的状态

    而Reactive表单需要在组件中创建control,并在模板上关联,关联方式如下:

    • form标签中关联组件中定义的control模型,通过指令formGroup
    • 关联form中的各元素和对应的control,通过指令formControlName

以上是实现方式的对比,这些实现方式带来的结果如下:
<ol>
<li>模板驱动的校验和错误信息不能动态设置;</li>
<li>简单模板驱动的校验规则如果比较多,对应的报错信息多起来,html文件会太臃肿;</li>
<li>模板驱动将业务逻辑留在html中而不是js中,不太符合html和js各自的本义;(呃……原谅我对官方术语不熟)</li>
<li>Reactive表单的表单检验规则和报错信息之间的关联更易读,易于维护;</li>
<li>Reactive表单和复杂的模板驱动表单需要手动将报错信息与元素的校验规则对应起来,比模板驱动费事;</li>
<li>Reactive表单没有双向绑定,从组件到模板这个方向的信息传递得由程序员根据自己需要在组件中处理。</li>
</ol>

如何选择?

依我看。。。。
<ol>
<li>非常简单(少量校验错误信息和校验规则)的表单校验就用模板驱动好了</li>
<li>其他都推荐Reactive表单校验方式</li>
<li>Reactive表单有一个没有双向绑定的问题,这个问题我觉得大部分情况不是问题,
1)在定义control的时候可以设置初始值
2)从模板到组件这个方向的数据传输没问题
以上两点基本可以符合大部分表单,因为大部分表单都只有初始化和提交。
3)从组件到模板这个方向的数据传输可以通过事件绑定等在组件中自定义</li>
<li>虽然不推崇两种表单混用,因为两者的功能有共同的部分,会混淆,但是在Reactive表单中使用ngModel试过还没有问题</li>
</ol>

参考链接:
https://angular.cn/docs/ts/latest/cookbook/form-validation.html
http://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven/

还有一个很重要的自定义校验,见下篇>>>>>>>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容