[英]Custom ControlValueAccessor in template-driven forms
我有一个自定义的<data-input-text>
组件,它有两种模式:常规和禁用。 这是模板(我为演示案例简化了一点):
<label *ngIf="!disabled"
class="field-label"
[ngClass]="{'focused' : isFocused, 'with-errors' : errors}">
<input class="field-value"
[type]="type"
[required]="required"
(focus)="onFocus()"
(blur)="onBlur()"
[(ngModel)]="value"
#fieldInput="ngModel">
</label>
<div class="field-label" *ngIf="disabled">
<span class="field-value">{{ value }}</span>
<span class="field-name">{{ label }}</span>
</div>
在父窗体中,我按以下方式使用此组件:
<form #profileForm="ngForm">
<data-text-input
label="lastName"
[required]="true"
[disabled]="userIsRegistered"
name="lastName"
ngModel></data-text-input>
</form>
userIsRegistered
返回一个布尔值,它应该在组件内的输入字段或跨度之间切换。 直到这里一切正常。
我在父组件中设置表单以匹配 BehaviorSubject,如下所示:
this._sub = this.dl.selectedEmployee.subscribe( u => {
if ( u.id ) {
this.isLoading = false;
setTimeout( () => {
this.profileForm.setValue(u);
this.profileForm.control.markAsPristine();
}, 10);
}
});
这是自定义 ControlValueAccessor组件:
import { Component, Input, ViewChild, forwardRef,
AfterViewInit, OnInit, OnChanges,
NgModule } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS,
ControlValueAccessor, FormControl,
Validator, NgForm } from '@angular/forms';
@Component({
selector: 'data-text-input',
template: `
<label *ngIf="!disabled"
class="field-label">
<input class="field-value"
[type]="type"
[required]="required"
(blur)="onBlur()"
[(ngModel)]="value"
#fieldValue="ngModel">
<span class="field-name">{{ label }}</span>
</label>
<div class="field-label" *ngIf="disabled">
<span class="field-value">{{ value }}</span>
<span class="field-name">{{ label }}</span>
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef( ()=> DataTextInputComponent ),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef( ()=> DataTextInputComponent ),
multi: true
}
]
})
export class DataTextInputComponent implements OnChanges, ControlValueAccessor, Validator {
@Input() public disabled: boolean = false;
@Input() public label: string;
@Input() public required: boolean = false;
@Input() public type: string = 'text';
@ViewChild('fieldValue') public fieldValue: FormControl;
// infrastructure
public registerOnChange(fn: any) { this.propagateChange = fn; }
public registerOnTouched(fn: any) { this.propagateTouch = fn; }
private propagateChange = (_: any) => { };
private propagateTouch = (_: any) => { };
/**
* inner value
*/
private innerValue: any = null;
/**
* on changes hook
*/
public ngOnChanges(): void {
if ( this.disabled ) {
this.propagateChange(this.innerValue);
}
}
/**
* input events
*/
public onBlur(): void {
this.propagateChange(this.innerValue);
this.propagateTouch(true);
}
/**
* value accessor setter and getter
*/
public get value(): any {
return this.innerValue;
};
public set value(value: any) {
if ( value !== 'undefined' ) {
this.innerValue = value;
this.propagateChange(value);
this.propagateTouch(true);
}
}
/**
* value accessor implementation
* @param value
*/
public writeValue(value: any): void {
if (value !== this.innerValue) {
this.innerValue = value;
}
}
/**
* validation
* @param c
*/
public validate(c: FormControl) {
return this.errors = (this.disabled) ? null : this.customValidate(c);
}
private customValidate(c: FormControl): {} {
if ( c.touched ) {
// some validation logic which is not relevant here;
return null;
}
return null;
}
}
表单中还使用了其他组件(如颜色选择器和 ng-select)。
所以奇怪的部分是这个。 表单值设置好了。 没有错误。 这些值在数据文本输入组件以及表单中的其他组件中正确显示(对于disabled
和!disabled
)。 奇怪的是,当我用调试器检查this.profileForm
对象时, controls
属性具有所有具有各自值的控件,但表单的value
属性错过了那些,其中disabled
属性(又名无输入字段)是设置为真。
这是 Plunker: https ://plnkr.co/edit/nbWQZzQjhGae622CanGa ? p = preview
有任何想法吗?
嗯,这并不明显,直到我追踪到AbstractControl.prototype.updateValueAndValidity
设置值的方式,结果证明,使用disabled
变量名在这里是一个坏主意:
<form #profileForm="ngForm">
<data-text-input
label="lastName"
[required]="true"
[disabled]="userIsRegistered"
name="lastName"
ngModel></data-text-input>
</form>
我已将disabled
属性重命名为isReadOnly
因为readonly
也是一个可以在其他地方检查的属性,也是一个 TypeScript 接口——而且,tada,它可以工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.