1. 程式人生 > 實用技巧 >angular11原始碼探索十四[表單校驗器]

angular11原始碼探索十四[表單校驗器]

自帶的限制條件

Validators.required
報錯
{'required': true}

驗證欄位值是否為true
Validators.requiredTrue
{required: true}

郵箱
Validators.email
{email: true}

最小長度
Validators.minLength(3)
{minlength: {requiredLength: 3, actualLength: 2}}
還可以直接在input使用
<input minlength="5">
    
 最大長度
Validators.maxLength(5)
{maxlength: {requiredLength: 5, actualLength: 7}
 <input maxlength="5">
     
正則
Validators.pattern(/foo/) 
{pattern: {requiredPattern: '^[a-zA-Z ]*$', actualValue: '1'}
 <input pattern="[a-zA-Z ]*"> 這個頁也可以直接在頁面使用

自定過濾器

//這個傳引數的版本
export function maxLength(maxLength: number): ValidatorFn {
    return (control: AbstractControl): ValidationErrors|null => {
      return hasValidLength(control.value) && control.value.length > maxLength ?
          {'maxlength': {'requiredLength': maxLength, 'actualLength': control.value.length}} :
          null;
    };
  }
如果不傳引數的話就是這樣的
這個自己編寫的自定義的
export type ValidationErrors = {
  [key: string]: any
};
export function (control: AbstractControl): ValidationErrors|null {
      return  control.value.length > maxLength ?
          {'maxlength': {'requiredLength': maxLength, 'actualLength': control.value.length}} :null;
    };
  }

這裡可以參考原始碼中的寫法

export interface Validator {
  /**
   這個是直接的
   */
  validate(control: AbstractControl): ValidationErrors|null;

  /**
註冊一個回撥函式,當驗證器輸入改變時呼叫, 暫時沒發現用處
   */
  registerOnValidatorChange?(fn: () => void): void;
}

管道的自定義指令的時候

@Directive({
  selector: '[customValidator]',
  // 註冊指令,然後頁面使用  
  providers: [{provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true}]
})
class CustomValidatorDirective implements Validator {
  validate(control: AbstractControl): ValidationErrors|null {
    return {'custom': true};
  }
}

禁用清空錯誤

const arr = new FormArray([new FormControl()], () => ({'expected': true}));
arr.errors  // {'expected': true}
arr.disable();
arr.errors // null
arr.enable(); //解除禁用,就繼續報錯

但是當禁用的時候,重新給陣列新增一個FormControl 也促發髒依賴更新,所以也會報錯

FomrBuilder

 fbForm: FormGroup;
 constructor(
    private  fb: FormBuilder
  ) {
    this.fbForm = this.fb.group({
      firstName: 'some value',
      login: ['some',[Validators.required]],
      //禁用
      should: {value: 'should', disabled: true},
      login:fb.control('',{updateOn:'blur'}),
    });
  this.fbForm = this.fb.group({
        firstName: 'some value',
      //第三個引數是非同步校驗器
        login: ['some', null,[this.asyncValidator1, this.asyncValidator2]],
        arrOne: this.fb.array(
          ['one', 'two'],
          null,
          [this.asyncValidator1, this.asyncValidator2]
        )
      }
    );
    console.log(this.fbForm.get('arrOne')?.errors);
    console.log(this.fbForm.get('login')?.errors);
// {'async1': true, 'async2': true}

FormControl 第二個引數配置的幾種形式

如果第一個引數,那麼值主要看value的值,第二個引數還是校驗
const control = new FormControl({ value: 'n/a', disabled: true });
const control = new FormControl('', Validators.required);
const control = new FormControl('', {
     validators: Validators.required,// 校驗器
    asyncValidators: myAsyncValidator,// 非同步校驗器
    updateOn: 'blur'// 校驗方式
 });
事件更新
更新策略AbstractControl(表示控制元件自行更新的事件)。
離開的時候,才會觸發校驗
可能的值:'change'| 'blur'| 'submit' 預設值:'change'

我們發現FormGroup, FormControl,FormArray 類似,第二個引數都可以這樣寫
 this.fbForm = this.fb.group({
        firstName: '',
       },{updateOn: 'blur',validators:this.addFn}
    );
 const a = new FormArray([new FormControl(), new FormControl()], {updateOn: 'blur'});
const control = new FormControl('', { updateOn: 'blur' });
const control = new FormControl('', { updateOn: 'submit' });
====
 addFn(control: AbstractControl) {
    //   更改了,觸發事件是失去焦點促發
    if (control.dirty) {
      console.log(control.value);
      return {name1: true};
    }
  }   

  this.fbForm = this.fb.group({
        firstName: ['some value', {
          updateOn: 'blur',
          validators: [this.addFn]
        }],
       three:fb.array([
        'one','two'
       ])
  })
    console.log(this.fbForm.get(['three',1])?.value);// get 還能這樣查

設定校驗

setValidators
      const c = new FormControl(null);
	//單個值
      c.setValidators(Validators.required);
	// 多個值
      c.setValidators([Validators.minLength(5), Validators.required]);
	// 清空校驗器
     c.clearValidators();
	//設定非同步校驗器
    c.setAsyncValidators(asyncValidator('expected'));
	//多個非同步校驗器
    c.setAsyncValidators([asyncValidator('expected')]);

非同步校驗器

export function asyncValidator(expected: string, timeouts = {}): AsyncValidatorFn {
  return (control: AbstractControl) => {
    const timeout = (timeouts as any)[control.value] ?? 0;
    const result = control.value != expected ? {async: true} : null;
    return createValidationPromise(result, timeout);
  };
}
function createValidationPromise(
    result: ValidationErrors|null, timeout: number): Promise<ValidationErrors|null> {
  return new Promise(resolve => {
    if (timeout == 0) {
      resolve(result);
    } else {
      setTimeout(() => {
        resolve(result);
      }, timeout);
    }
  });
}

使用案例

function asyncFnOne(c:string): AsyncValidatorFn {
  return (control: AbstractControl)=> {
    console.log(control.value);
    return of({name1:true})
  }
}
function asyncFnTwo(c:string): AsyncValidatorFn {
  return (control: AbstractControl)=> {
    console.log(control.value);
    return of({name2:true})
  }
}  

this.profileForm = new FormGroup({
      firstName: new FormControl(1, null!, 				[asyncFnOne('xxx'),asyncFnTwo('bbb')])
    });
   this.profileForm.get('firstName').asyncValidator(new FormControl(11))
      .subscribe(console.log);
    // {name1: true, name2: true}