AbstractControl
源码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
逻辑共享跨越FormControl
,FormGroup
和FormArray
// 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)' 这两种实验无效