Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
603 views
in Technique[技术] by (71.8m points)

typescript - Angular 4 ExpressionChangedAfterItHasBeenCheckedError

in ParentComponent =>

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''. Current value: '[object Object]'.
            at viewDebugError (vendor.bundle.js:8962)
            at expressionChangedAfterItHasBeenCheckedError (vendor.bundle.js:8940)

Parent component Html

<div>
  <app-child-widget [allItems]="allItems" (notify)="eventCalled($event)"></app-child-widget>
<div>

Parent component

export class ParentComponent implements OnInit {

  returnedItems: Array<any> = [];
  allItems: Array<any> = [];

  constructor(
  ) { }

  ngOnInit() {
     this.allItems = // load from server...
  }

  eventCalled(items: Array<any>): void {
    this.returnedItems = items;
  }
}

Child component

@Component({
  selector: 'app-child-widget',
  templateUrl: 'child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
  @Output() notify: EventEmitter<any> = new EventEmitter();
  @Input() private allItems: Array<any>;

  constructor() { }

  ngOnInit() {
    doSomething();
  }

  doSomething() {
    this.notify.emit(allItems);
  }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The article Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error explains this behavior in great details

Cause

Your problem is very similar to this one but instead of updating parent property through a service you're updating it through synchronous event broadcasting. Here is the quote from the linked answer:

During digest cycle Angular performs certain operations on child directives. One of such operations is updating inputs and calling ngOnInit lifecycle hook on child directives/components. What's important is that these operations are performed in strict order:

  • Update inputs
  • Call ngOnInit

So in your case Angular updated input binding allItems on child component, then called onInit on child component which caused an update to allItems of parent component. Now you have data inconsistency. Parent component has one value while the child another. If Angular continues synchronizing changes you'll get an infinite loop. That's why during next change detection cycle Angular detected that allItems was changed and thrown an error.

Solution

It seems that this is an application design flaw as you're updating details from both parent and child component. If it's not, then you can solve the problem by emitting the event asynchronously like this:

export class ChildComponent implements OnInit {
  @Output() notify: EventEmitter<any> = new EventEmitter(true);
                                                        ^^^^^^-------------

But you have to be very careful. If you use any other hook like ngAfterViewChecked that is being called on every digest cycle, you'll end up in cyclic dependency!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...