Angular 2 Popup Modal Issue: "Expression modified after checking"

See the problem in action on YouTube

Check out the GitHub repository for the demo app

My simple app consists of an app component, a child component (account), and an alert service that handles a message dialog component (popup modal).

To demonstrate the issue, I have two identical forms—one inside the app.component.ts and one inside account.component.ts. Each form has a button that triggers the alert service to display a message dialog modal.

The problem arises when I click on the input field of the form in the child component (account.component.ts) and then "press enter on my keyboard". This action results in the following error:

EXCEPTION: Error in ./AccountComponent class AccountComponent - inline template:2:2 caused by: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'. Please note that this error does not occur in any other scenario mentioned below.

  1. If I click the button instead of pressing enter on the keyboard

  2. The form in app.component.ts does not seem to have any issues even when I press enter. The error only occurs in the child component (account.component.ts).

  3. If I click the input for account.component, enter something, click the button, no error is shown. Then if I delete the input, press enter again, no error is shown, contrary to before.

I have searched on Stack Overflow and Google, and it appears that others are facing similar issues which they resolved by calling change detection. However, I tried implementing this solution like triggering change detect just after the modal is displayed, but it did not work. Besides, the fix does not explain why the form in app.component.ts does not trigger this error.

Below are snippets of the code. You can find the complete demo project on the GitHub link above. This problem has been bothering me for days, so any help would be greatly appreciated.

app.component.html

<label>This form is from app.component.html</label>
    <form name="form" [formGroup]="changePasswordForm" (ngSubmit)="onUpdatePassword()">
        <input placeholder="Old Password" formControlName="oldPassword">
        <button class="btn btn-success">Update Password</button>
    </form>
    
    <br><br><br><br>
    
    <label>This form is from account.component.html</label>
    <router-outlet> </router-outlet>
    
    <template ngbModalContainer></template>
    

app.component.ts

export class AppComponent implements OnInit {
    
        private changePasswordForm: FormGroup;
    
        constructor(
          private formBuilder: FormBuilder,
          private alertService: AlertService,
        ) { }
    
        ngOnInit() {
          this.changePasswordForm = this.formBuilder.group({
            oldPassword: [''],
          })
        }
    
        onUpdatePassword() {
          this.alertService.alertPopup('test2', 'asfafa')
        }
    }
    

account.component.html

<form name="form" [formGroup]="changePasswordForm" (ngSubmit)="onUpdatePassword()">
      <input placeholder="Old Password" formControlName="oldPassword">
      <button class="btn btn-success">Update Password</button>
    </form>
    

account.component.ts

export class AccountComponent implements OnInit {
    
      private changePasswordForm: FormGroup;
    
      constructor(
        private formBuilder: FormBuilder,
        private alertService: AlertService,
      ) { }
    
      ngOnInit() {
        this.changePasswordForm = this.formBuilder.group({
          oldPassword: [''],
        })
      }
    
      onUpdatePassword() {
        this.alertService.alertPopup('test2', 'asfafa')
      }
    }
    

alert.service.ts

@Injectable()
    export class AlertService {
        private subject = new Subject<any>();
        private keepAfterNavigationChange = false;
    
        constructor(
            private router: Router,
            private modalService: NgbModal,
        ) { }
    
    
        alertPopup(title: string, content: string) {
            // open modal to check if worked overnight
            const modalRef = this.modalService.open(MessageDialogComponent);
    
            modalRef.componentInstance.titleText = title
            modalRef.componentInstance.bodyText = content
    
            modalRef.result
                .then(response => {
                })
                .catch(() => {
                    return
                })
        }
    }
    

message-dialog.component.html

<div class="modal-header">
      <h4 class="modal-title">{{titleText}}</h4>
    </div>
    
    <div class="modal-body">
      <p>{{bodyText}}</p>
    </div>
    

message-dialog.component.ts

export class MessageDialogComponent implements OnInit {
    
      @Input() titleText: string;
      @Input() bodyText: string;
    
      constructor(
        public activeModal: NgbActiveModal,
      ) { }
    
      ngOnInit() {
      }
    }
    

https://i.stack.imgur.com/9kHgG.png

Answer №1

It appears that the error is occurring after running the following code:

ngAfterViewInit() {
    if (!this._elRef.nativeElement.contains(document.activeElement)) {
      this._renderer.invokeElementMethod(this._elRef.nativeElement, 'focus', []);
    }
}

https://github.com/ng-bootstrap/ng-bootstrap/blob/1.0.0-alpha.20/src/modal/modal-window.ts#L65

When an input event triggers a blur event, it marks your control as touched.

In the case of AccountComponent, the detection changes occur before ngbModalContainer, while the FormGroup within app.component.html receives the correct values.

Possible solutions:

1) Mark your controls as touched before opening the modal

account.component.ts

onUpdatePassword() {
  Object.keys(this.changePasswordForm.controls).forEach(key => {
     this.changePasswordForm.controls[key].markAsTouched();
  });

  this.alertService.alertPopup('test2', 'asfafa')
}

2) Change the order of tags

app.component.html

<template ngbModalContainer></template>
<router-outlet> </router-outlet>

Answer №2

I encountered a similar error before,

Try using ngAfterViewInit() in your appComponent instead of ngOnInit.

If that doesn't work, you can also attempt

setTimeOut(function(){
//Your Code
},1)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Discovering Child Elements in Angular 2 with @ViewChild and CSS Selectors

