ng-book札记——表单

Angular表单的基本对象为FormControl与FormGroup。

FormControl

FormControl代表单个input表单字段(field),即Angular表单的最小单元。
FormControl封装了表单字段的值与状态(valid, dirty, errors)。

在TypeScript中创建FormControl:

// create a new FormControl with the value "Nate"
let nameControl = new FormControl("Nate");

let name = nameControl.value; // -> Nate

// now we can query this control for certain values:
nameControl.errors // -> StringMap<string, any> of errors
nameControl.dirty // -> false
nameControl.valid // -> true

在DOM中绑定:

<input type="text" [formControl]="nameControl" />

FormGroup

FormGroup是FormControl集合的包装器。

let personInfo = new FormGroup({
  firstName: new FormControl("Nate"),
  lastName: new FormControl("Murray"),
  zip: new FormControl("90210")
})

由于FormGroup与FormControl继承了相同的基类(AbstractControl),这意味着可以像FormControl一样检查其值与状态。

personInfo.value; // -> {
// firstName: "Nate",
// lastName: "Murray",
// zip: "90210"
// }

// now we can query this control group for certain values, which have sensible
// values depending on the children FormControl's values:
personInfo.errors // -> StringMap<string, any> of errors
personInfo.dirty // -> false
personInfo.valid // -> true

Angular中使用表单有FormsModule与ReactiveFormsModule两种方式。

FormsModule

导入FormsModule后, 将自动对相关视图中的任意<form>标签附加NgForm。
NgForm提供两样功能:

  1. 名为ngForm的FormGroup
  2. (ngSubmit)
import {
  FormsModule
} from '@angular/forms';

// farther down...

@NgModule({
  declarations: [
    FormsDemoApp,
    DemoFormSkuComponent,
    // ... our declarations here
  ],
  imports: [
    BrowserModule,
    FormsModule, // <-- add this
  ],
  bootstrap: [ FormsDemoApp ]
})
class FormsDemoAppModule {}
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-demo-form-sku',
  templateUrl: './demo-form-sku.component.html',
  styles: []
})
export class DemoFormSkuComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

  onSubmit(form: any): void {
    console.log('you submitted value:', form);
  }
}
<div class="ui raised segment">
  <h2 class="ui header">Demo Form: Sku</h2>
  <form #f="ngForm"
    (ngSubmit)="onSubmit(f.value)"
    class="ui form">

  <div class="field">
    <label for="skuInput">SKU</label>
    <input type="text"
      id="skuInput"
      placeholder="SKU"
      name="sku" ngModel>
  </div>

  <button type="submit" class="ui button">Submit</button>
  </form>
</div>

#f="ngForm"为视图创建一个本地变量f,并绑定了ngForm。这样在视图的其它地方就可以灵活使用,比如在(ngSubmit)="onSubmit(f.value)中。

在input元素里,单独的ngModel特性表示一个单向绑定,以及在表单中创建一个名为sku的FormControl,并且自动添加至父级的FormGroup(这里是form)。

ReactiveFormsModule

这种方式下可以通过FormBuilder帮助创建FormGroup与FormControl。

import {
  ReactiveFormsModule
} from '@angular/forms';

// farther down...

@NgModule({
  declarations: [
    FormsDemoApp,
    DemoFormSkuComponent,
    // ... our declarations here
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule // <-- and this
  ],
  bootstrap: [ FormsDemoApp ]
})
class FormsDemoAppModule {}
import { Component, OnInit } from '@angular/core';
import {
  FormBuilder,
  FormGroup
} from '@angular/forms';

@Component({
  selector: 'app-demo-form-sku-with-builder',
  templateUrl: './demo-form-sku-with-builder.component.html',
  styles: []
})
export class DemoFormSkuWithBuilderComponent implements OnInit {
  myForm: FormGroup;

  constructor(fb: FormBuilder) {
    this.myForm = fb.group({
    'sku': ['ABC123']
    });
  }

  ngOnInit() {
  }

  onSubmit(value: string): void {
    console.log('you submitted value: ', value);
  }
}
<div class="ui raised segment">
  <h2 class="ui header">Demo Form: Sku with Builder</h2>
  <form [formGroup]="myForm"
    (ngSubmit)="onSubmit(myForm.value)"
    class="ui form">

  <div class="field">
    <label for="skuInput">SKU</label>
    <input type="text"
      id="skuInput"
      placeholder="SKU"
      [formControl]="myForm.controls['sku']">
  </div>

  <button type="submit" class="ui button">Submit</button>
  </form>
</div>

视图中通过[formGroup]="myForm"绑定已创建的FormGroup,同时onSubmit也改为myForm.value。
而对于input元素,现在借由[formControl]="myForm.controls['sku']"方式绑定已创建的FormControl。

验证

向一个FormControl添加验证器很简单,只需要将其传入第二个参数。

let control = new FormControl('sku', Validators.required);

如果是FormBuilder的话,可以使用以下语法:

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

@Component({
  selector: 'app-demo-form-with-validations-explicit',
  templateUrl: './demo-form-with-validations-explicit.component.html',
  styles: []
})
export class DemoFormWithValidationsExplicitComponent {
  myForm: FormGroup;
  sku: AbstractControl;

  constructor(fb: FormBuilder) {
    this.myForm = fb.group({
    'sku': ['', Validators.required]
  });

    this.sku = this.myForm.controls['sku'];
  }

  onSubmit(value: string): void {
    console.log('you submitted value: ', value);
  }
}
<div class="ui raised segment">
<h2 class="ui header">Demo Form: with validations (explicit)</h2>
<form [formGroup]="myForm"
  (ngSubmit)="onSubmit(myForm.value)"
  class="ui form"
  [class.error]="!myForm.valid && myForm.touched">

    <div class="field"
      [class.error]="!sku.valid && sku.touched">
      <label for="skuInput">SKU</label>
      <input type="text"
        id="skuInput"
        placeholder="SKU"
        [formControl]="sku">
      <div *ngIf="!sku.valid"
        class="ui error message">SKU is invalid</div>
      <div *ngIf="sku.hasError('required')"
        class="ui error message">SKU is required</div>
    </div>

    <div *ngIf="!myForm.valid"
      class="ui error message">Form is invalid</div>

    <button type="submit" class="ui button">Submit</button>
  </form>
</div>

* 通过myForm的valid属性值可以判断所有FormControl是否皆有效。
* [class.error]可以根据FormControl有效与否变更样式。
* sku.hasError('required'),可以指定特定的验证要求。

验证器还能够按照需求定制:

function skuValidator(control: FormControl): { [s: string]: boolean } {
  if (!control.value.match(/^123/)) {
    return {invalidSku: true};
  }
}

constructor(fb: FormBuilder) {
  this.myForm = fb.group({
    'sku': ['', Validators.compose([
    Validators.required, skuValidator])]
  });
<div *ngIf="sku.hasError('invalidSku')"
  class="ui error message">SKU must begin with <span>123</span></div>

监察

FormGroup与FormControl都有自己的EventEmitter,可以用于观察变化。
方式很简单,使用其valueChanges的subscribe方法进行订阅。

constructor(fb: FormBuilder) {
  this.myForm = fb.group({
    'sku': ['', Validators.required]
  });

  this.sku = this.myForm.controls['sku'];

  this.sku.valueChanges.subscribe(
    (value: string) => {
      console.log('sku changed to:', value);
    }
  );

  this.myForm.valueChanges.subscribe(
    (form: any) => {
      console.log('form changed to:', form);
    }
  );
}
posted @ 2018-04-18 23:02  Ken.W  阅读(241)  评论(0编辑  收藏  举报