Angular2 之 Form 表单

From表单分为两种类型:

  • 模板驱动表单(Template-Driven Forms)
  • 模型驱动表单(Model-Driven-Forms)
    模型:有结构的状态(是一种数据结构)FormContrller 表单项

@angular/forms
模板驱动表单
@angular/reactiveForms

关注点是表单的行为,不是怎么生成DOM。关注点是出错了,是一种出错的状态,是否发生了改变。

Template-Driven Forms

  • 知识点1

Template 驱动表单时,因为模板驱动的表单有它自己的模块,所以我们得把FormsModule添加到应用的imports数组中去,这样我们才能使用表单。

注意:

如果一个组件、指令或者管道出现在模块的imports数组中,就说明它是外来模块不要在到declarations数组中声明它们。
如果你自己写的它,并且它属于当前模块,就要把它声明在declarations数组中。

  • 知识点2 - 表单
<div class="container">
  <h1>Hero Form</h1> {{diagnostic}} <form> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" required [(ngModel)]="model.name" name="name"> TODO:remove this: {{model.name}} </div> <div class="form-group"> <label for="alterEgo">Alter Ego</label> <input type="text" class="form-control" id="laterEgo" [(ngModel)]="model.alterEgo" name="alterEgo"> </div> <div class="form-group"> <label for="power">Hero Power</label> <select class="form-control" id="power" required [(ngModel)]="model.power" name="power"> <option *ngFor="let p of powers" [value]="p">{{p}}</option> </select> </div> </form> </div>
  • 每个 input 元素都有id属性,它被label元素的for属性用它来匹配到对应的输入控件input。?
  • 每个 input 元素都有name属性,Angular 表单用它注册控件。(用于做表单验证)
  • diagnostic 的显示是因为在组件中,使用了get diagnostic() { return JSON.stringify(this.model); }这段代码,这个在语法级别,是相当于diagnostic这个属性只读的。(只读也可以使用readonly age: number;)
  • 知识点3 - [(ngModel)]内幕

    • [] ,这是一个从模型到视图的单项数据绑定。
    • () ,这是一个从视图到模型的反向数据绑定。
    • [()] ,这是双向数据绑定和双向数据流。
    • 我们可以把ngModel绑定拆成两个独立的绑定,就想我们重写的“Name”的<input>绑定一样:
    <input type="text" class="form-control" id="name" required [ngModel]="model.name" name="name" (ngModelChange)="model.name = $event" >
    TODO: remove this: {{model.name}}

    这个属性绑定看起来很眼熟,但事件绑定看起来有点怪。
    ngModelChange并不是<input> 元素的事件。 它实际上是来自NgModel指令的事件属性。 当 Angular 在表单中看到[(x)]的绑定目标时, 它会期待这个x指令有一个名为x的输入属性,和一个名为xChange输出属性

    模板表达式中的另一个古怪之处是model.name = $event
    。 之前看到的$event对象来自 DOM 事件。 但ngModelChange
    属性不会生成 DOM 事件 —— 它是Angular EventEmitter类型的属性,当它触发时, 它返回的是输入框的值 —— 也正是希望赋给模型name
    属性的值。

    很高兴知道这些,但是这样现实吗?实践上中,几乎总是优先使用[(ngModel)]形式的双向绑定。 只有当需要在事件处理函数中做一些特别的事情(例如合并或限制按键频率)时,才会拆分出独立的事件处理函数

  • 知识点4 - 表单验证

    在表单中使用ngModel,能让我们比仅仅使用双向数据绑定获得更多的控制权。eg:

    • 用户是否碰过此控件?
    • 它值的变化?
    • 数据是否有效?

    ngModel指令不仅仅跟踪状态,它还使用三个CSS类来更新控件,以反映当前的状态。

状态 为真时的CSS类 为假时的CSS类
控件已经被访问过 ng-touched ng-untouched
控件值已经变化 ng-dirty ng-pristine
控件值是有效的 ng-valid ng-invalid

我们可以在name的input标签添加一个名为spy的临时模板引用变量,然后利用这个spy显示它上面的所有css类。

<input type="text" class="form-control" id="name" required [(ngModel)]="model.name" name="name" #spy >
<br>TODO: remove this: {{spy.className}} // 这样显示className
  • 知识点5 - 表单验证
