【问题标题】:How do I know when custom form control is marked as pristine in Angular?我如何知道自定义表单控件何时在 Angular 中被标记为原始控件?
【发布时间】:2017-11-27 13:57:14
【问题描述】:

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

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

那么,我怎么知道我的控件何时调用了markAsPristine()?

ControlValueAccessor接口没有成员,和这个问题有关,我可以实现。

【问题讨论】:

  • 根据文档,statusChanges observable 在验证状态更改时发出,但我正在寻找脏/原始状态更改。
  • 你说得对。我读得太快了,我不明白你真正想要什么。好吧,AFAIK 你不知道何时调用 markAs-* 方法。
  • 谢谢。你知道访问FormControl 实例的方法吗?
  • 我已将这个子问题提取到另一个问题:stackoverflow.com/questions/44731894/…

标签: angular angular-forms


【解决方案1】:

经过彻底调查,我发现 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 ControlValueAccessor, OnInit {

  private control: AbstractControl;


  ngOnInit() {
    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 ControlValueAccessor, 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。

【讨论】:

  • control 是如何初始化的?
  • @TravisP 构造函数(@Optional() @Self() public ngControl: NgControl) { if (this.ngControl) { this.ngControl.valueAccessor = this; } }
  • 为什么是 const self = this;没用过?
【解决方案2】:

根据 Slava 的回答,另一个建议是替换 FormGroup 类中的 markAsDirty、markAsPristine 和 _updatePristine 方法:

ngOnInit(): void {
  const markAsDirty = this.formGroup.markAsDirty;
  this.formGroup.markAsDirty = (opts) => {
    markAsDirty.apply(this.formGroup, opts);
    console.log('>>>>> markAsDirty');
  };
  const markAsPristine = this.formGroup.markAsPristine;
  this.formGroup.markAsPristine = (opts) => {
    markAsPristine.apply(this.formGroup, opts);
    console.log('>>>>> markAsPristine');
  };
  const updatePristine = this.formGroup['_updatePristine'];
  this.formGroup['_updatePristine'] = (opts) => {
    updatePristine.apply(this.formGroup, opts);
    console.log('>>>>> updatePristine');
  };
}

我在console.log 位置发出事件,但当然也可以使用其他方法。

【讨论】:

    【解决方案3】:

    还有另一种方法可以检查表单是否脏。我们可以比较表单绑定到的对象。下面的函数可以用来比较对象属性

    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

    【讨论】:

      【解决方案4】:

      我的解决方法受到 Slava 的帖子和 Get access to FormControl from the custom form component in Angular 的启发,并将模板表单 (ngModel) 和响应式表单混合在一起。 组件内的复选框控件反映脏/原始状态并将其状态报告回外部以形成组。所以我可以将样式应用于基于类 ng-dirty、ng-valid 等的复选框输入 (&lt;label&gt;) 控件。 我还没有实现 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;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-30
        • 2022-07-29
        • 2017-07-02
        • 1970-01-01
        • 1970-01-01
        • 2018-11-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多