我的Angular应用程序中有几个自定义表单控件组件,它们实现了ControlValueAccessor
接口,效果很好。
但是,当在父表单上调用markAsPristine()
时,或者直接在我的自定义控件上调用时,我需要更新它的状态:我的自定义控件实际上有内部控件,我需要在其上调用markAsPristine()
太
所以,我怎么知道我的控件何时调用markAsPristine()
?
ControlValueAccessor
接口没有与此问题相关的成员,我可以实现。
答案 0 :(得分:7)
经过彻底调查后,我发现Angular并未特别提供此功能。我在官方存储库中posted an issue就此而言,它获得了功能请求状态。我希望它能在不久的将来实施。
在此之前,这里有两种可能的解决方法:
markAsPristine()
@Component({
selector: 'my-custom-form-component',
templateUrl: './custom-form-component.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: MyCustomFormComponent,
multi: true
}]
})
export class MyCustomFormComponent implements NG_VALUE_ACCESSOR, OnInit {
private control: AbstractControl;
ngOnInit () {
const self = this;
const origFunc = this.control.markAsPristine;
this.control.markAsPristine = function () {
origFunc.apply(this, arguments);
console.log('Marked as pristine!');
}
}
}
ngDoCheck
请注意,此解决方案性能较差,但它可以提供更好的灵活性,因为您可以监控原始状态何时发生变化。在上述解决方案中,只有在调用markAsPristine()
时才会通知您。
@Component({
selector: 'my-custom-form-component',
templateUrl: './custom-form-component.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: MyCustomFormComponent,
multi: true
}]
})
export class MyCustomFormComponent implements NG_VALUE_ACCESSOR, DoCheck {
private control: AbstractControl;
private pristine = true;
ngDoCheck (): void {
if (this.pristine !== this.control.pristine) {
this.pristine = this.control.pristine;
if (this.pristine) {
console.log('Marked as pristine!');
}
}
}
}
如果您需要访问组件中的FormControl
实例,请参阅此问题:Get access to FormControl from the custom form component in Angular。
答案 1 :(得分:0)
还有另一种检查表格是否脏的方法。我们可以比较绑定表单的对象。下面的函数可用于对象属性比较
isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
如果要在stackblitz链接下检查此用法。 我已经测试过,并且运作良好。 Stackblitz link
答案 2 :(得分:0)
我的解决方法的灵感来自 Slava 的帖子和 Get access to FormControl from the custom form component in Angular,并将模板表单 (ngModel) 和反应式表单混合在一起。
组件内部的复选框控件反映脏/原始状态并将其状态报告回外部以形成组。所以我可以根据类 ng-dirty、ng-valid 等将样式应用于复选框输入 (<label>
) 控件。
我还没有实现 markAsTouched、markAsUntouched,因为它可以用类似的方式完成。
StackBlitz
示例组件代码为:
import { AfterViewInit, Component, Input, OnInit, Optional, Self, ViewChild } from "@angular/core";
import { ControlValueAccessor, NgControl, NgModel } from "@angular/forms";
@Component({
selector: "app-custom-checkbox-control",
template: '<input id="checkBoxInput"\
#checkBoxNgModel="ngModel"\
type="checkbox"\
name="chkbxname"\
[ngModel]="isChecked"\
(ngModelChange)="checkboxChange($event)"\
>\
<label for="checkBoxInput">\
{{description}}\
</label>\
<div>checkbox dirty state: {{checkBoxNgModel.dirty}}</div>\
<div>checkbox pristine state: {{checkBoxNgModel.pristine}}</div>',
styleUrls: ["./custom-checkbox-control.component.css"]
})
export class CustomCheckboxControlComponent
implements OnInit, AfterViewInit, ControlValueAccessor {
disabled: boolean = false;
isChecked: boolean = false;
@Input() description: string;
@ViewChild('checkBoxNgModel') checkBoxChild: NgModel;
constructor(@Optional() @Self() public ngControl: NgControl) {
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
}
checkboxChange(chk: boolean) {
console.log("in checkbox component: Checkbox changing value to: ", chk);
this.isChecked = chk;
this.onChange(chk);
}
ngOnInit() {}
ngAfterViewInit(): void {
debugger
this.checkBoxChild.control.setValidators(this.ngControl.control.validator);
const origFuncDirty = this.ngControl.control.markAsDirty;
this.ngControl.control.markAsDirty = () => {
origFuncDirty.apply(this.ngControl.control, arguments);
this.checkBoxChild.control.markAsDirty();
console.log('in checkbox component: Checkbox marked as dirty!');
}
const origFuncPristine = this.ngControl.control.markAsPristine;
this.ngControl.control.markAsPristine = () => {
origFuncPristine.apply(this.ngControl.control, arguments);
this.checkBoxChild.control.markAsPristine();
console.log('in checkbox component: Checkbox marked as pristine!');
}
}
//ControlValueAccessor implementations
writeValue(check: boolean): void {
this.isChecked = check;
}
onChange = (val: any) => {};
onTouched = () => {};
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}