ngIf - 检查后表达式已更改

时间:2017-04-20 07:53:00

标签: angular angular2-changedetection

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

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

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

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

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

所以我收到了这个错误:表达式在检查后发生了变化。上一个值:'false'。当前价值:'true'。

显然按钮没有显示......

请参阅此Plunker (查看控制台以查看错误...)

知道如何使这项工作吗?

10 个答案:

答案 0 :(得分:25)

发生此错误是因为您在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

答案 1 :(得分:1)

在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/

答案 2 :(得分:1)

如果您使用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>

答案 3 :(得分:1)

要解决此问题,您可以将* ngIf状态更改的变量从 ngAfterViewInit 更改为 ngOnInit 构造函数

@ tiep-phan告诉我们,另一种方法是,每当更改* ngIf的状态时,将ChangeDetectorRef传递给构造函数并调用detectChanges()。

答案 4 :(得分:1)

对于任何在相似方面苦苦挣扎的人,基本上您都在尝试在不应该使用的地方更新dom,在此链接https://blog.angular-university.io/angular-debugging/中,您可以找到有关如何调试的详细信息,并找到确切的代码段产生了该问题,以及一些解决方法的想法

答案 5 :(得分:1)

我们还可以通过将 changeDetection 更改为 OnPush 来抑制抛出此 ExpressionChangedAfterItHasBeenCheckedError。因此,不会执行额外的更改检测,因此不会引发错误。 为此,您需要在 .ts 文件中添加 PHP version: 5.6.40 PHP CLI: /usr/local/bin/php PHP CGI: Not Installed (php-cgi sapi is necessary to use built-in web server) Loaded Configuration File: Not Found Additional .ini files parsed: /usr/local/etc/php/conf.d/docker-php-ext-calendar.ini /usr/local/etc/php/conf.d/docker-php-ext-exif.ini /usr/local/etc/php/conf.d/docker-php-ext-gettext.ini /usr/local/etc/php/conf.d/docker-php-ext-mysql.ini /usr/local/etc/php/conf.d/docker-php-ext-mysqli.ini /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini /usr/local/etc/php/conf.d/docker-php-ext-pdo_mysql.ini /usr/local/etc/php/conf.d/docker-php-ext-shmop.ini /usr/local/etc/php/conf.d/docker-php-ext-sockets.ini /usr/local/etc/php/conf.d/docker-php-ext-sysvmsg.ini Debugger extension: Not Found Loaded extensions: calendar, Core, ctype, curl, date, dom, ereg, exif, fileinfo, filter, ftp, gettext, hash, iconv, json, libxml, mbstring, mhash, mysql, mysqli, mysqlnd, openssl, pcre, PDO, pdo_mysql, pdo_sqlite, Phar, posix, readline, Reflection, session, shmop, SimpleXML, sockets, SPL, sqlite3, standard, sysvmsg, tokenizer, xml, xmlreader, xmlwriter, Zend OPcache, zlib 作为 ChangeDetectionStrategy.OnPush 装饰器的一部分,如下所示:

@Component

答案 6 :(得分:0)

由于某种原因,@ 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

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

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

this.ngZone.runOutsideAngular(() => {    
     this.interval = window.setInterval(() => {        

         //logic which we want to handle

     }, 1)});

答案 9 :(得分:0)

实现 AfterContentChecked 方法。

constructor(
    private cdr: ChangeDetectorRef,
) {}

ngAfterContentChecked(): void {
   this.cdr.detectChanges();
}