角度4 - “在检查之后表达已经改变”。使用NG-IF时出错 [英] Angular 4 - "Expression has changed after it was checked" error while using NG-IF

查看:101
本文介绍了角度4 - “在检查之后表达已经改变”。使用NG-IF时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我设置了服务来跟踪已登录的用户。该服务返回一个Observable,所有 subscribe 的组件都会被通知(到目前为止只有一个组件订阅它)

I setup a service to keep track of logged in users. That service returns an Observable and all components that subscribe to it are notified (so far only a single component subscribe to it).

服务:

private subject = new Subject<any>();

sendMessage(message: boolean) {
   this.subject.next( message );
}

getMessage(): Observable<any> {
   return this.subject.asObservable();
} 

Root App组件:(此组件订阅可观察的)

Root App Component: (this component subscribes to the observable)

ngAfterViewInit(){
   this.subscription = this._authService.getMessage().subscribe(message => { this.user = message; });
}

欢迎组件:

ngOnInit() {
  const checkStatus = this._authService.checkUserStatus();
  this._authService.sendMessage(checkStatus);
}

App Component Html:(这是发生错误)

<div *ngIf="user"><div>






我正在尝试做什么:

我希望每个组件(除了根应用程序组件之外的)将用户登录状态发送到根应用程序组件,以便我可以在Root App Component Html中操作UI。

I want every component (except the Root App Component) to send the users logged-in state to the Root App Component so I can manipulate the UI within the Root App Component Html.

问题:

初始化欢迎组件时出现以下错误。

I get the following error when the Welcome Component is initialised.

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

请注意,此 * ngIf =user发生此错误表达式,位于Root App Components HTML文件中。

Please note this error occurs on this *ngIf="user" expression which is located within Root App Components HTML file.

有人可以解释这个错误的原因以及我如何解决这个问题吗?

旁注:如果您认为有更好的方法来实现我想要做的事情,那么请告诉我。

On a side note: If you think theres a better way to achieve what I'm trying to do then please let me know.

更新1:

将以下内容放在构造函数中解决了这个问题,但又不想使用 constructor 为此目的所以它似乎不是一个好的解决方案。

Putting the following in the constructor solves the issue but don't want to use the constructor for this purpose so it seems it's not a good solution.

欢迎组件:

constructor(private _authService: AuthenticationService) {
  const checkStatus = this._authService.checkUserStatus();
  this._authService.sendMessage(checkStatus);
 }

Root App组件:

constructor(private _authService: AuthenticationService){
   this.subscription = this._authService.getMessage().subscribe(message => { this.usr = message; });
}

更新2:

这是 plunkr 。要查看错误,请检查浏览器控制台。应用程序加载时,应显示布尔值true,但我在控制台中收到错误。

Here's the plunkr. To see the error check the browser console. When the app loads a boolean value of true should be displayed but I get the error in the console.

请注意,此plunkr是一个非常基本的版本我的主应用程序。由于应用程序有点大,我无法上传所有代码。但是plunkr完美地证明了这个错误。

推荐答案

这意味着变化检测周期本身似乎有引起了变化,这可能是偶然的(即变化检测周期以某种方式引起的)或有意的。如果您故意在更改检测周期中更改某些内容,则应重新触发新一轮的更改检测,这在此处不会发生。此错误将在prod模式下被抑制,但意味着您的代码中存在问题并导致神秘问题。

What this means is that the change detection cycle itself seems to have caused a change, which may have been accidental (ie the change detection cycle caused it somehow) or intentional. If you do change something in a change detection cycle on purpose, then this should retrigger a new round of change detection, which is not happening here. This error will be suppressed in prod mode, but means you have issues in your code and cause mysterious issues.

在这种情况下,具体问题是你正在改变孩子的变化检测周期中影响父母的事情,这也不会重新触发父母的变化检测虽然像observables这样的异步触发器通常会这样做。它不会重新触发父级循环的原因是因为这违反了单向数据流,并且可能会导致子项重新触发父级更改检测周期,然后重新触发子级,然后重新触发父级,依此类推,并导致应用中的无限变化检测循环。

In this case, the specific issue is that you're changing something in a child's change detection cycle which affects the parent, and this will not retrigger the parent's change detection even though asynchronous triggers like observables usually do. The reason it doesn't retrigger the parent's cycle is becasue this violates unidirectional data flow, and could create a situation where a child retriggers a parent change detection cycle, which then retriggers the child, and then the parent again and so on, and causes an infinite change detection loop in your app.

听起来好像我说孩子不能向父母组件发送消息,但事实并非如此,问题是孩子可以在更改检测周期(例如生命周期挂钩)期间向父节点发送消息,它需要在外面发生,如响应用户事件。

It might sound like I'm saying that a child can't send messages to a parent component, but this is not the case, the issue is that a child can't send a message to a parent during a change detection cycle (such as life cycle hooks), it needs to happen outside, as in in response to a user event.

这里最好的解决方案是通过创建一个新组件来阻止违反单向数据流,该新组件不是导致更新的组件的父组件,因此无法创建无限更改检测循环。这在下面的plunkr中得到了证明。

The best solution here is to stop violating unidirectional data flow by creating a new component that is not a parent of the component causing the update so that an infinite change detection loop cannot be created. This is demonstrated in the plunkr below.

添加了子项的新app.component:

New app.component with child added:

<div class="col-sm-8 col-sm-offset-2">
      <app-message></app-message>
      <router-outlet></router-outlet>
</div>

消息组件:

@Component({
  moduleId: module.id,
  selector: 'app-message',
  templateUrl: 'message.component.html'
})
export class MessageComponent implements OnInit {
   message$: Observable<any>;
   constructor(private messageService: MessageService) {

   }

   ngOnInit(){
      this.message$ = this.messageService.message$;
   }
}

模板:

<div *ngIf="message$ | async as message" class="alert alert-success">{{message}}</div>

略微修改的消息服务(只是稍微清洁的结构):

slightly modified message service (just a slightly cleaner structure):

@Injectable()
export class MessageService {
    private subject = new Subject<any>();
    message$: Observable<any> = this.subject.asObservable();

    sendMessage(message: string) {
       console.log('send message');
        this.subject.next(message);
    }

    clearMessage() {
       this.subject.next();
    }
}

这比让更改检测正常工作更有好处没有创建无限循环的风险。它还使您的代码更加模块化并更好地隔离责任。

This has more benefits than just letting change detection work properly with no risk of creating infinite loops. It also makes your code more modular and isolates responsibility better.

https://plnkr.co/edit/4Th7m0Liovfgd1Z3ECWh?p=preview

这篇关于角度4 - “在检查之后表达已经改变”。使用NG-IF时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