Angular NGM模型,但使用反应形式验证方法
我不喜欢Angular的任何一种表单验证方法,但我正在研究一种将每种方法的优点结合起来的方法,并且接近我喜欢的答案 我知道,无论我选择Angular NGM模型,但使用反应形式验证方法,angular,forms,validation,reactive,ngmodel,Angular,Forms,Validation,Reactive,Ngmodel,我不喜欢Angular的任何一种表单验证方法,但我正在研究一种将每种方法的优点结合起来的方法,并且接近我喜欢的答案 我知道,无论我选择formBuilder还是ngModel路线,表单都有一个NgForm,它有一个包含根FormGroup的属性,该根FormGroup具有FormControl对象的异构集合。HTML元素都有一个adapter对象来实现ControlValueAccessor接口,我自己的角度组件,如可以实现相同的接口,并假装只是另一个元素,其值是任意复杂的对象。每个FormCo
formBuilder
还是ngModel
路线,表单都有一个NgForm
,它有一个包含根FormGroup
的属性,该根FormGroup
具有FormControl
对象的异构集合。HTML元素都有一个adapter对象来实现ControlValueAccessor
接口,我自己的角度组件,如
可以实现相同的接口,并假装只是另一个元素,其值是任意复杂的对象。每个FormControl
包装一个元素,通过ControlValueAccessor
接口与它对话,因此它不知道它实际上在与什么对话
我知道在元素上放置ngModel
或formControl
指令将为该元素创建formControl
实例;即使
标记自动获取NgForm,元素也不会自动获取一个
我知道formBuilder
会显式创建空心FormControl
s,它缺少HTML元素,但每个都有一个名称,在HTML中formControlName
给HTML元素一个名称,但没有FormControl
实例,基本上,formControlName
和formBuilder
都与一个与名称匹配的服务对话,并用其元素填充FormControl
中的空洞
最后,FormControl
是验证器所在的位置,以及dirty/toucted/etc.属性
我对ngModel
的问题和大家的一样:验证很糟糕。自定义验证只不过是一个if
语句的条件,但是ngModel
希望我将这个小条件包装在一个完整的指令中,并将其粘贴到元素的HTML中。对于if
语句来说,这需要大量额外的输入——您无法使一行代码可重用,因为使用包装器需要一行代码。跨领域验证很糟糕
我对formBuilder的问题是赋值语句。对于一个包含12个属性的模型,我写了24行,12行用于将值放入表单中,12行用于以一种类型不安全的方式再次取出它们。这是ngModel不需要的大量额外键入,而且它有点违反了DRY原则,因为我必须在Typescript中重复HTML中输入字段的列表和层次结构
最近我这样做:
<input type=text name=foo [(ngModel)]="myModel.myProperty" />
及
。。。。
这给了我最好的两个世界,简洁和控制
但是对于
我发现我仍然必须使用ControlValueAccessor
样板文件,这意味着我不能使用ngModel
在返回的小3属性对象和我的官方12属性模型真值源之间传递值。需要三个明确的赋值语句。我想避免这些,并避免更多角度特定的样板
如果选取器HTML中的FormControl
s可以使用选取器在HTML中看到ngForm,这将很容易,但它不能
我的问题是:FormControl
如何向NgForm
注册自身FormBuilder
不将NgForm
作为输入参数,它“只知道”要附加哪个表单。即使在同一个HTML模板中有多个表单,它也能正确处理。如果有一个隐藏在它后面的服务找到了NgForm,我可以从我的选择器中使用该服务来找到它自己模板之外的NgForm吗?FormControl
s从ngModel
/formControlName
构造函数接收NGFormForm
实例,该构造函数由Angular DI放置在那里。组件装饰器中的“样板文件”:
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateRangePickerComponent),
multi: true
}
…向DI系统注册自定义组件(正在实现ControlValueAccessor
)。具体而言,NG\u VALUE\u访问器
与PickerService
在这里的作用相同:
export class MyComponent {
constructor(pickerService: PickerService)
multi:true
部分意味着注入的东西不只是一个服务,就像PickerService一样,而是一组服务RadioControlValueAccessor
,SelectControlValueAccessor
,和CheckboxControlValueAccessor
坐在这把伞下,如果你使用“样板文件”,你自己的DateRangePicker
可能就是其中之一。Angular在查看HTML模板时为手头的工作选择了正确的一个
将组件包装在lambda forforwardRef
中只解决了一小部分初始化问题,仅此而已
基本上,实现ControlValueAccessor
可以生成Angular期望的类,并且装饰器指定Angular中的位置
但是如果你真的不想使用它…
在父HTML中的表单上使用模板引用变量,并将其传递给子组件,就像传递任何其他值一样:
<form #theForm="ngForm" ...
<date-range-picker [form]="theForm" ...
必须将一个添加到另一个
ngAfterViewInit() {
this.mod.control.setValidators([Validators.required, c => c.value.duration != 0]);
this.form.addControl(this.mod);
}
你基本上完成了。*ngIf
销毁和重新创建所述控件可能会有问题,或者更改检测不像通常那样彻底,但以这种方式解决这些问题意味着您正在有效地重新发明
当一个子模板中有多个ngModels时,这一点开始变得明显:
@ViewChildren(NgModel) ngModels: QueryList<NgModel>;
readonly validations = {
'reasonField': [Validators.required, Validators.maxLength(500)],
'durationField': [Validators.required, c => c.value.duration != 0],
};
ngAfterViewInit() {
this.ngModels.forEach(ngModel => {
ngModel.control.setValidators(this.validations[ngModel.name]);
this.form.addControl(ngModel);
});
}
@ViewChildren(NgModel)ngModels:QueryList;
只读验证={
'reasonField':[Validators.required,Validators.maxLength(500)],
'durationField':[Validators.required,c=>c.value.duration!=0],
};
ngAfterViewInit(){
这个.ngModels.forEach(ngModel=
@Input() form: NgForm;
@ViewChild(NgModel) mod: NgModel;
ngAfterViewInit() {
this.mod.control.setValidators([Validators.required, c => c.value.duration != 0]);
this.form.addControl(this.mod);
}
@ViewChildren(NgModel) ngModels: QueryList<NgModel>;
readonly validations = {
'reasonField': [Validators.required, Validators.maxLength(500)],
'durationField': [Validators.required, c => c.value.duration != 0],
};
ngAfterViewInit() {
this.ngModels.forEach(ngModel => {
ngModel.control.setValidators(this.validations[ngModel.name]);
this.form.addControl(ngModel);
});
}