<div class="form-group">
      <label for="name">Name</label>
      <input type="text" class="form-control" id="name" required [(ngModel)]="model.name" name="name" #spy="ngModel">
      TODO:remove this: {{model.name}}| {{spy.className}}
    </div>
    <div [hidden]="spy.valid || spy.pristine" class="alter alter-danger">
      Name IS required!
    </div>

#spy=”ngModel”可以得到这个input`的一些状态。

  • 知识点6 - 表单重置

    heroForm.reset()可以将表单重置。

  • 知识点7 - 表单提交

    在填表完成之后,用户还应该能提交这个表单。 “Submit(提交)”按钮位于表单的底部,它自己不做任何事,但因为有特殊的 type 值 (type=”submit”),所以会触发表单提交。

    表单提交,我们使用NgSubmit,并且通过事件绑定把它绑定到HeroFormComponent.submit()方法

<button type="submit" class="btn btn-default" [disabled]="!heroForm.form.valid">Submit</button>

NgForm指令
什么NgForm指令?之前没有添加NgForm 指令啊!

是 Angular 干的。Angular 自动创建了NgForm指令,并把它附加到标签。NgForm指令为form元素扩充了额外的特性。 它持有通过ngModel指令和name属性为各个元素创建的那些控件,并且监视它们的属性变化,包括有效性。 它还有自己的valid属性,只有当其中所有控件都有效时,它才有效。

Model-Driven-Forms

有时候手动编写和维护表单所需工作量和时间会过大。特别是在需要编写大量表单时。表单都很相似,而且随着业务和监管需求的迅速变化,表单也要随之变化,这样维护的成本过高。

整个例子,我的理解就是:

所谓动态表单,也就是,将表单的配置信息写成配置方便更改,而不是以模板的形式写死,这样,我们就可以在修改应用代码的情况下,动态的创建表单项。

程序启动

我们将使用响应式表单(Reactive Forms)。

响应式表单属于另外一个叫做ReactiveFormsModule的NgModule,所以,为了使用响应式表单类的指令,我们得往@angular/forms库中引入ReactiveFormsModule模块。并且需要引入imports: [ ReactiveFormsModule ],这一点很重要,不能忘记引入

自定义表单

自定义表单的例子
自定义表单验证的资料

模板驱动表单的实现例子

pdf文档的名字:Custom Form Controls in Angular 2 by thoughtram.pdf

下面是一个使用表单的例子。

import { Component,forwardRef } from '@angular/core';
import { ControlValueAccessor,NG_VALUE_ACCESSOR } from '@angular/forms';

// 所谓表单指的就是,数据从model-->view,然后再从view-->model的这样的一些项,类似的有`input`,`select`,`textarea`等这样的。
@Component({
  template: `
        <input type="text" [(ngModel)]="value">
    `,providers: [
    {
      provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => CustomerFormItemComponent),multi: true,},],})
class CustomerFormItemComponent implements ControlValueAccessor {
  private propagateChange: (_: any) => {};
  private _value: String; // 为了避免出现死循环,所以设置一个_value

  // 如果只设置get方法的话,可以设置只读
  get value() {
    return this._value;
  }
  // 这种写法是语法级别的,set方法可以很好的监听传入值的改变。
  set value(value: String) {
    if (this._value === value) {
      return;
    } else {
      this._value = value;
      this.propagateChange(value);
    }
  }
  // 表单初始化的时候,完成model --> view 的数据设置。
  writeValue(value: String): void {
    this._value = value;
  }

  registerOnChange(fn: any): void {
    this.propagateChange = fn;
  }

  registerOnTouched(fn: any): void {
  }
}

未完待续…

相关文章

AngularJS 是一个JavaScript 框架。它可通过 注:建议把脚本放在 元素的底部。这会提高网页加载速度,因...
angluarjs中页面初始化的时候会出现语法{{}}在页面中问题,也即是页面闪烁问题。出现这个的原因是:由于...
AngularJS 通过被称为指令的新属性来扩展 HTML。AngularJS 指令AngularJS 指令是扩展的 HTML 属性,带有...
AngularJS 使用表达式把数据绑定到 HTML。AngularJS 表达式AngularJS 表达式写在双大括号内:{{ expres...
ng-repeat 指令可以完美的显示表格。在表格中显示数据 {{ x.Name }} {{ x.Country }} 使用 CSS 样式为了...
$http是 AngularJS 中的一个核心服务,用于读取远程服务器的数据。读取 JSON 文件下是存储在web服务器上...