在 ngOnInit 中添加验证器时的 ExpressionChangedAfterItHasBeenCheckedError

Posted

技术标签:

【中文标题】在 ngOnInit 中添加验证器时的 ExpressionChangedAfterItHasBeenCheckedError【英文标题】:ExpressionChangedAfterItHasBeenCheckedError when add validator in ngOnInit 【发布时间】:2018-02-16 11:32:42 【问题描述】:

我正在尝试将自定义验证器添加到输入,但是当我这样做时会触发 ExpressionChangedAfterItHasBeenCheckedError 错误,说明某些内容从 TRUE 更改为 FALSE。

我将问题追溯到下面一行:

ngOnInit(): void 
    this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()]));

如果我删除 TabValidator.ipaddress(),那么错误就会消失。同样,如果我的验证器强制返回“null”,那么错误就会消失。我的验证器如下:

private static _ipaddress(address: string): any 
    console.log('checking ip address: '+address+' valid: '+ip.isV4Format(address)+' is null: '+(address === null ? 'yes' : 'no'));
    if (!ip.isV4Format(address))  return  'wrongFormat': true ; 
    console.log('returning null');
    return null;

public static ipaddress(): ValidatorFn 
    return (control: AbstractControl):  [key: string]: any  => 
        console.log('validator returning: '+TabValidator._ipaddress(control.value));
        return TabValidator._ipaddress(control.value);
    ;

从控制台日志看来,验证器每次都返回相同的值。

有人可以解释这里出了什么问题吗?我无法解释什么变量从“真”变为“假”,因为上面的行中没有涉及布尔值。验证器如何返回不同的值而不出现在我的控制台日志中?

我读过一个关于不在 ngOnInit 中做事的 SO 问题,但这似乎是一个红鲱鱼。


更新:我试过了:

ngOnInit(): void 
    Promise.resolve().then(() => 
    this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()]));
    );

还有

ngOnInit(): void 
  setTimeout(() => this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()])), 10);

但随后错误变为:

错误错误:找不到名称为“网关”的控件


解决方案:

我将此追溯到一个错误 - 使用响应式表单 + 验证器 + ngb 选项卡。将验证器添加到选项卡上的控件会导致错误。唯一的解决方案是更改为模板表单。

【问题讨论】:

这主要发生在组件的渲染顺序不正确时。你生命周期中的某些东西出了问题。关于这个问题的 Angular github 问题页面上有很多线程。检查您的组件。验证您是否没有任何*ngIf 阻止某些组件的渲染等等......我多次遇到这个问题,总是因为我的代码/组件渲染顺序不正确。 我没有任何 ngIf 或 ngFor。我确实根据应用于输入的有效性类应用了不同的样式。 是的,正如我所说,*ngIf* 可能是一个问题,但有很多事情会导致该错误,正如我所说,主要是因为组件渲染的顺序。代码太少,无法告诉您问题出在哪里。如果您可以在 plunker 中复制(我现在推荐 stackblitz.com)会非常有帮助... 查看这篇文章Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error 【参考方案1】:

如果您查看 Angular 的 github 问题页面,您可以找到与您遇到的相同问题相关的几张票(例如 https://github.com/angular/angular/issues/15634 ,还有更多,我刚刚找到的这张)。长话短说:Angular 不喜欢某些(全部?)生命周期钩子中的视图更改。在您的特定情况下到底发生了什么变化:我假设这是 formField 的有效性状态(即在 Angular 的控制代码内部,您无权访问),内部触发更改检测...

如果您可以这样做,请将this.ipv4Fields.addControl() 移至构造函数。如果这是不可能的 - 请参阅 github 票证中的建议(做detectChanges,或Promise,或setTimeout,等等)...

更新:

@Component()
export class MyComponent implements OnInit
    
    @Input()
    ipv4Fields: FormGroup;
    
    //inject `ChangeDetecorRef`
    constructor(protected changeDetectorRef: ChangeDetectorRef)
    
    
    ngOnInit()
        this.ipv4Fields.addControl('gateway', new FormControl('', [TabValidator.ipaddress()]));
        this.changeDetectorRef.detectChanges();
    

【讨论】:

我尝试移至构造函数,但尚未创建字段,因此出错。我尝试使用 promise(请参阅上面的问题更新)但没有奏效。你能提供一个关于我的 ngOnInit 应该是什么样子的小代码示例吗? 就我个人而言,我已经厌倦了与那个错误作斗争,所以我每次遇到这个问题时,我都会做this.changeDetectorRef.detectChanges();。每个角度发布他们都会解决其中的一些问题,所以我在没有那部分的情况下重新测试我的代码。所以controller(protected changeDetectorRef: ChangeDetectorRef) ngOnInit()/*other code;*/ this.changeDetectorRef.detectChanges();。当我需要动态更新验证器时,我遇到了类似的问题。我的表格是模态的。因此,我没有在 ngOnInit 中设置验证器,而是将该代码移至 modalShow() 回调,这就足够了 %) 好的,我实现了上面的(我认为控制器应该是构造函数)但仍然得到 ExpressionChanged... 错误 对不起,我现在没有更好的解决方案:( 我很欣赏这些想法。这是否意味着我的问题与您提到的错误不同,或者我的问题只是同一错误的不同表现形式? (猜测)

以上是关于在 ngOnInit 中添加验证器时的 ExpressionChangedAfterItHasBeenCheckedError的主要内容,如果未能解决你的问题,请参考以下文章

添加重新激活验证器以使用选项卡中子组件中的输入形成

了解 ngOnInit

在 Spring Boot 应用程序中使用 @Valid 和 BindingResult 时的表单输入验证问题

使用带有 expressjs 的 multer 上传文件时的错误处理

在回发时,如何向验证摘要添加错误消息?

订阅角度时无法在 ngoninit 中获得价值