【发布时间】: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 && this.formControl.value!=value) this.formControl.setValue(value, { emitEvent: false });
标签: angular angular-material angular-reactive-forms