I'm looking to update the style of the second paragraph using either the nth-child() selector or by a specific class: import { Component, ViewChild } from '@angular/core'; @Component({ selector: 'my-app', template: ` <d ...

What are some strategies for accessing the original values of form components that have not been altered when using ngModel?

I am currently developing a form and I intend to utilize the previous values as "value" in the form. By employing ngModel dynamically, I am able to change some properties. However, I have encountered an issue where the field that remains unchanged by the u ...

Error message: The function URL.createObjectURL is not recognized in this context | Issue with Antd charts

Currently, I am working on integrating charts from antd into my TypeScript application. Everything runs smoothly on localhost, but as soon as I push it to GitHub, one of the tests fails: FAIL src/App.test.tsx ● Test suite failed to run TypeError: ...

The cloud function that is callable is currently inactive and encountering errors upon invocation

I am experiencing issues with my Cloud Function which is supposed to call a request to the URL passed to it. This is my first time using TypeScript to make a request, so I added some print calls to troubleshoot the problem. However, the first log never app ...

When running the command `npm start`, an error message is generated

Hey everyone, I've been trying to learn some basic AngularJS 2.0 skills through a tutorial. Unfortunately, when I tried running the command npm run start, it didn't work as expected. I'm currently using Git Bash on Windows 10 OS. If you hav ...

The type 'unknown' cannot be assigned to type 'KeyboardEvent'. Error in file 'ts' (2345)

Currently delving into TypeScript and Angular, I encountered an issue in my textbook with an example that refuses to compile. I am unsure of how to resolve this problem. Below is the malfunctioning function: ngOnInit(): void { const logger = fromEvent ...

What is the best way to convert a JSON string received from Angular into a Java Object within a Spring

I am currently utilizing WebSocket to create a chat application. Below is the code from my Angular application that sends a MessageModel object to the backend after converting it into a JSON string: sendMessage(message: MessageModel){ let data = JSON.str ...

From HTML to Mat Table: Transforming tables for Angular

I am currently facing a challenge with my HTML table, as it is being populated row by row from local storage using a for loop. I am seeking assistance in converting this into an Angular Material table. Despite trying various suggestions and codes recommend ...

The declaration file for the 'react' module could not be located

I was exploring Microsoft's guide on TypeScript combined with React and Redux. After executing the command: npm install -S redux react-redux @types/react-redux I encountered an error when running npm run start: Type error: Could not find a decla ...

Checkbox selections persist when navigating between pages

I am currently working with Angular 9 and I have a list of checkboxes that need to default to true when displaying certain data. If one of these checkboxes is unchecked, it should trigger the display of specific information. The issue I am facing is that o ...

Versions of Angular that are compatible with Ionic 2 (do not have an exported member)

How do I determine the compatible Angular version for each Ionic version? I keep encountering errors like "has no exported member." For example: The module ".../node_modules/@angular/core/index" does not have an exported member called InjectionToken. The ...

Issue encountered while submitting form data to API endpoint using Angular framework

Having trouble posting form data to a web api using a service that consistently returns a 404 bad request error. The service method looks like this: postIncidents(): Observable<any> { return this.http.post<any>(this.serviceApiUrl, {}) ...

Encountering an uncaughtException: Error stating that the module '....nextserverapphomelibworker.js' cannot be located while attempting to utilize pino.transport in Next.js

I recently set up a Next.js project with typescript using create-next-app. Opting for Pino as the logging library, recommended by Next.js, seemed like the logical choice. Initially, when I utilized Pino without incorporating its transport functionality, e ...

Stylishly incorporating components in higher-order components

Trying to enhance my component wrapper with styles using a higher order component has led to Typescript flagging an error with ComponentWithAdddedColors. type Props = { bg?: string; }; function withColors<TProps>( Component: React.ComponentType ...

Warning from Cytoscape.js: "The use of `label` for setting the width of a node is no longer supported. Please update your style settings for the node width." This message appears when attempting to create

I'm currently utilizing Cytoscape.js for rendering a dagre layout graph. When it comes to styling the node, I am using the property width: label in the code snippet below: const cy = cytoscape({ container: document.getElementById('cyGraph&apo ...

Bring JSON into Angular 7 application

For my Angular project, I've set up a localization service by importing JSON files. Following this recommended method, I updated my typings.d.ts as shown below: declare module "*.json" { const value: any; export default value; } Everything w ...

Methods to close the currently active ngx-modal when a new modal is triggered within the same Angular 8 component

I am currently working on developing a versatile modal component that has the ability to be called from within the same modal itself. Is there any way to configure the component and modal in such a manner that when the reusable component is triggered, it ...

Looking to retrieve HTML elements based on their inner text with queryselectors?

I am looking to extract all the HTML divs that contain specific HTML elements with innerText = ' * ' and save them in an array using Typescript. If I come across a span element with the innerText= ' * ', I want to add the parent div to ...

Discovering the Cookie in Angular 2 after it's Been Created

My setup includes two Components and one Service: Components: 1: LoginComponent 2: HeaderComponent (Shared) Service: 1: authentication.service Within the LoginComponent, I utilize the authentication.service for authentication. Upon successful authent ...

The button event listener in React fails to trigger without a page refresh

Within my index.html file, I have included the following code snippet: <head> ... <script type="text/javascript" src="https://mysrc.com/something.js&collectorId=f8n0soi9" </script> <script ...