我有一个简单的场景,但是无法让它正常工作!
在我看来,我在一个高度有限的方框中显示一些文字。
正在从服务器获取文本,因此当文本进入时视图会更新。
现在我有一个“展开”按钮,其中ngIf
应该显示该按钮,如果框中的文字溢出。
问题在于,因为文本在获取时会发生变化,所以在Angular的变化检测完成后,“展开”按钮的条件会变为true
...
所以我收到了这个错误:表达式在检查后发生了变化。上一个值:'false'。当前价值:'true'。
显然按钮没有显示......
请参阅此Plunker (查看控制台以查看错误...)
知道如何使这项工作吗?
答案 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()
{
//...
}
}
答案 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
在组件之间共享值:
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')
);
<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;
})
}
答案 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();
}