angular

Angular 表單應用及驗證

張家瑄 2019/06/11 11:11:18
259

Angular 表單應用

概述

在 Angular framework 中, Form 實作區分為分為兩種Module:

  • ReactiveFormsModule : Model driven form (Reactive form)
  • FormsModule  :  Template driven form

功能上,兩者均可以達成表單控制項操作、驗證、事件觸發等等;而 Model driven form 又可稱為 Reactive form,是由於 Model driven form 是以 reactive、意即observable模式設計,控制項的事件以資料流的方式建構表單,因此這些資料是即時同步被更新的。

Template driven form 是以 ngModel 和控制項進行two-way-binding方式驅動( 詳見:Angular官網  ),適用於快速開發單純的表單欄位及驗證,但由於包含驗證的程式碼均位於html,控制項的事件也並非同步,可能導致後續測試的困擾,較不適用於複雜結構的表單。

Angular Form 基本類別介紹
  • AbstractControl: FormControl、FormGroup、FormArray這三個實例表單類的抽象基類,提供了subclasses的通用行為以及屬性,例如observable。
  • FormControl:在單個表單元件中檢查值並驗證狀態(比如input、select等等)。
  • FormGroup:包含AbstractControl索引或名稱對應實體的集合(controls屬性),通常一個form表單會綁定至少一個FormGroup。
  • FormArray:用索引的方式去追蹤檢查表單的驗證狀態。
建構 Reactive Form

我們可以利用建構函式,或類別 FormBuilder 的.group( )、array( )方法初始化出FormGroup實體。

constructor(private fb: FormBuilder) { }

formModel: FormGroup;

ngOnInit() {
    // 建構函式
    this.formModel = new FormGroup(
        { 'name' : new FormControl( '', Validators.required) }
    );
    // FormBuilder
    this.formModel = this.fb.group({name: ['', Validators.required]})
}

而在.html中,則僅需綁定FormGroup以及formControl ( 亦有其他相似語法,如:[FormControl]="formModel.get('name')" )

<form [formGroup]="formModel">
    <input type="text" formControlName="name"/>
</form>

在上面的程式碼中,可以看到Validators.required,在初始化時也加到FormControl中,這邊的required是Angular提供的靜態方法,將HTML5 Form Validation,以介面 ValidatorFn 的方式實作,讓我們能將邏輯都集中在.ts當中處理,保持html簡單、僅提供畫面及綁定;另外,我們亦可以實作 ValidatorFn介面來客製化驗證邏輯。

(以下為實作 Template Driven Form 的 html程式碼,驗證邏輯required直接以Directive形式綁定 html。)

<form>
    <input type="text" name="name" [(ngModel)]="data.name" required />
</form>
Reactive Form、 Template Driven Form 範例程式 ( Live Example / GitHub )

範例中,主要有兩個頁面:

  • Abstract Controls : 呈現AbstractCotrol三種子類別,並應用 FormArray 達成動態生成欄位的效果,當FormGroup物件改變,屬性綁定的<form>亦隨之改變。
  • Form Validation : 呈現 Reactive form 及 Template driven form 表單的驗證比較;其中實作了 ValidatorFn 讓 Reactive form 使用,而為了讓 Template driven form 也能使用,實作Validator介面用Directive的方式叫用,請見節錄程式碼 :
/** age should not less than 0 or larger than 120 */
export const ageValidator: ValidatorFn = (control: FormControl): ValidationErrors | null => {
    const age = +control.value;
    if (!control.value || control.value === '') return null;
    return !(age && age > 0 && age < 120) ? { 'ageValidator': true } : null;
};

@Directive({
    selector: '[appValidAge]',
    providers: [{
    provide: NG_VALIDATORS,
    useExisting: forwardRef(() => ValidAgeDirective),
    multi: true
    }]
})
@Injectable({ providedIn: 'root' })
export class ValidAgeDirective implements Validator {
    validate(control: AbstractControl): ValidationErrors {
    return ageValidator(control)
    }
}
Reactive Form、 Template Driven Form 比較
  Reactive Form Template Driven Form
    建構方式 在component.ts中建立,較為明確、集中,和html耦合性較低。 在html中以ngModel等Directive方式建立,開發快速。
    綁定之資料模型 可為樹狀結構 非樹狀
    表單驗證 實作ValidationFn Directive
    表單控制項變動 同步,valueChanges 傳出observale streams 非同步,控制項事件觸發ngModelChange
    適用狀況 巢狀資料模型、需要動態處理控制項及驗證 單層資料模型、無其他相依表單
 
後記

本文範例中,主要透過 Angular Material form-field 來呈現控制項錯誤效果,而為了跨控制項的錯誤效果,另實作了Material Input 的 ErrorStateMatcher介面,詳細請見(Material官網: Input ErrorMatcher)。

 

參考資料:

Angular 官網: forms-overview

Reactive Forms (Model-Driven Forms)   Claire Chang

alligator.io: Reactive-forms with formArray and dynamic-fields

 

張家瑄