響應式表單提供了一種模型驅動的方式來處理表單輸入,其中的值會隨時間而變化。本文會向你展示如何建立和更新基本的表單控制元件,接下來還會在一個表單組中使用多個控制元件,驗證表單的值,以及建立動態表單,也就是在執行期新增或移除控制元件。
這裡的 require 建儀是寫在 formGroup 裡面去作理管,才可方便的在 js 中看到何為必填值。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | <form [formGroup]="subscribeForm" (ngSubmit)="submit()"><mat-form-field class="w-full">
 <input
 matInput
 required
 type="email"
 formControlName="email"
 />
 <mat-error> Please enter a valid email address </mat-error>
 </mat-form-field>
 <button type="submit">submit</button>
 </form>
 
 | 
invalid function
透過下列這段 checked form 是否有尚未寫好的值,或是沒有通過的 validations。
disable enable
可把欄位 disable 和 enable,但是特別注意 disable() 的值,如果直接用 this.form.value 會拿不到值
| 12
 
 | this.formName.controls["control"].disable();this.formName.controls["control"].enable();
 
 | 
如果想要拿到 disable() 的值,需要用 getRawValue() 來取得到已被 disable() 的值
| 1
 | this.formName.getRawValue();
 | 
How do I restrict an input to only accept numbers?
| 1
 | <input type="number" ng-model="myText" name="inputName">
 | 
How do I restrict an input to only accept numbers?
form 的表單很大的情況,希望可以有結構化的去管理資料,可以 nest formGroup 的方式,方便一目了然的去抓到資料
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | this.contactForm = this.formBuilder.group({firstname: ["", [Validators.required, Validators.minLength(10)]],
 lastname: ["", [Validators.required, Validators.maxLength(15), Validators.pattern("^[a-zA-Z]+$")]],
 email: ["", [Validators.required, Validators.email]],
 gender: ["", [Validators.required]],
 isMarried: ["", [Validators.required]],
 country: ["", [Validators.required]],
 address: this.formBuilder.group({
 city: ["", [Validators.required]],
 street: ["", [Validators.required]],
 pincode: ["", [Validators.required]],
 }),
 });
 
 | 
https://www.tektutorialshub.com/angular/angular-formbuilder-in-reactive-forms/
Validations
下列是 angular 在 formGroup 中提供的 validations 的方式,當然也可以自己去寫 customer 的 validation function
Angular
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | class Validators {static min(min: number): ValidatorFn
 static max(max: number): ValidatorFn
 static required(control: AbstractControl): ValidationErrors | null
 static requiredTrue(control: AbstractControl): ValidationErrors | null
 static email(control: AbstractControl): ValidationErrors | null
 static minLength(minLength: number): ValidatorFn
 static maxLength(maxLength: number): ValidatorFn
 static pattern(pattern: string | RegExp): ValidatorFn
 static nullValidator(control: AbstractControl): ValidationErrors | null
 static compose(validators: ValidatorFn[]): ValidatorFn | null
 static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn | null
 }
 
 | 
updateValidations
setErrors 如果是 null 的情況下,會把原本的 required 的設定也都拿掉,所以要特別小心
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | this.formName.get("formControlName").setValidators([Validators.required]);
 this.formName.get("formControlName").setErrors({ required: true });
 this.formName.get("formControlName").setErrors({ required: false });
 this.formName.get("formControlName").setErrors(null);
 
 this.myForm.controls["controlName"].clearValidators();
 
 this.formName.updateValueAndValidity();
 
 this.formName.controls.reset();
 
 
 | 
NOTE 如果是在另一個 component 中要去 control 另一個 form 給它 setErrors 的話可以加上面這一段,這樣子才可以正常的設定 error
| 1
 | this.formName.markAsTouched({ onlySelf: true });
 | 
Angular reactive forms set and clear validators
https://www.tektutorialshub.com/angular/how-to-add-validators-dynamically-using-setvalidators-in-angular/
hasError
在操作 js 的時候,需要有一個特定的 error style 可以用 setErrors 來設定 boolean 讓 template 來使用
| 1
 | this.productsForm[index].controls["unit_price"].setErrors({ bottomPrice: true });
 | 
| 12
 3
 
 | <mat-error *ngIf="productsForm[index].get('unit_price').hasError('bottomPrice')"><span>Please enter bottomPrice.</span>
 </mat-error>
 
 | 
清除不需要 error style 可以把此方法,我原本設用的 error 移除
| 1
 | this.addProductForm.controls["part_no"].setErrors(null);
 | 
| 12
 3
 4
 
 | note;const pattern = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}/;
 
 const pattern = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
 
 | 
Custom & Async Validators
Custom Validators
Custom Async Validators
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | import { Injectable } from '@angular/core';import {AbstractControl, FormControl, FormGroup, ValidationErrors} from '@angular/forms';
 import {Observable, of} from 'rxjs';
 import {delay, map} from 'rxjs/operators';
 
 @Injectable({
 providedIn: 'root'
 })
 export class ZipcodeService {
 private validZipcodes = ['00001', '00002', '00003', '00004'];
 
 fakeHttp(value: string) {
 return of(this.validZipcodes.includes(value)).pipe(delay(5000));
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | import { ZipcodeService } from "./zipcode.service";import { AbstractControl, AsyncValidatorFn, ValidationErrors } from "@angular/forms";
 import { Observable } from "rxjs";
 import { map } from "rxjs/operators";
 
 export class ZipcodeValidator {
 static createValidator(zipcodeService: ZipcodeService): AsyncValidatorFn {
 return (control: AbstractControl): Observable<ValidationErrors> => {
 return zipcodeService.fakeHttp(control.value).pipe(map((result: boolean) => (result ? null : { invalidAsync: true })));
 };
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | import {Component, OnInit} from '@angular/core';import { FormControl, FormGroup, Validators} from '@angular/forms';
 import {ZipcodeService} from './zipcode.service';
 import {ZipcodeValidator} from './zipcode-validator';
 
 @Component({
 selector: 'app-async-validator-demo',
 templateUrl: './async-validator-demo.component.html',
 styleUrls: ['./async-validator-demo.component.scss']
 })
 export class AsyncValidatorDemoComponent implements OnInit {
 address: FormGroup;
 zipcodeSyncValidators = [
 Validators.required,
 Validators.pattern('\\d{5}')
 ];
 
 constructor(private zipcodeService: ZipcodeService) {}
 
 ngOnInit(): void {
 this.address = new FormGroup({
 zipcode: new FormControl('',
 this.zipcodeSyncValidators,
 ZipcodeValidator.createValidator(this.zipcodeService))
 });
 }
 }
 
 | 
Angular: Custom Async Validators
https://offering.solutions/blog/articles/2020/05/03/cross-field-validation-using-angular-reactive-forms/#adding-custom-validators-to-a-single-form-control
有些情況下,submit form 之後,要在清除 form 的資料,可用下列的方式去作 clear 的動作。
| 1
 | <form [formGroup]="addProductForm" (ngSubmit)="addProduct()" #formDirective="ngForm">...</form>
 | 
| 12
 3
 4
 5
 6
 7
 
 | export class CorporateQuotationPageComponent implements OnInit {@ViewChild('formDirective') formDirective;
 ...
 resetForm() {
 this.formDirective.resetForm();
 }
 }
 
 | 
reset() vsj resetForm() ??
ValueChange
| 1
 | this.formName.controls["control"].setValue(data);
 | 
EmitEvent & ValueChanges
coming soon …
OnlySelf & ValueChanges
coming soon …
value change 參考文章