【问题标题】:Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'检查后表达式已更改。以前的值:'ng-valid: true'。当前值:'ng-valid: false'
【发布时间】:2019-05-22 21:45:07
【问题描述】:

我在父组件和子组件内的部分中有角度反应形式。

在子组件中,我有一个复选框 - 当它被选中时 - 打开更多字段,我希望它们都是必需的。

我正在使用 setValidators,但出现错误

ParentFormComponent.html:3 ERROR 错误: ExpressionChangedAfterItHasBeenCheckedError:表达式已更改 检查后。以前的值:'ng-valid: true'。当前值: 'ng-有效:假'。 在 viewDebugError (core.js:7601) 在表达式ChangedAfterItHasBeenCheckedError (core.js:7589) 在 checkBindingNoChanges (core.js:7691) 在 checkNoChangesNodeInline (core.js:10560) 在 checkNoChangesNode (core.js:10541) 在 debugCheckNoChangesNode (core.js:11144) 在 debugCheckRenderNodeFn (core.js:11098) 在 Object.eval [作为 updateRenderer] (ParentFormComponent.html:3) 在 Object.debugUpdateRenderer [作为 updateRenderer] (core.js:11087) 在 checkNoChangesView (core.js:10442)

ParentFormComponent.html:3 错误上下文 DebugContext_ {view: Object, nodeIndex: 2, nodeDef: Object, elDef: Object, elView: Object}

这是 ParentFormComponent.html:3 的那一行

 <form [formGroup]="parentForm" (ngSubmit)="submitForm()">

这是我的代码:

<label class="container">DB
    <input #db type="checkbox" name="db" (change)="checkValue(db.name, db.checked)"> 
    <span class="checkmark"></span>
</label>

<div *ngIf="db.checked" formGroupName="_monitorDB">
    <mat-form-field>
        <input matInput placeholder="Server name" formControlName="ServerName">
    </mat-form-field>

    <mat-form-field>
        <input matInput placeholder="DataBase name" formControlName="DbName">
    </mat-form-field>

    <mat-form-field>
        <input matInput placeholder="table name" formControlName="DB_tableName">
    </mat-form-field>

    <mat-form-field>
        <input matInput placeholder="port" formControlName="DbPort">
    </mat-form-field>

    <mat-form-field>
        <input matInput placeholder="Query" formControlName="Query">
    </mat-form-field>

    <mat-form-field>
        <input matInput placeholder="Permissions" formControlName="Premissions">
    </mat-form-field>

</div>

在ts文件中:

checkValue(name:string, event: any){
    if (event == true){
      this.test.push(name);
      if (name =="db"){
         this.childForm.get('_monitorDB').get('ServerName').setValidators([Validators.required]);
         this.childForm.get('_monitorDB').get('DbName').setValidators([Validators.required]);
         this.childForm.get('_monitorDB').get('DB_tableName').setValidators([Validators.required]);
         this.childForm.get('_monitorDB').get('DbPort').setValidators([Validators.required]);
         this.childForm.get('_monitorDB').get('Query').setValidators([Validators.required]);
         this.childForm.get('_monitorDB').get('Premissions').setValidators([Validators.required]);

      }

    }

    else{
      const index: number = this.test.indexOf(name);
      if (index !== -1) {
          this.test.splice(index, 1);
          if (name =="db"){
            this.childForm.get('_monitorDB').get('ServerName').clearValidators();
            this.childForm.get('_monitorDB').get('ServerName').updateValueAndValidity();
            this.childForm.get('_monitorDB').get('DbName').clearValidators();
            this.childForm.get('_monitorDB').get('DbName').updateValueAndValidity();
            this.childForm.get('_monitorDB').get('DB_tableName').clearValidators();
            this.childForm.get('_monitorDB').get('DB_tableName').updateValueAndValidity();
            this.childForm.get('_monitorDB').get('DbPort').clearValidators();
            this.childForm.get('_monitorDB').get('DbPort').updateValueAndValidity();
            this.childForm.get('_monitorDB').get('Query').clearValidators();
            this.childForm.get('_monitorDB').get('Query').updateValueAndValidity();
            this.childForm.get('_monitorDB').get('Premissions').clearValidators();
            this.childForm.get('_monitorDB').get('Premissions').updateValueAndValidity();
          }
      }      
    }

     this.checkboxArr.emit(this.test);
 }

【问题讨论】:

  • 你能在 stackblitz 上提供最少的可重现代码吗?

标签: angular validation angular6 angular-reactive-forms


【解决方案1】:

