ExpressionChangedAfterItHasBeenCheckedError角度

时间:2020-08-03 12:03:27

标签: angular typescript rxjs

我正在为我的项目集成一个加载指示器,该指示器的可见性将在HTTP Interceptor服务中集中确定。但是,实施后,出现以下错误:

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

下面是我删除的文件:

app.component.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>

spinner.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);
    }
}

interceptor.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);
            });

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

2 个答案:

答案 0 :(得分:2)

您的加载状态应初始化(默认为false)。

由于主题在初始化时不访问值,因此可以将其更改为 VehicleList = [ { parent: "audi", child: [{ type: 'G-audiA', subchild: [{ id: 1, name: 'type audi A1' }, { id: 2, name: 'type audi A2' }] }, { type: 'G-audiB', subchild: [{ id: 1, name: 'type audi B1' }, { id: 2, name: 'type audi B2' }] }] }, { parent: "bmw", child: [{ type: 'G-bmwA', subchild: [{ id: 1, name: 'type bmw A1' }, { id: 2, name: 'type bmw A2' }] }, { type: 'G-bmwB', subchild: [{ id: 1, name: 'type bmw B1' }, { id: 2, name: 'type bmw B2' }] }] }]

BehaviorSubject

答案 1 :(得分:1)

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

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

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

已更新,以在代码的第二行解压缩:

角度特征更改检测-它是由以下过程触发的过程:

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

然后在应用程序的组件树中运行,检查所有数据绑定(取决于CD策略等)。

您看到的错误是在相同的更改检测周期内发生的,当Angular检测到绑定值已更改后,检测到该绑定值已更改,从而导致Angular出现问题,因为它无法更新模板。

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

RXJS在后台的延迟使用setTimeout(),它如上所述触发了另一个CD周期。 主题是一个可以多播到多个观察者的Observable。因此,我们通过管道传递“延迟”功能,因此我们为该特定更改保留了CD周期。

因此,如果我们做书的人-这更像是一种可行的解决方法。但是以我们在此处运行的另一个CD周期为代价。