15

I have a parent component and i am passing some HTML from it to a child common component using @ViewChild().

When Child component loads up a popup. Console throws below error.

"ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: undefined'. Current value: 'ngIf: this is description'. It seems like the view has been created after its parent and its children have been dirty checked. Has it been created in a change detection hook ?"

I am using { NgbModal } from '@ng-bootstrap/ng-bootstrap';

This is the code.

Update - This parent component is called as app-parent-component in another parent html file.

Parent Component

@ViewChild('templateToLoad') templateToLoad;


constructor(private modalService: NgbModal, private ChangeDetector: ChangeDetectorRef) {
}

ngOnInit() {
    this.openPopup();
}

ngAfterViewInit() {
    this.ChangeDetector.detectChanges();
}

private openPopup() {
    const modalPrompt = this.modalService.open(CommonChildModalComponent, {windowClass: 'modal-prompt fade-in-down'});
    modalPrompt.componentInstance.title = 'Title';
    modalPrompt.componentInstance.contentTemplate = this.templateToLoad;
    modalPrompt.componentInstance.originContext = this;
    modalPrompt.componentInstance.description = 'ABC';

Parent HTML

<ng-template #templateToLoad>
  <div class="someClass">This data will be shown on Popup without any error.
  </div>
</ng-template>

CommonChildPopup Component

@Input() title?: string;
@Input() description?: string;
@Input() originContext: any;
@Input() contentTemplate?: any;

constructor(public activeModal: NgbActiveModal) {
}

ngOnInit() {
    console.log('I am here in CommonModalComponent ngOnInit');
}

CommonChildPopup HTML

<div class="modal-header">
  <h4 class="modal-title">{{title}}</h4>
</div>

<div class="modal-body pb-3" [class.dimmer]="simulateLoading">
  <p *ngIf="description">{{description}}</p>
  <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>

The above console error is for this line ngIf="description". If i remove this line, same error will come for next line. Please help.

Aakash Kumar
  • 893
  • 4
  • 15
  • 38
  • 1
    the error is due to value of the variable being changed after the angular has checked the content. So whatever logic you are implementing to change the values , do it inside ``ngAfterContentChecked()`` – CruelEngine Mar 15 '18 at 06:46
  • I am not changing any values or any logic here.. whatever values I am setting in parent component, those should be loaded but somehow it is calling chid component and seeing undefined and second time it finds correct values. – Aakash Kumar Mar 15 '18 at 06:47

3 Answers3

19

You're trying to update the property values in a lifecycle hook after they have been previously checked in the parent component.

The recommended solution is to open the modal on a button click / another user triggered event, or if you need to open it after the view is initialized you can use the setTimeout() that will skip a tick

ngAfterViewInit() { setTimeout(() => this.openPopup()); }

Working plunker : https://plnkr.co/edit/FVV7QVp620lIGJwEhN6V?p=preview

A very nice and detailed explanation about this error : https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

Lucian Moldovan
  • 567
  • 5
  • 6
12

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-untouched: true'. Current value: 'ng-untouched: false'.

setTimeout trick doesn't work for subscribing for reactive form either. The following workaround should work for me

html

<select [ngModel]="countryId"  #ngModelDir="ngModel"
      (ngModelChange)="fileChangeEvent($event, ngModelDir)">

ts

import { NgModel } from '@angular/forms';
...
fileChangeEvent(event: any, ngModelDir: NgModel) {
  ngModelDir.control.markAsTouched();
  const modalRef = this.modalService.open(MymodalComponent, {
    keyboard: false,
    backdrop: 'static'
  });
}
Arul
  • 1,420
  • 1
  • 15
  • 25
  • This works! But I want to know why? and whats the reason because the control is not touched.... – ismaestro Mar 04 '19 at 15:50
  • 1
    @Ismaestro When we open a modal window, the form element loses the focus. So need to mark the control in a form group as touched explicitly. – Arul Mar 05 '19 at 07:46
  • Still ! This code doesn't work in case of ng-select library for edit flow when you have multiple selected values coming in edit flow. – Aakash Kumar Mar 05 '19 at 10:21
8

In my case the error was happening in an Angular 4 application. The application needed to open an ng-bootrap modal when the user selected a value from a dropdown in a reactive form. Using the setTimeout workaround did not work. However, the solution described in the below link - i.e. setting the control to touched (by calling markAsTouched() on the control) resolved the error.

This issue is in Angular as described here.

Atif Majeed
  • 1,021
  • 17
  • 14
  • I've solved a very similar problem using this workaround in Angular 8.2.4. Thank you for saving my night. – sedran Oct 25 '21 at 20:16