我倾向于从不使用setTimeout,因为我认为这是一种不好的做法,但我最近发现在创建自定义组件和与 DOM 交互时,有时这是必要的。

但是,当我使用它时,它的延迟为0ms。只需将动作包装在setTimeout 中,无延迟就足以将动作移动到执行队列的末尾,这将解决 Angular 和 DOM 之间交互的许多问题。

TL,DR

setTimeout(() => actionThatCausesTheError(), 0);

【讨论】:

    【解决方案2】:

    跟踪字段更改的位置并标记组件以进行 changeDetection...它可能位于函数中或诸如 AfterViewChecked 之类的 Angular 生命周期钩子之一中

    constructor(
        private _cdr: ChangeDetectorRef
    ) {
    }
    
    // mark for check where change is happening
    this._cdr.markForCheck();
    this._cdr.detectChanges();
    

    将 changeDetection 改为 ChangeDetectionStrategy.OnPush 也可以解决问题,但可能会导致其他问题,您需要手动标记该组件以进行检查。

    【讨论】:

      【解决方案3】:

      将父组件上的 ChangeDetectionStrategy 更改为:changeDetection: ChangeDetectionStrategy.OnPush。简单干净。

      编辑这实际上不是一个解决方案,它只会隐藏错误。请查看 cmets 中发布的 github 链接。

      【讨论】:

      • 你能详细说明一下吗?为什么要改变检测策略?
      • 根据你分享的github线程,这个“解决方案”实际上并不能解决错误,只是隐藏它并且可以破坏很多东西。因此,在使用它之前,请确保 ChangeDetectionStrategy.OnPush 对您意味着什么
      • 当时,它让我克服了那个错误,所以我发布了解决方案。但我已经编辑了我的解决方案。您必须调试此错误。
      【解决方案4】:

      我遇到了同样的问题,我使用 AfterViewCheckedChangeDetectorRef 修复了它:

      import { AfterViewChecked, ChangeDetectorRef } from '@angular/core'
      
      export class ClassName implements AfterViewChecked {
        constructor(private readonly changeDetectorRef: ChangeDetectorRef) {}
      
        ngAfterViewChecked(): void {
          this.changeDetectorRef.detectChanges();
        }
      }
      
      

      【讨论】:

      • 有效,谢谢!如果能解释一下这个修复程序的作用,那就太好了。 ngAfterVIewChecked 每次 Angular 完成对组件及其子组件的更改检测时都会调用。很棒的介绍:indepth.dev/posts/1058/…
      • 很棒的解决方案。像魅力一样为我工作。谢谢
      【解决方案5】:

      添加changeDetection: ChangeDetectionStrategy.OnPush 应该可以解决问题

      @Component({
        selector: 'app-test-center-location-step3',
        templateUrl: './test-center-location-step3.component.html',
        styleUrls: ['./test-center-location-step3.component.scss'],
        changeDetection: ChangeDetectionStrategy.OnPush
      })
      

      【讨论】:

        【解决方案6】:

        试试这个:

        ngOnInit():void
        {
           this._monitorDB.patchValue({
            ServerName: 'ServerName',
            DbName: 'DbName',
            DB_tableName: 'DB_tableName',
            DbPort: 'DbPort',
            Query: 'Query',
            Premissions:'Premissions'
           });
        }
        

        【讨论】:

          【解决方案7】:

          我面临同样的问题,我知道这不是一种方法,但我找到的唯一解决方案是 setTimeout

          ngAfterViewInit(): void {
           setTimeout(() => {
              this.form.patchValue({
              name: 'xyz',
              email: 'abc@gmail.com',
              phone: '0987654321'
             })
            }, );
          }
          

          【讨论】:

            【解决方案8】:

            我不知道这样做是否正确,但我通过更改解决了它:

            <div *ngIf="db.checked" formGroupName="_monitorDB">
            

            <div [hidden]="!db.checked" formGroupName="_monitorDB">
            

            【讨论】:

            • 这不是正确的做法,因为您的元素在表单中可用,如果您的表单很大,则会产生性能问题。如果我提供的答案对你没有帮助,那么请告诉我,我会给你一个没有 rxweb 方法的答案。
            • 这对我有用,当我使用复选框执行此过程时,我试图显示或隐藏表单的一部分。
            猜你喜欢
            • 2021-01-31
            • 1970-01-01
            • 1970-01-01
            • 2020-06-18
            • 1970-01-01
            • 2018-01-11
            • 1970-01-01
            • 2019-02-03
            • 2017-07-09
            相关资源
            最近更新 更多