Dealing with Angular Material Stepper and utilizing Separate Components for individual steps, encountering the error of ExpressionChangedAfterItHasBeenChecked

I am currently working with a material stepper that contains forms within each step. I am facing an issue where each step should be controlled by the form associated with it.

Although similar questions have been asked on SO, the answers provided did not resolve my specific problem. Therefore, I am reaching out for assistance.

Parent HTML

<mat-horizontal-stepper linear #stepper>
    <mat-step [stepControl]="frmStepOne">
        <ng-template matStepLabel>Step One Details</ng-template>
        <app-first-step #stepOne></app-first-step>
    </mat-step>
    <mat-step [stepControl]="frmStepTwo">
        <ng-template matStepLabel>Step Two Details</ng-template>
        <app-second-step #stepTwo></app-second-step>
    </mat-step>
</mat-horizontal-stepper> 

Within my parent component, I have the following setup.

@ViewChild('stepOne') stepOneComponent: FirstStepComponent;
@ViewChild('stepTwo') stepTwoComponent: SecondStepComponent;


get frmStepOne() {
    return this.stepOneComponent ? this.stepOneComponent.frmStepOne : null;
}

get frmStepTwo() {
    return this.stepTwoComponent ? this.stepTwoComponent.frmStepTwo : null;
}

Child Class Component

frmStepOne: FormGroup;

constructor(private formBuilder: FormBuilder) {

    this.frmStepOne = this.formBuilder.group({
      name: ['', Validators.required]
    });
}

Child Class HTML

<mat-card>

  <form [formGroup]="frmStepOne">

    <mat-form-field>
      <input matInput formControlName="name" matInput placeholder="Name" required>
    </mat-form-field>

    <mat-card-actions>
      <button mat-raised-button matStepperNext class="nav-btn pull-right">Next</button>
    </mat-card-actions>

  </form>

</mat-card>

Upon running the app, the console displays the following error message.

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'stepControl: null'. Current value: 'stepControl: [object Object]'.

While suggestions from similar discussions on Stack Overflow advise moving form initialization to the constructor instead of ngOnInit, I have already made this adjustment but the error persists.

Angular Material Stepper Component For Each Step

If anyone has insights on resolving this issue, please share your recommendations.

Thank you.

Answer №1

Essentially, Angular detects the change after already setting the value as null for both values. To address this, you must explicitly instruct Angular to run change detection. Take a look at this demo code for reference.

I have eliminated the use of get logic because directly calling a function from HTML is considered bad practice and can lead to multiple function calls. You can try adding console logs in your code to see this in action.

To resolve this issue, I have implemented ngAfterViewInit along with ChangeDetectorRef.

export class AppComponent implements AfterViewInit {
  title = 'mat-stepper';

  form1: FormGroup;
  form2: FormGroup;
  @ViewChild('stepOne') stepOneComponent: FirstStepComponent;
  @ViewChild('stepTwo') stepTwoComponent: SecondStepComponent;

  constructor(private cdr :ChangeDetectorRef){}

  ngAfterViewInit(){
    this.form1 = this.stepOneComponent.frmStepOne;
    this.form2 = this.stepTwoComponent.frmStepTwo
    this.cdr.detectChanges();
  }

}

Following these steps will effectively solve the error

Answer №2

If you encounter an error, consider changing the troublesome variable to an Observable and applying the pipe with the delay(0) operator

Check out this link for a functional solution that eliminates errors without the need for change detection.

Here's an example:

Imagine using a variable named name of type string in your html template causing the aforementioned error.

<div>{{ name }}</div> // Error occurs here 

This error can happen when Angular checks a value that changed after the initial check.

To resolve this, convert name into an Observable like name$

name$: Observable<string>; 

private myName$ = new BehaviorSubject<string>("");
myNameListener$: Observable<string> = this.myName$.asObservable();
myName(name: string) {
    this.myName$.next(name)
}

In ngOnInit(), listen to the Observable

ngOnInit() {

    this.name$ = this.myNameListener$.pipe(
        delay(0)
    )

}

Subscribe to the Observable in your html template using the async pipe

<div *ngIf=" (name$ | async) as name ">{{ name }}</div>

Now you can pass your actual data to the observable whenever needed.

this.myName(data);

For a working solution and avoiding change detection issues, visit this link.

Best of luck!

Answer №3

An efficient approach to managing this situation is to define the form group within the child component, allowing for easy reference in the parent template! This method not only promotes type safety but also results in cleaner code compared to alternative solutions.

<mat-step [stepControl]="step1.step1Form">
  <app-step1 #step1></app-step1>
</mat-step>

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

Search through an array of objects and assign a new value

I am facing a challenge with an array of objects structured as shown below: [ { "e_id": "1", "total": 0 }, { "e_id": "3", "total": 0 } ] My objecti ...

What is the best way to simulate a function on a service that will provide a rejected promise?

Currently, I am working with Angular 6 and Jasmine to run tests on my code. One of the functions in my codebase invokes a service for logging in. This service returns a promise that resolves or rejects based on the success of the login attempt. To preven ...

