【问题标题】:Angular: Maximum call stack size exceeded with multiple valueChangesAngular:多个 valueChanges 超出了最大调用堆栈大小
【发布时间】:2019-11-13 02:05:35
【问题描述】:

注意 - 我在末尾添加了一个 stackblitz 链接。

我的代码存在问题,如果当前填充了另一个表单控件,我希望基本上禁用一个表单控件。例如,如果字段 A 已填充,则输入字段 B 和 C 将被禁用。如果字段 A 被清除,则字段 B 和 C 将重新启用。这发生在所有三个字段中,按其各自的顺序表示,如果您填充 B,则 A 和 C 将被禁用,等等。

this.SearchForm.get('a').valueChanges
    .subscribe( ( value ) => {
      if ( value ) {
        this.SearchForm.get('b').disable();
        this.SearchForm.get('c').disable();
      } else {
        this.SearchForm.get('b').enable();
        this.SearchForm.get('c').enable();
      }
    });

如果我只在一个字段上设置它,那么效果很好。但是,如果我将其更改为包括设置其他字段,如下所示:

this.SearchForm.get('a').valueChanges
    .subscribe( ( value ) => {
      if ( value ) {
        this.SearchForm.get('b').disable();
        this.SearchForm.get('c').disable();
      } else {
        this.SearchForm.get('b').enable();
        this.SearchForm.get('c').enable();
      }
    });

    this.SearchForm.get('b').valueChanges
    .subscribe( ( value ) => {
      if ( value ) {
        this.SearchForm.get('a').disable();
        this.SearchForm.get('c').disable();
      } else {
        this.SearchForm.get('a').enable();
        this.SearchForm.get('c').enable();
      }
    });

    this.SearchForm.get('c').valueChanges
    .subscribe( ( value ) => {
      if ( value ) {
        this.SearchForm.get('a').disable();
        this.SearchForm.get('b').disable();
      } else {
        this.SearchForm.get('a').enable();
        this.SearchForm.get('b').enable();
      }
    });

我收到“超出最大调用堆栈大小”错误。也许有更好的方法可以使用反应形式来做到这一点,但我还没有遇到任何事情。我采用这种方法的部分原因是,一旦该字段为空,它将用更少的代码轻松地重新启用其他字段。

Link of Stackblitz 如果你注释掉 b 和 c 的 valueChanges 并且只有一个,你可以看到我正在寻找的那种行为,但是如果你添加一个额外的 valuechanges 就会得到错误。

【问题讨论】:

  • 可能是因为启用/禁用其他 formControl 会触发其更改监听器

标签: angular angular-reactive-forms


【解决方案1】:

disable() / enable() 方法有一个 emitEvent 选项:

disable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
enable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

https://angular.io/api/forms/AbstractControl

尝试传入{ emitEvent: false }

const disableEnableOptions = {emitEvent: false };
this.SearchForm.get('a').valueChanges
    .subscribe( ( value ) => {
      if ( value ) {
        this.SearchForm.get('b').disable(disableEnableOptions);
        this.SearchForm.get('c').disable(disableEnableOptions);
      } else {
        this.SearchForm.get('b').enable(disableEnableOptions);
        this.SearchForm.get('c').enable(disableEnableOptions);
      }
    });

这应该会阻止您的更改触发您的其他订阅者。您应该能够在其余代码中实现这一点。

这是我的 stackblitz 分支:https://stackblitz.com/edit/angular-w5r29c

【讨论】:

  • 感谢您的帮助。我从文档中看到它说以下内容: emitEvent: 当 true 或未提供(默认值)时,当控件被禁用时,statusChanges 和 valueChanges 可观察对象都会发出具有最新状态和值的事件。当为 false 时,不会发出任何事件。我不太清楚这意味着什么以及为什么会解决这个问题?
  • 当您调用 disable 或 enable() 时,框架会向 statusChanges 和 valueChanges 可观察对象发出事件。当您将 emitEvent 设置为 false 时,它​​会阻止这种情况发生。它解决了您的问题,因为您订阅了“a”、“b”和“c”的 valueChanges。
【解决方案2】:

当您调用值更改“a”时,您会调用另一个值更改,依此类推,直到达到调用堆栈限制。

基本上完整的表单被映射到模型上,因此每当您启用/禁用任何控件时,表单模型的属性都会更改。并且因为模型在变化所以会触发 valueChanges 事件。

如果你使用新版本的angular,你可能可以使用

this.form.get('b').setValue(newValue, {onlySelf: true, emitEvent: false});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-07
    • 2016-09-26
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    相关资源
    最近更新 更多