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-touchedng-untouched
控件值已经变化ng-dirtyng-pristine
控件值是有效的ng-validng-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 {
  }
}

未完待续…


写在后面

GitHub上集大家之力搞了一个前端面试题的项目,里面都是大家面试时所遇到的题以及一些学习资料,有兴趣的话可以关注一下。如果你也有兴趣加入我们的话,请在项目中留言。项目同时也可以在gitbook上查看。

InterviewLibrary-GitHub
InterviewLibrary-gitbook

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值