RangeError: Maximum call stack size exceeded when using valueChanges.subscribe

后端 未结 4 694
南方客
南方客 2021-02-02 08:30

I am using Angular 5 with Reactive forms and need to make use of the valueChanges in order to disable required validation dynamically

component class:

ex         


        
相关标签:
4条回答
  • 2021-02-02 09:10

    The problem is that you modify the value of the field inside of the valueChanges event handler for that same field, causing the event to be triggered again:

    this.userForm.get('loginTypeId').valueChanges.subscribe(
      (loginTypeId: string) => {
        ...
        this.userForm.get('loginTypeId').updateValueAndValidity(); <-- Triggers valueChanges!
    }
    
    0 讨论(0)
  • 2021-02-02 09:20

    If you want to subscribe to any form changes and still run patchValue inside it, then you could add the {emitEvent: false} option to patchValue, thus the patching will not trigger another change detection

    code:

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

    PS. This is also less tedious than subscribing to each form control one-by-one to avoid triggering change max call stack exceeded. Especially if you form has 100 controls to subscribe to.

    Now to elaborate further, if you still need to updateValueAndValidity inside the subscription, then I suggest you use the distinctUntilChanged rxjs operator, to only run the subscription, when some value changes.

    distinctUntilChanged documentation can be found here

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

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

    Now we will also have to make it a custom validation function, because by default, distinctUntilChanged validates objects by pointer and the pointer is new on every change.

    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();
        } );
    

    And voila, we are patching and updating, without running into the maximum call stack!

    0 讨论(0)
  • 2021-02-02 09:23

    Try adding distinctUntilChanged() in the pipeline just before subscribe(). It should filter out those "change" events where value was not actually changed.

    0 讨论(0)
  • 2021-02-02 09:25

    My answer is just development of this one.

    By adding distinctUntilChanged() in the pipeline just before subscribe() you avoid the "Maximum call stack size exceeded" because

    distinctUntilChanged method only emit when the current value is different than the last.

    The usage:

    this.userForm.get('password')
      .valueChanges.pipe(distinctUntilChanged())         
      .subscribe(val => {})
    

    Documentation

    0 讨论(0)
提交回复
热议问题