Angular 框架仰賴著 HTML 5 強大的功能,本身已經包括雙向綁定、變化跟踪、驗證、錯誤處理等功能。
可以處理:

  • Component 和 Template 構建表單
  • 雙向綁定[(ngModel)]讀寫表單表。
  • ngControl追蹤表單狀態和有效性。
  • 根據ngControl改變 CSS。
  • 顯示驗證錯誤消息並啟用/禁止表單。
  • 使用本地模板變量共享控制間的信息。

簡單版

// app.simpleform.html

<!-- 這邊有用 Bootstrap CSS 唷! –>
<div class="jumbotron">
<h2>Template Driven Form</h2>
<!-- 這邊我們宣告一個區域變數 "form" 並讓它變成 ngForm 的實例。這樣子可以讓 "form" 使用 FormGroup的 API,我們就能夠用 ngSubmit 送出 form.value 表格的值。–>
<form #form="ngForm" (ngSubmit)="submitForm(form.value)">
<div class="form-group">
<label>First Name:</label>
<!-- 這邊的 ngModal 是單向綁定,只會送資料回去。當然我們也可以用 [(ngModal)] 來雙向綁定表格的值 –>
<input type="text" class="form-control" placeholder="John" name="firstName" ngModel required>
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" class="form-control" placeholder="Doe" name="lastName" ngModel required>
</div>
<div class="form-group">
<label>Gender</label>
</div>
<!-- Radio 和 checkbox 跟一般的 HTML用法差不多 –>
<div class="radio">
<label>
<input type="radio" name="gender" value="Male" ngModel>
Male
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="gender" value="Female" ngModel>
Female
</label>
</div>
<div class="form-group">
<label>Activities</label>
</div>
<label class="checkbox-inline">
<input type="checkbox" value="hiking" name="hiking" ngModel>
Hiking
</label>
<label class="checkbox-inline">
<input type="checkbox" value="swimming" name="swimming" ngModel>
Swimming
</label>
<label class="checkbox-inline">
<input type="checkbox" value="running" name="running" ngModel>
Running
</label>
<div class="form-group">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
</div>

// app.simpleform.ts

import { Component } from '@angular/core';

@Component({
  selector: 'simple-form',
  templateUrl: 'app.simpleform.html'
})
export class SimpleFormComponent {
  //這邊設定提交後要幹嘛,目前只是 Console.log 出資料而已
  submitForm(form: any): void{
    console.log('Form Data: ');
    console.log(form);
  }
}

程式碼就不多解釋,都有加註解,此外 required 會強制要求使用者輸入,否則就會無法提交。
上面的範例會長得像這樣

複雜一點點版本

好的例子勝過萬言

// app.complexform.ts

import { Component } from '@angular/core';
// 需要引入一些程式庫
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
selector: 'complex-form',
templateUrl : './app.complexform.html'
})
export class ComplexFormComponent {
// 新建一個 FormGroup 物件
complexForm : FormGroup;

// 建構一個實例 FormBuilder
constructor(fb: FormBuilder){
// 用 FormBuilder 來製造我們的表格
this.complexForm = fb.group({
// 定義表格的預設值
'firstName' : '',
'lastName': '',
'gender' : 'Female',
'hiking' : false,
'running' : false,
'swimming' : false
})
}

// 提交的執行的 function
submitForm(value: any): void {
console.log('Reactive Form Data:', value);
}
}

//app.complexform.html

<div class="jumbotron">
    <h2>Data Driven (Reactive) Form</h2>
    <!-- 現在我們不宣告區域變數了,改用formGroup 這個指令,並將它定義成 complexForm 物件。 complexForm 將會控制表格所有的變數 -->
    <form [formGroup]="complexForm" (ngSubmit)="submitForm(complexForm.value)">
      <div class="form-group">
        <label>First Name:</label>
        <!-- 不用 ngModel 而改用 formControl 指令可以同步 input 到 complexForm 物件。現在也不需要 name 屬性了,但還是可以選擇加入 -->
        <input class="form-control" type="text" placeholder="John" [formControl]="complexForm.controls['firstName']">
      </div>
      <div class="form-group">
        <label>Last Name</label>
        <input class="form-control" type="text" placeholder="Doe" [formControl]="complexForm.controls['lastName']">
      </div>
      <div class="form-group">
        <label>Gender</label>
      </div>
      <div class="radio">
        <label>
          <input type="radio" name="gender" value="Male" [formControl]="complexForm.controls['gender']">
          Male
        </label>
      </div>
      <div class="radio">
        <label>
          <!-- radio 和 checkbox 用法也一樣 -->
          <input type="radio" name="gender" value="Female" [formControl]="complexForm.controls['gender']">
          Female
        </label>
      </div>
      <div class="form-group">
        <label>Activities</label>
      </div>
      <label class="checkbox-inline">
        <input type="checkbox" value="hiking" name="hiking" [formControl]="complexForm.controls['hiking']"> Hiking
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" value="swimming" name="swimming" [formControl]="complexForm.controls['swimming']"> Swimming
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" value="running" name="running" [formControl]="complexForm.controls['running']"> Running
      </label>
      <div class="form-group">
        <button type="submit" class="btn btn-default">Submit</button>
      </div>
    </form>
  </div>

