一、演示自定义控件的双向绑定
二、如何实现
cform.component.ts(自定义表单控件CformComponent 的实现,注意下里面的注释段落)
import { Component, OnInit, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-cform',
template: `{{info}}
<button (click)="changeInfo()">改变info的值</button>`,
styleUrls: ['./form-c.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CformComponent),
multi: true
}
]
})
export class CformComponent implements OnInit, ControlValueAccessor {
info = '';
onModelChange = (val) => {};
onTouched = (val) => {};
constructor() { }
// 该方法用于将模型中的新值写入视图或 DOM 属性中,即model->view,从组件外输入到组件内会触发该方法
writeValue(obj: any): void {
this.info = obj;
}
// 设置当控件接收到 change 事件后,调用的函数,可以用来通知外部,组件内部值已经发生变化,即view->model,要主动调用this.onModelChange方法才可以。
registerOnChange(fn: any): void {
this.onModelChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
throw new Error('Method not implemented.');
}
ngOnInit() {
}
// 改变值然后通知组件外面,值已经变化了
changeInfo() {
this.info = 'another value';
this.onModelChange(this.info);
this.onTouched(this.info);
}
}
app.component.html(自定义表单控件的使用)
<div [formGroup]="formGroup">
<app-cform formControlName="name" #appCform></app-cform>
</div>
app.component.ts(自定义表单控件的使用)
import { Component, ViewChild } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { CformComponent } from './cform/form-c.component';
import { timer, interval } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
……
@ViewChild('appCform') appCform: CformComponent;
formGroup = new FormGroup({
name: new FormControl('just a test')
});
constructor(private cdRef: ChangeDetectorRef) {
// 立即发出值, 然后每5秒发出值
const source = timer(0, 5000).subscribe(value => {
console.log(value);
console.log(this.formGroup.value);
});
}
……
}
三、简单讲解
基本流程:实现angular自定义表单控件,得组件实现ControlValueAccessor这个接口,并且实现该接口的4个方法(4个方法看上面实现代码的逻辑。)。然后就完活。
writeValue 当这个组件初始化的时候,外面传入这个组件内部的值
registerOnChange
registerOnTouched
setDisabledState
NG_VALUE_ACCESSOR 类型 const,也就是提供者令牌
官网的解释:用于为表单控件提供ControlValueAccessor。
ControlValueAccessor 类型 interface
官网的解释:定义一个接口,该接口充当 Angular 表单 API 和 DOM 中的原生元素之间的桥梁,实现此接口以创建与 Angular 表单集成的自定义表单控件指令。
从该段话的描述不难发现,要想实现angular的自定义表单构件必须要实现这个ControlValueAccessor接口,实现接口那么就要实现接口中的writeValue、registerOnChange、registerOnTouched等方法,而NG_VALUE_ACCESSOR这个提供者用于为表单控件提供ControlValueAccessor服务,所以要在组件中指定提供者,指定所依赖的提供者用到useExisting: forwardRef(() => CformComponent)。