繁体   English   中英

ExpressionChangedAfterItHasBeenCheckedError Angular

[英]ExpressionChangedAfterItHasBeenCheckedError Angular

我正在为我的项目集成一个加载指示器,其可见性将在 HTTP 拦截器服务中集中确定。 但是,在实施之后,我收到以下错误:

 ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'null'. Current value: 'true'.

以下是我精简的文件:

应用程序组件.ts

import { SpinnerService } from '@app/services/spinner.service';
import { Subject } from 'rxjs';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnDestroy {
    title = 'My App';
    isLoading: Subject<boolean> = this.spinnerService.isLoading;

    constructor(public spinnerService: SpinnerService) {
    }

}

app.component.html

<div class="example-container" [class.example-is-mobile]="mobileQuery.matches">
    <router-outlet></router-outlet>
    <div *ngIf="isLoading | async" class="overlay">
        Loading
    </div>
</div>

旋转器.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable()

export class SpinnerService {

    isLoading = new Subject<boolean>();

    constructor() {
    }

    show() {
        this.isLoading.next(true);
    }

    hide() {
        this.isLoading.next(false);
    }
}

拦截器.service.ts

import { Injectable } from '@angular/core';
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse} from '@angular/common/http';
import {EMPTY, Observable, throwError} from 'rxjs';

import { environment } from '@env/environment';
import { finalize } from 'rxjs/operators';
import { SpinnerService } from '@app/services/spinner.service';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {

    // snackBar: MatSnackBar;

    constructor(public spinnerService: SpinnerService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.spinnerService.show();

        return next.handle(request).pipe(
            finalize(() => this.spinnerService.hide()),
        );
    }
}

最后示例用法在我的其他组件中如下:

        this.http.get(`${environment.apiUrl}/form/schema/days`)
            .subscribe(data => {
                console.log('done', data);
            });

我不知道我在这里做错了什么,有人可以进一步说明吗?

您的加载 state 应该被初始化(默认为 false)。

由于 Subject 在初始化时不访问值,您可以将其更改为BehaviorSubject

isLoading = new BehaviorSubject<boolean>(false);

理想情况下,您应该在 NgOnInit 挂钩内调用数据源,这可以防止出现此问题。 解决此问题的一种方法是使用 RXJS 延迟运算符使加载器值更改发生在当前组件初始化周期之外:

isLoadingSubject = = new Subject<boolean>();
isLoading: Observable<boolean> = this.isLoadingSubject.pipe( delay(0) );

我认为这是相关阅读: https://blog.angular-university.io/angular-debugging/

更新为在代码的第二行解压一下:

Angular 具有变化检测功能——这是一个由以下因素触发的过程:

  • 输入事件(点击、鼠标悬停、键盘弹起等)
  • Ajax 请求
  • setTimeout() 和 setInterval()

然后,它会遍历应用程序的组件树,检查所有数据绑定(受 CD 策略等约束)。

您看到的错误发生在相同的更改检测周期内 Angular 检测到绑定值在对该绑定执行更改检查后已经更改,因此导致 Angular 出现问题,因为它无法相应地更新模板。

因此,要解决这样的问题,我们可以确保更改在特定 CD 周期开始之前发生(有时我们可以通过将数据更改操作移动到不同的组件生命周期挂钩来实现)或延迟它 - 确保更改将在下一个周期。

RXJS 的底层延迟使用 setTimeout() ,如上所述,它会触发自己的另一个 CD 周期。 Subject 是一个 Observable,可以多播到多个 Observer。 所以我们 pipe 它具有“延迟”功能,因此我们为该特定更改保留了 CD 周期。

因此,如果我们学究气——这更像是一种可行的解决方法。 但是以我们在这里运行的另一个 CD 周期为代价。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM