文档章节

Angular4表单(模板驱动表单、响应式表单、自定义表单验证)

SubinY
 SubinY
发布于 2017/07/25 20:25
字数 1280
阅读 276
收藏 1

看了两天官网文档表单和表单验证后,才发现之前自己仅仅通过特定的 Angular CSS 类去控制去反馈用户的操作,如ng-touched等等是多么的入门,虽然说这已经够用了,但是之前写的都是在视图层上写逻辑,各种判断,导致后期维护有点困难。趁有时间记录一下这两天看了资料的一些心得。

模板驱动表单VS响应式表单

两者都可以通过表单绑定获取整个表单的值和是否合法eg: myForm.value, myForm.valid等等

Template-Driven Forms (模板驱动表单) 的特点

  • 使用方便

  • 适用于简单的场景

  • 通过 [(ngModel)] 实现数据双向绑定

  • 最小化组件类的代码

  • 不易于单元测试

  • 导入FormModule

  • 表单绑定通过  #myForm="ngForm"

Reactive Forms (响应式表单) 的特点

  • 比较灵活

  • 适用于复杂的场景

  • 简化了HTML模板的代码,把验证逻辑抽离到组件类中

  • 方便的跟踪表单控件值的变化

  • 易于单元测试

  • 导入ReactiveFormsModule

  • 表单绑定通过 [formGroup]="myForm"

组件类代码解释

Template-Driven Forms (模板驱动表单)

import { Component, AfterViewChecked, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Hero }      from '../shared/hero';
@Component({
  selector: 'hero-form-template2',
  templateUrl: './hero-form-template2.component.html'
})
export class HeroFormTemplate2Component implements AfterViewChecked {

  powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];

  hero = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What');

  submitted = false;

  onSubmit() {
    this.submitted = true;
  }

  // Reset the form with a new hero AND restore 'pristine' class state
  // by toggling 'active' flag which causes the form
  // to be removed/re-added in a tick via NgIf
  // TODO: Workaround until NgForm has a reset method (#6822)
  active = true;

  addHero() {
    this.hero = new Hero(42, '', '');

    this.active = false;
    setTimeout(() => this.active = true, 0);
  }
  /*组件手动记录表单对象*/
  heroForm: NgForm;

  /*heroFrom变量是Angular从模板衍生出来的控制模型的引用。 我们利用@ViewChild来告诉Angular注入这个模型到组件类的currentForm*/
  @ViewChild('heroForm') currentForm: NgForm;

  /*监听视图中表格变化,第一次渲染表格对象有多少个值就跑多少次*/
  ngAfterViewChecked() {
    this.formChanged();
  }

  formChanged() {
    /*检测是否发生变化*/
    if (this.currentForm === this.heroForm) { return; }
    this.heroForm = this.currentForm;
    if (this.heroForm) {
      /*heroForm得到的是FormModule对象,该对象继承于AbstractControl抽象类,里面包含touched等*/
      /*valueChanges返回一个Observal,用于获取视图返回的可观察对象*/
      this.heroForm.valueChanges
        .subscribe(data => this.onValueChanged(data));
    }
  }

  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] = '';
      /*heroForm.form.get('name')获取对应表单里的键值*/
      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 Forms (响应式表单) 的特点

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

import { Hero }                   from '../shared/hero';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';

@Component({
  ...
})
export class HeroFormReactiveComponent implements OnInit {

  ...
  /**/
  heroForm: FormGroup;
  constructor(private fb: FormBuilder) { }

  ngOnInit(): void {
    /*初始化表单对象*/
    this.buildForm();
  }

  buildForm(): void {
    /*FormBuilder.group是一个用来创建FormGroup的工厂方法*/
    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
  }

  /*与驱动表单上面代码类似*/
  ...
}

组件视图层对比

Template-Driven Forms (模板驱动表单)

表组通过ngModelGroup去绑定

<form #templateForm="ngForm" novalidate (ngSubmit)="onSubmit(templateForm)">
    <span>Name</span>
    <label class="user_name">
      <input #uName="ngModel" type="text" name="user_name" [(ngModel)]="user.name" required>
    </label>
    <div ngModelGroup="account">
      <span>QQ</span>
      <label class="user_name">
        <input type="text" name="user_qq" [(ngModel)]="user.account.qq" required>
      </label>
      <span>Password</span>
      <label class="user_name">
        <input type="password" name="user_password" [(ngModel)]="user.account.password" required>
      </label>
    </div>
    <button style="width: 200px; height: 36px; border: 1px solid #ccc; margin-top: 20px;" [disabled]="templateForm.invalid">提交</button>
</form>

Reactive Forms (响应式表单) 的特点

表组通过

formGroupName="address"

-------------------------------------------------------------------

自定义表单验证

html: 

这里注意要通过表单.get的方法获取FormModel的东西,reactiveForm.get('age').touched 或者reactive.controls.age?.touched

<form [formGroup]="reactiveForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label for="name">Name</label>
    <input type="text" id="name" class="form-control" formControlName="name">
  </div>
  <div class="form-group" [ngClass]="{'has-error': (reactiveForm.get('age').touched || reactiveForm.get('age').dirty) && !reactiveForm.get('age').valid }">
    <label class="col-md-2 control-label" for="ageId">年龄</label>

    <div class="col-md-8">
      <input class="form-control" id="ageId" type="number" placeholder="请输入年龄" formControlName="age" />
      <span class="help-block" *ngIf="(reactiveForm.get('age').touched || reactiveForm.get('age').dirty) && reactiveForm.get('age').errors">
        <span *ngIf="reactiveForm.get('age').errors.range">
            输入年龄不合法
        </span>
      </span>
    </div>
  </div>
  <p>{{reactiveForm.value | json}}</p>
