我如何知道自定义表单控件何时在Angular中标记为pristine?

时间:2017-06-23 22:24:50

标签: angular angular-forms

我的Angular应用程序中有几个自定义表单控件组件,它们实现了ControlValueAccessor接口,效果很好。

但是,当在父表单上调用markAsPristine()时,或者直接在我的自定义控件上调用时,我需要更新它的状态:我的自定义控件实际上有内部控件,我需要在其上调用markAsPristine()

所以,我怎么知道我的控件何时调用markAsPristine()

ControlValueAccessor接口没有与此问题相关的成员,我可以实现。

3 个答案:

答案 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;
  }
}