【问题标题】:Getting "RangeError: Maximum call stack size exceeded" when doing form.reset() with Angular custom text field使用 Angular 自定义文本字段执行 form.reset() 时出现“RangeError:超出最大调用堆栈大小”
【发布时间】:2021-06-21 20:56:24
【问题描述】:

我使用 Angular Material 创建了一个自定义文本字段。一切似乎都正常工作,除非我尝试重置 FormGroup 我得到RangeError: Maximum call stack size exceeded

我使用这个 StackOverflow 答案设置了自定义文本字段:https://stackoverflow.com/a/60071669/2026659

这是我的自定义文本字段组件打字稿:

import { Component, Input, Optional, Self, AfterViewInit } from '@angular/core';
import { ControlValueAccessor, NgControl, FormControl } from '@angular/forms';
import { of } from 'rxjs';
import { skipWhile, take } from 'rxjs/operators';

export class RequiredFormControl extends FormControl {
  required = true;
}
@Component({
  selector: 'input-text-field',
  templateUrl: './input-text-field.component.html',
  styleUrls: ['./input-text-field.component.scss']
})
export class InputTextFieldComponent implements AfterViewInit, ControlValueAccessor {
  public _formControl = new FormControl(); 
  public onChange = (value: any) => {};

  @Input() public fieldId: string;
  @Input() public isRequired: boolean;
  @Input() public fieldClass: string;
  @Input() public labelText: string;
  @Input() public tabNumber: number;

  constructor(@Self() @Optional() public ngControl: NgControl) {
    if(this.ngControl) {
      this.ngControl.valueAccessor = this;
    }
  }
  
  ngAfterViewInit(): void {
    if (this.ngControl) {
      of(this.ngControl.control)
        .pipe(
          skipWhile(fc => !fc),
          take(1)
        )
        .subscribe(fc => {
          this.formControl = fc as FormControl;
        });
    }
  }

  get formControl() :FormControl|RequiredFormControl {
    return this._formControl;
  }
  set formControl(forControl:FormControl|RequiredFormControl)  {
    this._formControl = forControl;
  }

  registerOnChange(fn: (value: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: (value: any) => void): void {}

  writeValue(value: any): void {
    if(this.formControl) this.formControl.setValue(value, { emitEvent: false });
  }

}

这是我的自定义文本字段 html:

<mat-form-field>
  <mat-label><span *ngIf="isRequired" class="input-required">*</span> {{labelText}}</mat-label>
  <input type="text"
    matInput
    [formControl]="formControl"
    [id]="fieldId" 
    [class]="fieldClass" 
    [tabIndex]="tabNumber"
>
</mat-form-field>

我在父组件中创建了关联的 FormGroup 和 FormControl:

const userProfile = new FormGroup({
      firstName: new FormControl(null, Validators.required),
      lastName: new FormControl(null, Validators.required),
      ....
});

我在父组件中有一个按钮,它触发了一个重置​​表单的功能:

public resetFormEventClick() {
  this.userProfile.reset();
}

我查看了堆栈跟踪,发现当代码到达我的自定义文本字段组件中的writeValue 函数时发生错误。

我尝试向此Stackblitz 添加表单重置功能并得到相同的 RangeError。我试图查看错误是否是我的代码所特有的。

似乎错误是当重置函数尝试更改自定义文本字段的值时。与该值相关的所有函数都将其更改回 null。我不明白为什么它会引发与调用堆栈相关的错误。任何帮助,将不胜感激。我一直在努力让这个自定义组件完全工作。

更新: 我尝试在 writeValue 函数中使用 patchValue 而不是 setValue,如下所示:this.formControl.patchValue(value, {onlySelf: true, emitEvent: false}); 但仍然得到相同的 RangeError。

我在 writeValue 函数中添加了一个 console.log,当触发 this.userProfile.reset() 时,console.log 消息不断重复,然后发生 RangeError。似乎有一个无限循环,代码不断触发 writeValue 函数。

【问题讨论】:

  • 你在说 - 在函数 ngAfterViewInit- 中,“formControl”inside 你的组件是 ngControl outside 组件 - 我忽略这样做的原因是什么,但这就是你得到递归错误的原因。我想这段代码是关于一个没有实现 ControlValueAccessor 但我不确定的组件
  • 哦,有趣。我采用了这种方法,因为我在创建自定义控件时尝试使用的另一种方法,实现 MatFormFieldControl,使反应式表单验证不起作用。我需要将表单控件绑定到 NgControl,这似乎是最有效的方式。
  • 我看到代码来自stackoverflow.com/questions/60062444/…。也许你可以解决简单的 if in write 值:if(this.formControl &amp;&amp; this.formControl.value!=value) this.formControl.setValue(value, { emitEvent: false });

标签: angular angular-material angular-reactive-forms


【解决方案1】:

感谢@Eliseo 的建议,我得以解决错误。

我把 writeValue 函数改成这样:

writeValue(value: any): void {
    if(this.formControl && this.formControl.value!=value) this.formControl.setValue(value, { emitEvent: false });
  }

更新 if 语句停止了无限循环。

【讨论】:

    猜你喜欢
    • 2021-03-07
    • 1970-01-01
    • 2018-01-24
    • 2018-02-06
    • 1970-01-01
    • 2021-05-30
    • 2021-07-11
    相关资源
    最近更新 更多