</form>

ts:

...

@Component({
  ...
})
export class ReactiveFormComponent implements OnInit {

  user: any = {
    name: '',
    age: 0
  };

  reactiveForm: FormGroup;

  constructor(public fb: FormBuilder) { }

  ngOnInit() {
    this.buildForm();
  }

  buildForm(): void {
    this.reactiveForm = this.fb.group({
      'name': [this.user.name, Validators.required],
      'age': [this.user.age, this.ageValidator]    //自定义验证方法使用
    });

    // this.reactiveForm.valueChanges
    //   .subscribe(data => { console.log(data) });
  }

  /*有参数的验证器工厂函数写法,这里FormControl和下面的AbstractControl都是指向抽象类的属性和方法都是为了获取表单属性*/
  private ageValidatorParams(min: number, max: number) {
    return (c: FormControl): { [key: string]: any } | null => {
      let age = c.value;
      if (age && (isNaN(age) || age < min || age > max)) {
        return { 'range': true, min: min, max: max };
      }
      return null;
    }
  }
  /*必须返回一个对象,该对象为错误信息的值*/
  private ageValidator(c: AbstractControl): { [key: string]: any } | null {
    let age = c.value;
    if (age && (isNaN(age) || age < 20 || age > 120)) {
      return { 'range': true, min: 20, max: 120 };
    }
    return null;
  }

}

 

 

 

 

参考文献:

1、Angular官方文档(表单验证)

2、模板驱动式表单

3、响应式表单

4、案例情况比较多的表单验证

© 著作权归作者所有

SubinY
粉丝 9
博文 87
码字总数 66343
作品 0
佛山
程序员
私信 提问
细说 Angular 2+ 的表单(二):响应式表单

细说 Angular 2+ 的表单(一):模板驱动型表单 响应式表单 响应式表单乍一看还是很像模板驱动型表单的,但响应式表单需要引入一个不同的模块: 而不是 与模板驱动型表单的区别 接下来我们还...

接灰的电子产品
2017/06/13
0
0
angular6 中怎样创建响应式表单?

在angular中,提供了两种创建表单的方式: * 模板驱动型表单(Template Driven Forms) * 响应式表单(Reactive Forms) 在模板驱动型表单中,我们直接通过 ngModel 指令在组件模板中创建 co...

IrisHuang
2018/07/13
0
0
【Angular】——表单处理

纯HTML表单:显示表单项,校验用户输入,提交表单数据。就是普通写的HTML页面。 模板式表单:表单的数据模型是通过组件模板中的相关指令来定义的。因为使用这种方式定义表单的数据模型时,我...

fjj15732621696
2018/03/18
0
0
细说 Angular 2+ 的表单(一):模板驱动型表单

细说 Angular 2+ 的表单(二):响应式表单 摘要 在企业应用开发时,表单是一个躲不过去的事情,和面向消费者的应用不同,企业领域的开发中,表单的使用量是惊人的。这些表单的处理其实是一个...

接灰的电子产品
2017/04/24
0
0
如何优雅的使用 Angular 表单验证

随便说说,这一节可以跳过 去年参加 ngChine 2018 杭州开发者大会的时候记得有人问我: Worktile 是什么时候开始使用 Angular 的,我说是今年(2018年) 3 月份开始在新模块中使用最新的 An...

why520crazy
01/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

带出7个“师弟”,支付宝BASIC College的辅导员是个伪90后

文/电商在线 朱婷 “我的花名是改之,不是‘有则改之无则加勉’的改之,而是‘杨过,字改之’的那个改之。”一见面,他对自己花名的介绍,就让人耳目一新。至于为什么要用杨过的字给自己起名...

阿里云云栖社区
26分钟前
5
0
一次性搞清楚equals和hashCode

前言 在程序设计中,有很多的“公约”,遵守约定去实现你的代码,会让你避开很多坑,这些公约是前人总结出来的设计规范。 Object类是Java中的万类之祖,其中,equals和hashCode是2个非常重要...

编程SHA
27分钟前
1
0
kibana 性能监控 apm

启动本地节点:nohup ./apm-server -c apm-server.yml -e weblogic 12 性能监控 修改 wls12213/user_projects/domains/base_domain/startWebLogic.sh 文件,添加如下内容 export JAVA_OPTION......

internetafei
29分钟前
2
0
给指定的某个commit号加tag并推送

给指定的某个commit号加tag 打tag不必要在head之上,也可在之前的版本上打,这需要你知道某个提交对象的校验和(通过git log获取,取校验和的前几位数字即可)。 git tag -a v1.2 9fceb02 -m...

dragon_tech
29分钟前
1
0
等保2.0来了 | 网站被黑,还要被处罚,为什么背锅的总是我?

“净网2019”专项行动开展以来,四川公安网安部门查处了一批不履行网络安全管理义务的网络运营者,其中包括不履行网络安全保护义务的高校、政府机关、企业和单位均受到了处罚! ▶ 宜宾学院因...

亚洲诚信
31分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部