Angular 2 表單
- 2016-12-21
- Liu, An-Chi 劉安齊
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]
})
// 用來觀察表格元素的變化
console.log(this.complexForm);
this.complexForm.valueChanges.subscribe( (form: any) => {
console.log('form changed to:', 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>