上面複雜一點點的範例,更貼切 Angular 的用法,跑出來會長這樣。
也可以看到 Female 為預設值。

表格驗證

Angular 2 的表格驗證支援模板控制以及組件控制,但是透過組件控制在表格驗證上有更大的彈性。詳細可以參考 Angular 2 docs

我們從複雜板來加入表格驗證功能

//app.validationform.ts

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

@Component({
selector: 'form-validation',
template : 'app.validationform.html'
})
export class FormValidationComponent {
complexForm : FormGroup;

constructor(fb: FormBuilder){
this.complexForm = fb.group({
// 表示一定要輸入
'firstName' : [null, Validators.required],
// 表示一定要輸入,而且最短為5個字元,最多為10個字元。有多個規則時用陣列包住。
'lastName': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10)])],
'gender' : [null, Validators.required],
'hiking' : [false],
'running' : [false],
'swimming' : [false]
})

// &#x7528;&#x4F86;&#x89C0;&#x5BDF;&#x8868;&#x683C;&#x5143;&#x7D20;&#x7684;&#x8B8A;&#x5316;
console.log(this.complexForm);
this.complexForm.valueChanges.subscribe( (form: any) =&gt; {
  console.log(&apos;form changed to:&apos;, form);
}
);

}

// 提交執行的程式
submitForm(value: any){
console.log(value);
}
}

  //app.validationform.html
  
  <div class="jumbotron">
    <h2>Form with Validations</h2>
    <form [formGroup]="complexForm" (ngSubmit)="submitForm(complexForm.value)">
      <!-- 填寫的值必須有效 且 可以讓使用者點擊過一次之後才會顯示 Error ( ngClass 使顏色改變) -->
      <div class="form-group" [ngClass]="{'has-error':!complexForm.controls['firstName'].valid && complexForm.controls['firstName'].touched}">
        <label>First Name:</label>
        <input class="form-control" type="text" placeholder="John" [formControl]="complexForm.controls['firstName']">
        <!-- .hasError('why')可以在發生填寫錯誤時告訴使用者為甚麼 Error -->
        <div *ngIf="complexForm.controls['firstName'].hasError('required') && complexForm.controls['firstName'].touched" class="alert alert-danger">You must include a first name.</div>
      </div>
      <div class="form-group" [ngClass]="{'has-error':!complexForm.controls['lastName'].valid && complexForm.controls['lastName'].touched}">
        <label>Last Name</label>
        <input class="form-control" type="text" placeholder="Doe" [formControl]="complexForm.controls['lastName']">
        <!-- .hasError('required')代表必須要填 -->
        <div *ngIf="complexForm.controls['lastName'].hasError('required') && complexForm.controls['lastName'].touched" class="alert alert-danger">You must include a last name.</div>
        <!-- .hasError('minlength')代表太短 -->
        <div *ngIf="complexForm.controls['lastName'].hasError('minlength') && complexForm.controls['lastName'].touched" class="alert alert-danger">Your last name must be at least 5 characters long.</div>
        <div *ngIf="complexForm.controls['lastName'].hasError('maxlength') && complexForm.controls['lastName'].touched" class="alert alert-danger">Your last name cannot exceed 10 characters.</div>
      </div>
      <div class="form-group">
        <label>Gender</label>
        <!-- Radio 無法上色,所以只用提示字串 -->
        <div class="alert alert-danger" *ngIf="!complexForm.controls['gender'].valid && complexForm.controls['gender'].touched">You must select a gender.</div>
      </div>
      <div class="radio">
        <label>
          <input type="radio" name="gender" value="Male" [formControl]="complexForm.controls['gender']">
          Male
        </label>
      </div>
      <div class="radio">
        <label>
          <input type="radio" name="gender" value="Female" [formControl]="complexForm.controls['gender']">
          Female
        </label>
      </div>
      <div class="form-group">
        <label>Activities</label>
      </div>
      <label class="checkbox-inline">
        <input type="checkbox" value="hiking" name="hiking" [formControl]="complexForm.controls['hiking']"> Hiking
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" value="swimming" name="swimming" [formControl]="complexForm.controls['swimming']"> Swimming
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" value="running" name="running" [formControl]="complexForm.controls['running']"> Running
      </label>
      <div class="form-group">
        <button type="submit" class="btn btn-primary" [disabled]="!complexForm.valid">Submit</button>
      </div>
    </form>
  </div>