angular11源码探索十六[自定义表单组件封装]
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)' 这两种实验无效
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2020-01-06 本周学习总结