angular11源码探索十六[自定义表单组件封装]

AbstractControl

api

源码validator

由注册到此的所有同步验证器组成的同步验证器函数

 get validator(): ValidatorFn|null {
    return this._composedValidatorFn || null;
  }

export function composeValidators(validators: Array<Validator|ValidatorFn>): ValidatorFn|null {
  return validators != null ? Validators.compose(normalizeValidators<ValidatorFn>(validators)) :null;
}
将多个验证器组合成一个返回联合的函数
*所提供控件的个别错误映射
 static compose(validators: null): null;
  static compose(validators: (ValidatorFn|null|undefined)[]): ValidatorFn|null;
  static compose(validators: (ValidatorFn|null|undefined)[]|null): ValidatorFn|null {
    if (!validators) return null;
    const presentValidators: ValidatorFn[] = validators.filter(isPresent) as any;
    if (presentValidators.length == 0) return null;

    return function(control: AbstractControl) {
      return mergeErrors(executeValidators<ValidatorFn>(control, presentValidators));
    };
  }
function mergeErrors(arrayOfErrors: (ValidationErrors|null)[]): ValidationErrors|null {
  let res: {[key: string]: any} = {};
    // 没有使用数组的形式,chrome 80 之前有个bug,
    // 我们发现它把所有报错信息放在这个对象中
  arrayOfErrors.forEach((errors: ValidationErrors|null) => {
    res = errors != null ? {...res!, ...errors} : res!;
  });

  return Object.keys(res).length === 0 ? null : res;
}

案例

 addFn(c: FormControl): ValidationErrors | null {
    return c.value == 11 ? {sex: true} : null;
  }
 this.profileForm = new FormGroup({
  firstName: new FormControl('xxx', [Validators.required, Validators.max(8), Validators.min(1), this.addFn])
          })
  //验证1,3没有通过
    console.log(this.profileForm.get('firstName').validator(new FormControl(11)));
    // {max: {max: 8, actual: 11},sex: true}

asyncValidator

异步校验

export interface AsyncValidator extends Validator {
  validate(control: AbstractControl):
      Promise<ValidationErrors|null>|Observable<ValidationErrors|null>;
}
Promise.resolve(null)
of(null)
of({name1:true})

AbstractControls

逻辑共享跨越FormControlFormGroupFormArray

// FG - FormGroup
// FA - FormArray
// FC - FormControl

    FG
  /   
FC    FG
    /    
  FC     FA
        / | 
      FC FC FC

CheckboxControlValueAccessor

复选框

  • input[type=checkbox][formControlName]
  • input[type=checkbox][formControl]
  • input[type=checkbox][ngModel]

事件

change事件,blur事件

formControl

<form [formGroup]="profileForm">
  <label >
    <input type="checkbox" formControlName="lastName" >
  </label>
  <label >
    <input type="checkbox" formControlName="firstName" >
  </label>
</form>

 profileForm: FormGroup;
  ngOnInit(): void {
    this.profileForm = new FormGroup({
      firstName: new FormControl(false),
      lastName: new FormControl(false)
    });
    this.profileForm.valueChanges.subscribe(console.log)
  }

formControlName

<form [formGroup]="profileForm">
  <div formArrayName="firstArr" *ngFor="let item of getArr;let i=index">
    <label>
      <input type="checkbox" [formControlName]="i">
    </label>
  </div>
</form>

export class TwoComponent implements OnInit, AfterViewInit {
  profileForm: FormGroup;
  constructor() {
    this.profileForm = new FormGroup({
      firstArr: new FormArray(
        [
          new FormControl(false),
          new FormControl(false),
          new FormControl(false),
        ]
      )
    });
  }
  get getArr(): FormArray {
    return this.profileForm.get('firstArr')?.controls
  }
 ngOnInit(): void {
    this.profileForm.get('firstArr').valueChanges.subscribe(console.log)
  }
}

ngModel

可以使用事件拿稍微好一些

<input type="checkbox" [(ngModel)]="objArr.age1" (change)="clickChange($event)">
<input type="checkbox" [(ngModel)]="objArr.age2" (blur)="blurChange()">
<input type="checkbox" [(ngModel)]="objArr.age3">
  
 objArr={
    age1:false,
    age2:false,
    age3:false
  }
 
  blurChange(){
    console.log(this.objArr);
  }
  clickChange(e) {
    console.log(e,this.objArr);
  }

ControlValueAccessor自定义表单组件

interface ControlValueAccessor {
  writeValue(obj: any): void
  registerOnChange(fn: any): void
  registerOnTouched(fn: any): void
  setDisabledState(isDisabled: boolean)?: void
}

该接口充当Angular表单API和DOM中的本机元素之间的桥梁。

使用方法可以参考CheckboxControlValueAccessor源码

经常很多的尝试和研究终于弄懂了

<app-b [formControl]="formControl" (change)="clickChange($event)"></app-b>

 formControl = new FormControl({ value: { scope: '12', query: '44' }})

ngAfterViewInit() {
   	 //类似change的事件
  	  this.formControl.valueChanges.subscribe(console.log)
}
  //失去焦点
  clickChange(e){
    console.log(1,this.formControl.value);
  }

子组件,注意了,要查看具体步奏

<div [formGroup]="form">
  <label >
    <input type="text" formControlName="scope">
    <input type="text" formControlName="query">
  </label>
</div>


@Component({
  selector: 'app-b',
  templateUrl: './b.component.html',
  styleUrls: ['./b.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => BComponent),
      multi: true,
    }
  ]
})
export class BComponent implements OnInit, ControlValueAccessor, AfterViewInit {
  form: FormGroup;
     constructor(
    private fb: FormBuilder,
  	) {
    this.form = this.fb.group({
      scope: new FormControl(''),
      query: new FormControl(''),
    });
  }
    // 父传子赋值
     @Input()
      set value(value: FormFieldValue) {
        this.form.patchValue(value);
      }
    ngAfterViewInit() {
        this.form.valueChanges.subscribe(value => {
            this.onChange(value)
          }
        )
      } 
      //父传子的时候赋值给子
  writeValue(obj: any): void {
    this.value = obj;
  }
  //同步更新的事件
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  //失去焦点的事件
  registerOnTouched(fn: any): void {
    this.onToutch = fn;
  }
  //父传子禁用的值
  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }
}

给属性默认添加事件

官网

源码

[ngModel],[formControl],[formControlName]
 host: {
    '(input)': '$any(this)._handleInput($event.target.value)',
    '(blur)': 'onTouched()',
    '(compositionstart)': '$any(this)._compositionStart()',
    '(compositionend)': '$any(this)._compositionEnd($event.target.value)'
  },
      都具有获取change事件和blur事件 

例如

<form [formGroup]="profileFormOne">
  <input type="text" formControlName="lastName1" (input)="clickChange($event.target)" (blur)="blurChange($event.target)">
  <input type="text" formControlName="firstName">
</form>    
//change事件
 clickChange(e: EventTarget) {
    console.log(e.value);
  }
// blur事件
  blurChange(target: EventTarget) {
    console.log(target.value);
  }

'(compositionstart)' '(compositionend)' 这两种实验无效
原文地址:https://www.cnblogs.com/fangdongdemao/p/14238813.html