Exploring methods for testing React components with TypeScript's props

I am currently tackling a react-typescript project and I am looking to conduct testing on props passed to a react component using react-testing library. Here, we have the SharedDashboardUiLatestValueWidget.tsx component: export interface SharedDashboardU ...

When creating a new instance of the Date object in Javascript, the constructor will output a date that is

In my project using TypeScript (Angular 5), I encountered the following scenario: let date = new Date(2018, 8, 17, 14, 0); The expected output should be "Fri Aug 17 2018 14:00:00 GMT-0400 (Eastern Daylight Time)", but instead, it is returning: Mon Sep ...

Having trouble connecting my Express.js API to Angular6. My server setup seems to be causing the issue

After uploading my Angular app on Godaddy Host in the public_html directory, I also included the Express.js API in the public_html/web-api folder. When it comes to running node on the host using SSH, everything seems to work fine as indicated by the consol ...

Error message "Cannot access property 'template' of undefined" is thrown when using Material Design Mat-footer-row component

Whenever I include the following code snippet: I encounter the error message: Cannot read property 'template' of undefined ..... <ng-container matColumnDef="Price"> <mat-header-cell *matHeaderCellDef >Price</mat-head ...

Leveraging the 'ref' keyword in TypeScript with Next.js

Currently, I am learning TypeScript in React but encountered a warning. import {useref} from 'react' export default function test(){ cons tmp = useRef() const data = tmp.current?.value return ( <div> <input type = ...

The error TS2339 occurs because the property 'remove' is not found in the type 'Document<unknown>'

I encountered an error while using my application Runtime Error: TSError: ⨯ Unable to compile TypeScript: src/controllers/notes.ts:134:20 - error TS2339: Property 'remove' does not exist on type 'Document<unknown, {}, { createdAt: Nat ...

Encountered a problem while creating a new Angular 7 app: Module 'temp' not found

While attempting to create a new app using the command ng new first-app, I encountered the following error: internal/modules/cjs/loader.js:589 throw err; ^ Error: Cannot find module 'temp' at Function.Module._resolveFilename (internal ...

Can you explain the significance of this line of code: elements:Array<any>?

Completely new to Angular and Javascript. I recently received an assignment for my angular.js class where I need to work on a code hint and make a simple form. The code hint provided is as follows: export class AppComponent { items:Array<any> ...

`Running ng serve will result in the creation of a 'dist' folder within each app sub

Since beginning my project, I have encountered an issue that is both normal and frustrating. The dist folder is being created with incomplete information related to the components inside it. dashboard dist (unwanted) components panel dist (unwanted) c ...

unique prefix for every component in nrwl nx applications

Is there a method to assign distinct component selector prefixes for individual apps without triggering tslint errors? For example, using 'one' as the prefix for App1 and 'two' for App2. Currently, only one prefix can be specified in th ...

Angular date control and its corresponding date panel are not properly aligned on the user interface

I am utilizing Angular and Angular Material for date control display. See the code snippet below: <input type="date" (change)="validateDateRange($event,true, index)" class="form-control oot-start-date align-middle" name=& ...

Angular 2 signal sender

I have a specific class definition for my Project: export class Project { $key: string; file: File; name: string; title: string; cat: string; url: string; progress: number; createdAt: Date = new Date(); constructor(file: File) { th ...

Choosing the Right Language for AngularJS 2: TypeScript, JavaScript, or Dart?

AngularJS 2 is on the horizon, and the documentation recommends three languages: Typescript, Javascript, and Dart. As someone who primarily works with Javascript EcmaScript 5, I'm curious about the strengths and weaknesses of these three options. Cu ...

A guide to sending epoch time data to a backend API using the owl-date-module in Angular

I have integrated the owl-date-time module into my application to collect date-time parameters in two separate fields. However, I am encountering an issue where the value is being returned in a list format with an extra null value in the payload. Additiona ...

Having trouble sending a POST request to a .NET API through the browser but successful with Postman? Need some troubleshooting advice?

My current struggle is with the inability to send a POST request to my .NET API using a browser, even though it works perfectly fine with Postman. //angular const url = "http://localhost:5001/ApplicationManager/saveForm"; const model = { ...

Steps to resolve the issue of MatCardComponent not being detected despite being imported within the module

I am encountering an issue with my Angular nrwl nx project that includes Bootstrap 5 and Angular Material. Within a library, I have a dashboard component where I import the MatCardModule. However, when trying to use it inside the DashboardComponent, I rece ...

Removing Multiple Object Properties in React: A Step-by-Step Guide

Is there a way in React to remove multiple object properties with just one line of code? I am familiar with the delete command: delete obj.property, but I have multiple object properties that need to be deleted and it would be more convenient to do this i ...

What is the best way to extract data from the currentValue property of SimpleChange objects?

Trying to utilize the onChanges lifecycle hook in Angular has led me to the SimpleChange object, with properties like currentValue and previousValue. When passing an array of numbers to this hook, I noticed that currentValue displays as type Array. Check ...