![](/img/trans.png)
[英]Bind template reference variables to ngModel while using reactive forms
[英]ngModel but using the reactive forms method of validation
我不喜歡Angular的形式驗證路線,但我正在研究一種方法來結合每種方法的最佳方式,並且接近我喜歡的答案。
我知道無論我是使用formBuilder
還是ngModel
路由,都有一個表單的NgForm
,它有一個屬性,包含根FormGroup
,它具有FormControl
對象的異構集合。 HTML Elements都有一個實現ControlValueAccessor
接口的適配器對象,我自己的角度組件如<date-range-picker>
可以實現相同的接口,並假裝只是另一個元素,其值是一個任意復雜的對象。 每個FormControl
包裝一個元素,通過ControlValueAccessor
接口與它進行對話,因此它ControlValueAccessor
它究竟與它實際交談的內容。
我知道在元素上放置ngModel
或formControl
指令將為該元素創建FormControl
實例; 即使<form>
標簽自動獲得NgForm
,元素也不會自動獲取。
我知道formBuilder
將顯式創建缺少HTML元素的空心FormControl
,但每個都有一個名稱,而在HTML中, formControlName
為HTML元素提供一個名稱但沒有FormControl
實例,並且基本上formControlName
和formBuilder
都與一個服務進行通信匹配名稱並填充空心FormControl
及其元素。
最后, FormControl
是驗證器所在的位置,以及臟/觸摸/等。 屬性。
我對ngModel
問題與每個人的問題相同:驗證很糟糕。 自定義驗證只不過是if
語句的條件,但是ngModel
希望我將整個指令中的那個小條件包裝起來並將其粘貼在元素的HTML中。 對於if
語句來說,這是一個額外的輸入 - 你不能使單行重用,因為它需要一行來使用包裝器。 並且跨領域驗證很糟糕。
我對formBuilder
問題是賦值語句。 對於12個屬性的模型,我寫了24行,12個將值放入表單中,12個以類型不安全的方式再次將它們取回。 這是ngModel不需要的大量額外輸入,並且它違反了DRY原則,因為我必須在Typescript中的HTML中重復輸入字段的列表和層次結構。
我最近這樣做:
<input type=text name=foo [(ngModel)]="myModel.myProperty" />
同
@ViewChild(NgModel) mod: NgModel;
ngAfterViewInit() {
this.mod.control.setValidators([Validators.required, Validators.minLength(3)]);
}
和
<span class=danger *ngIf="mod?.control?.errors?.required">....
這給了我兩全其美,簡潔和控制。
但對於<date-range-picker>
我發現我仍然需要使用ControlValueAccessor
樣板,這意味着我不能使用ngModel
在它返回的小3屬性對象和我的官方12屬性模型源之間ngModel
值。真相。 需要三個顯式賦值語句。 我想避免這些,並避免更多角度特定的樣板。
如果選擇器HTML中的FormControl
可以使用選擇器在HTML中看到ngForm,那將很容易,但它不能。
我的問題是: FormControl
如何使用NgForm
注冊自己? FormBuilder
不會將NgForm
作為輸入參數,它只知道要附加哪種形式。 即使在同一個HTML模板中有多個表單,它也是正確的。 如果有一個服務隱藏在它后面並找到NgForm,我可以使用我的選擇器中的那個服務來找到一個超出它自己的模板的NgForm嗎?
FormControl
從ngModel
/ formControlName
構造函數接收NgForm
實例,該構造函數由Angular DI放置在那里。 組件裝飾器中的這個“樣板”:
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateRangePickerComponent),
multi: true
}
...使用DI系統注冊自定義組件(正在實施ControlValueAccessor
)。 具體來說, NG_VALUE_ACCESSOR
扮演的角色與PickerService
在此處扮演的角色相同:
export class MyComponent {
constructor(pickerService: PickerService)
multi: true
部分意味着注入的東西不僅僅是一個服務,就像PickerService一樣,但實際上是一組服務。 RadioControlValueAccessor
, SelectControlValueAccessor
和CheckboxControlValueAccessor
位於此保護傘下,如果您使用“樣板”,您自己的DateRangePicker
可能就在其中。 在查看HTML模板時,Angular會為當前作業選擇正確的一個。
將一個組件包裝在lambda中用於forwardRef
只是解決了一個小的初始化順序問題,僅此而已。
基本上,實現ControlValueAccessor
使一類什么角度預計,和裝飾指定了在角把它。
但如果你真的不想用它......
在父HTML的表單上使用模板引用var,並將其傳遞給子組件,就像它是任何其他值一樣:
<form #theForm="ngForm" ...
<date-range-picker [form]="theForm" ...
在子組件中,像任何其他輸入一樣接受表單,並且還獲取對子HTML(您已經為驗證器目的而做)中使用的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);
}
你基本上完成了。 *ngIf
可能存在破壞和重新創建所述控件的問題,或者更改檢測不像通常那樣徹底,但以這種方式解決這些問題意味着您正在有效地重新發明Angular。
當您在子模板中有多個ngModel時,這開始變得明顯:
@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);
});
}
..現在將驗證綁定到字段開始看起來很像formBuilder
。 (沒有24個賦值語句。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.