angular - RangeError : Maximum call stack size exceeded when using valueChanges. 订阅

标签 angular typescript angular5 angular-reactive-forms

我将 Angular 5 与 Reactive 表单一起使用,需要使用 valueChanges 来动态禁用所需的验证

组件类:

export class UserEditor implements OnInit {

    public userForm: FormGroup;
    userName: FormControl;
    firstName: FormControl;
    lastName: FormControl;
    email: FormControl;
    loginTypeId: FormControl;
    password: FormControl;
    confirmPassword: FormControl;
...

ngOnInit() {
    this.createFormControls();
    this.createForm();
    this.userForm.get('loginTypeId').valueChanges.subscribe(

            (loginTypeId: string) => {
                console.log("log this!");
                if (loginTypeId === "1") {
                    console.log("disable validators");
                    Validators.pattern('^[0-9]{5}(?:-[0-9]{4})?$')]);
                    this.userForm.get('password').setValidators([]);
                    this.userForm.get('confirmPassword').setValidators([]);

                } else if (loginTypeId === '2') {
                    console.log("enable validators");
                    this.userForm.get('password').setValidators([Validators.required, Validators.minLength(8)]);
                    this.userForm.get('confirmPassword').setValidators([Validators.required, Validators.minLength(8)]);

                }

                this.userForm.get('loginTypeId').updateValueAndValidity();

            }

        )
}
createFormControls() {
    this.userName = new FormControl('', [
        Validators.required,
        Validators.minLength(4)
    ]);
    this.firstName = new FormControl('', Validators.required);
    this.lastName = new FormControl('', Validators.required);
    this.email = new FormControl('', [
      Validators.required,
      Validators.pattern("[^ @]*@[^ @]*")
    ]);
    this.password = new FormControl('', [
       Validators.required,
       Validators.minLength(8)
    ]);
    this.confirmPassword = new FormControl('', [
        Validators.required,
        Validators.minLength(8)
    ]);

}

createForm() {
 this.userForm = new FormGroup({
      userName: this.userName,
      name: new FormGroup({
        firstName: this.firstName,
        lastName: this.lastName,
      }),
      email: this.email,
      loginTypeId: this.loginTypeId,
      password: this.password,
      confirmPassword: this.confirmPassword
    });
}

但是,当我运行它时,出现浏览器 javascript 错误

UserEditor.html:82 ERROR RangeError: Maximum call stack size exceeded
    at SafeSubscriber.tryCatcher (tryCatch.js:9)
    at SafeSubscriber.webpackJsonp.../../../../rxjs/_esm5/Subscription.js.Subscription.unsubscribe (Subscription.js:68)
    at SafeSubscriber.webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.Subscriber.unsubscribe (Subscriber.js:124)
    at SafeSubscriber.webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:242)
    at SafeSubscriber.webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.SafeSubscriber.next (Subscriber.js:186)
    at Subscriber.webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.Subscriber._next (Subscriber.js:127)
    at Subscriber.webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.Subscriber.next (Subscriber.js:91)
    at EventEmitter.webpackJsonp.../../../../rxjs/_esm5/Subject.js.Subject.next (Subject.js:56)
    at EventEmitter.webpackJsonp.../../../core/esm5/core.js.EventEmitter.emit (core.js:4319)
    at FormControl.webpackJsonp.../../../forms/esm5/forms.js.AbstractControl.updateValueAndValidity (forms.js:3377)

“记录这个!”被重复调用,就像它被递归调用一样,这就是为什么它们是堆栈错误

如果我删除 valueChanges.subscribe 代码,除了有条件地删除验证之外。

为什么递归调用 valueChanges.subscribe?

最佳答案

如果您想订阅任何表单更改并仍然在其中运行 patchValue,那么您可以将 {emitEvent: false} 选项添加到 patchValue,这样补丁将不会触发另一个更改检测

代码:

this.formGroup
    .valueChanges
    .subscribe( _ => {
        this.formGroup.get( 'controlName' ).patchValue( _val, {emitEvent: false} );
    } );

附言。这也比一个一个地订阅每个表单控件以避免触发更改最大调用堆栈超出的繁琐。尤其是当您的表单有 100 个要订阅的控件时。

现在进一步详细说明,如果您仍然需要在订阅内更新 ValueAndValidity,那么我建议您使用 distinctUntilChanged rxjs 运算符,以便仅在某些值更改时运行订阅。

distinctUntilChanged 文档可以在这里找到

https://www.learnrxjs.io/operators/filtering/distinctuntilchanged.html

distinctUntilChanged - Only emit when the current value is different than the last.

现在我们还必须将其设为自定义验证函数,因为默认情况下,distinctUntilChanged 通过指针验证对象,并且指针在每次更改时都是新的。

this.formGroup
    .valueChanges
    .distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
    .subscribe( _ => {
        this.formGroup.get( 'controlName' ).patchValue( _val, {emitEvent: false} );
        this.formGroup.get( 'controlName' ).updateValueAndValidity();
    } );

瞧,我们正在修补和更新,没有遇到最大调用堆栈!

关于angular - RangeError : Maximum call stack size exceeded when using valueChanges. 订阅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47821809/

相关文章:

node.js - 升级后 eslint 说 NodeJS 未定义

javascript - 如何使用 Aurelia 通过单击内部 anchor 链接来保持在同一页面上?

html - Angular Material 动画 - matRipple : escapes bounds of a div element

angular - Ng4LoadingSpinner 5 秒后超时

java - 在 spring 数据中使用什么代替 fetch.EAGER ?

angular - 在 ng-bootstrap Angular 2 typescript 中的选项卡之间切换时如何保留以前的 ngbTab 内容

javascript - 如何在 Protractor 测试中检查应用程序 URL 的状态代码?

javascript - CORS 错误 : “requests are only supported for protocol schemes: http…” etc

javascript - Angular2 CLI 中出现意外标记 <

javascript - 组合/合并可观察对象