響應式表單提供了一種模型驅動的方式來處理表單輸入,其中的值會隨時間而變化。本文會向你展示如何建立和更新基本的表單控制元件,接下來還會在一個表單組中使用多個控制元件,驗證表單的值,以及建立動態表單,也就是在執行期新增或移除控制元件。
這裡的 require 建儀是寫在 formGroup 裡面去作理管,才可方便的在 js 中看到何為必填值。
1 2 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
會拿不到值
1 2
| 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
的方式,方便一目了然的去抓到資料
1 2 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
1 2 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 的設定也都拿掉,所以要特別小心
1 2 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 });
|
1 2 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);
|
1 2 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
1 2 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)); } }
|
1 2 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 }))); }; } }
|
1 2 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>
|
1 2 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 參考文章