【问题标题】:ngIf - Expression has changed after it was checkedngIf - 检查后表达式已更改
【发布时间】:2017-09-16 17:27:35
【问题描述】:

我有一个简单的场景,但无法正常工作!

在我看来,我在一个高度有限的框中显示了一些文本。

正在从服务器获取文本,因此当文本进入时视图会更新。

现在我有一个“展开”按钮,其中有一个ngIf,如果框中的文本溢出,应该显示该按钮。

问题是因为文本在获取时会发生变化,所以在 Angular 的更改检测完成后,“展开”按钮的条件变为 true...

所以我收到此错误:检查后表达式已更改。以前的值:“假”。当前值:'true'。

显然按钮没有显示...

查看此Plunker(检查控制台以查看错误...)

知道如何进行这项工作吗?

【问题讨论】:

标签: angular angular2-changedetection


【解决方案1】:

出现这个错误是因为你在dev mode:

dev mode 中,更改检测会在每次常规更改检测运行后添加一个额外的轮次,以检查模型是否已更改。

因此,要强制更改检测运行下一个刻度,我们可以执行以下操作:

export class App implements AfterViewChecked {

  show = false; // add one more property

  constructor(private cdRef : ChangeDetectorRef) { // add ChangeDetectorRef
    //...
  }
  //...
  ngAfterViewChecked() {
    let show = this.isShowExpand();
    if (show != this.show) { // check if it change, tell CD update view
      this.show = show;
      this.cdRef.detectChanges();
    }
  }

  isShowExpand()
  {
    //...
  }
}

现场演示:https://plnkr.co/edit/UDMNhnGt3Slg8g5yeSNO?p=preview

【讨论】:

  • 这对我有用。在我的 ngAfterViewChecked 方法中,我只有 this.cdRef.detectChanges();.
  • 与@Ziggler 相同,它也对我有用,ngAfterViewChecked 中只有this.cdRef.detectChanges();
【解决方案2】:

出于某种原因,@Tiep Phan 的回答对我强制更改检测不起作用,但使用 setTimeout(它也强制更改检测)可以。

我也只需将它添加到有问题的行,它可以与我在 ngOnInit 中已有的代码一起正常工作,而不必添加 ngAfterViewInit。

例子:

ngOnInit() {
    setTimeout(() => this.loadingService.loading = true);
    asyncFunctionCall().then(res => {
        this.loadingService.loading = false;
    })
}

更多详情在这里:https://github.com/angular/angular/issues/6005

【讨论】:

    【解决方案3】:

    在 ngAfterContentChecked 为我解决问题后导致更改检测器运行

    示例如下:

    import { ChangeDetectorRef,AfterContentChecked} from '@angular/core'
    export class example implements OnInit, AfterContentChecked {
        ngAfterContentChecked() : void {
            this.changeDetector.detectChanges();
        }
    }
    

    虽然,当我阅读一些文章时,这个问题在生产模式下得到解决,无需任何必要的修复。

    以下是此类问题的可能原因:

    它强制执行单向数据流:当我们的控制器类上的数据更新时,更改检测运行并更新视图。

    但是视图的更新本身并不会触发进一步的更改,而这些更改又会触发视图的进一步更新

    https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/

    【讨论】:

      【解决方案4】:

      实现AfterContentChecked 方法。

      constructor(
          private cdr: ChangeDetectorRef,
      ) {}
      
      ngAfterContentChecked(): void {
         this.cdr.detectChanges();
      }  
      

      【讨论】:

      • 这个是为我做的
      【解决方案5】:

      要克服这个问题,您可以将改变 *ngIf 状态的变量从 ngAfterViewInit 移动到 ngOnInitconstructor。因为这是不允许的在调用 AfterViewInit 方法时更改 html 的状态。

      正如@tiep-phan 所说,另一种方法是将 ChangeDetectorRef 传递给构造函数,并在 AfterViewInit 方法中更改 *ngIf 的状态后调用 this.chRef.detectChanges()。

      【讨论】:

      • 请注意:我正在调用异步函数来获取数据(来自 ngoninit),但没有等待所述函数
      【解决方案6】:

      对于任何在类似问题上苦苦挣扎的人,基本上你正在尝试在你不应该更新的地方更新 dom,在这个链接 https://blog.angular-university.io/angular-debugging/ 你可以找到有关如何调试的详细信息,找到确切的代码片段产生问题,以及如何解决它的一些想法

      【讨论】:

        【解决方案7】:

        我们还可以通过将 changeDetection 更改为 OnPush 来抑制抛出此 ExpressionChangedAfterItHasBeenCheckedError。因此,不会执行额外的更改检测,因此不会引发错误。 为此,您需要在 .ts 文件中添加 ChangeDetectionStrategy.OnPush 作为 @Component 装饰器的一部分,如下所示:

        @Component({
          selector: 'your-component',
          templateUrl: 'your-component.component.html',
          changeDetection: ChangeDetectionStrategy.OnPush
        })
        

        【讨论】:

        • 老兄......你救了我的一天!
        • @Cabuxa.Mapache 很高兴保存?
        【解决方案8】:

        如果您使用BehaviorSubject 在组件之间共享值:

        component.ts:

        import { Observable } from 'rxjs/Observable';
        import {tap, map, delay} from 'rxjs/operators';
        
        private _user = new BehaviorSubject<any>(null);
        user$ = this._user.asObservable();
        
        Observable.of('response').pipe(
                    tap(() =>this._user.next('yourValue'),
                    delay(0),
                    map(() => 'result')
                  );
        
        

        component.html:

        <login *ngIf="!(dataService.user$ | async); else mainComponent"></login>
        

        【讨论】:

          【解决方案9】:

          我收到此错误是因为我在加载时打开了一个 Ngbmodal 弹出窗口,其中包括在检查后更改的对象。我通过调用 setTimeout 中的模态打开函数解决了这个问题。

          setTimeout(() => {
            this.modalReference = this.modalService.open(this.modal, { size: "lg" });
          });
          

          【讨论】:

            【解决方案10】:

            Angular 变更检测过程是同步的,为避免此错误,我们可以将其设置为异步过程,并且我们可以通过执行类似的操作使该代码在变更检测之外运行。 我们可以把这个 sn-p 放在 onInit 或 Constructor 方法中。

            this.ngZone.runOutsideAngular(() => {    
                 this.interval = window.setInterval(() => {        
            
                     //logic which we want to handle
            
                 }, 1)});
            

            【讨论】:

              猜你喜欢
              • 2017-01-24
              • 1970-01-01
              • 2021-01-31
              • 1970-01-01
              • 1970-01-01
              • 2017-06-08
              • 2016-10-19
              • 2017-05-08
              • 1970-01-01
              相关资源
              最近更新 更多