ngModelChange not triggered when updating the model from a component

I am currently exploring Angular and trying to grasp the concept of connecting different controllers to update their values based on changes in one controller.

In a specific scenario, I have a form with ngModel and I am attempting to utilize ngModelChange to automatically update another property in my model. The challenge arises when ngModelChange is triggered only by user interaction with the control, not when the model is updated from the component.

For reference, here is an example code snippet:

https://stackblitz.com/edit/angular-ivy-z2q4mr

HTML Template:

<form #f="ngForm" (ngSubmit)="onSubmit(f)">
  <mat-form-field appearance="fill">
    <mat-label>Field 1</mat-label>
    <input matInput name="field1" [(ngModel)]="MyItem.field1" (ngModelChange)="changeField2($event)">
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Field 2</mat-label>
    <input matInput disabled name="field2" [(ngModel)]="MyItem.field2" (ngModelChange)="changeField3($event)">
  </mat-form-field>
  
  <mat-form-field appearance="fill"</gt;
    <mat-label>Field 3</mat-label>
    <input matInput disabled name="field3" [(ngModel)]="MyItem.field3">
  </mat-form-field>
</form>

Component

import { OnInit, Component, ViewChild } from '@angular/core';


@Component({
  selector: 'app-component1',
  templateUrl: './component1.component.html',
  styleUrls: ['./component1.component.css']
})
export class Component1Component implements OnInit  {
  MyItem : any = {}

    constructor() {
      }
      
    ngOnInit(): void {
    }
    
    changeField2(event: any){
        this.MyItem.field2 = this.MyItem.field1 + " modification field 2";
    }
    
    changeField3(event: any) {
        this.MyItem.field3 = this.MyItem.field2 + " modification field 3";
    }
}

The desired outcome is for field3 to be updated automatically when making changes to field1, as it is indirectly related through field2. While calling changeField3 inside changeField2 is a possible solution, managing multiple controllers can make this approach cumbersome due to potential repetitive calls.

Answer №1

If you do not input anything into Field2, the Field3 will not be updated.

The (ngModelChange) event is triggered by the ngModel directive when the model changes.

To ensure your example functions properly, you need to update the third field along with the second whenever the first field changes.

changeField2(event: any) {
    this.MyItem.field2 = this.MyItem.field1 + ' modification field 2'; 
    this.MyItem.field3 = this.MyItem.field2 + ' modification field 3';
}

Another approach to achieve your desired outcome is to utilize ReactiveForms. Check out this Playground for a demonstration.

Answer №2

If you're looking to incorporate the functionality of `(change)` into your HTML form, you can do so by using the following code snippet:

<form #f="ngForm" (change)="changeField3()">
  <mat-form-field appearance="fill">
    <mat-label>Field 1</mat-label>
    <input matInput name="field1" [(ngModel)]="MyItem.field1" (ngModelChange)="changeField2($event)">
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Field 2</mat-label>
    <input matInput disabled name="field2" [(ngModel)]="MyItem.field2" >
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Field 3</mat-label>
    <input matInput disabled name="field3" [(ngModel)]="MyItem.field3">
  </mat-form-field>
</form>

To handle the change event in your Typescript file, you can implement the `changeField3()` function like this:

  changeField3() {
   
    this.MyItem.field3 = this.MyItem.field2 + ' modification field 3'; 
  }

This method allows you to monitor any modifications made to your form model dynamically.

Answer №3

By utilizing reactive-forms and the ValueChanges property, I successfully achieved my objective of capturing controller value changes, even when altered by a component method.

For a demonstration of this using reactive-forms, refer to this example: https://stackblitz.com/edit/angular-ivy-gh7zho

Below is the HTML template:

<form [formGroup]="testForm" (ngSubmit)="onSubmit()">
  <mat-form-field appearance="fill">
    <mat-label>Field 1</mat-label>
    <input matInput name="field1" formControlName="field1" />
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Field 2</mat-label>
    <inputs matInput name="field2" formControlName="field2" />
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Field 3</mat-label>
    <input matInput name="field3" formControlName="field3" />
  </mat-form-field>

  <button type="submit">Submit</button>
</form>

<button (click)="resetField2()">Reset field2</button>

And here is the Component code:

import { OnInit, Component, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  testForm: FormGroup = this.fb.group({
    field1: [{ value: '', disabled: false }, Validators.required],
    field2: [{ value: '', disabled: true }, Validators.required],
    field3: [{ value: '', disabled: true }, Validators.required]
  });
  
  constructor(private fb: FormBuilder) {
    const field1 = this.testForm.get('field1');
    const field2 = this.testForm.get('field2');

    field1!.valueChanges.subscribe(val => {
      this.changeField2();
    });

    field2!.valueChanges.subscribe(val => {
      this.changeField3();
    });
  }

  changeField2() {
    this.testForm
      .get('field2')!
      .setValue(this.testForm.get('field1')!.value + ' modification field 2'); //Updates field2 whenever field1 is changed
  }

  changeField3() {
    this.testForm
      .get('field3')!
      .setValue(this.testForm.get('field2')!.value + ' modification field 3'); //Does not get called when updating field2
  }

  resetField2() {
    this.testForm.get('field2')!.setValue(''); //Resets field2 to only show modification field 3
  }

  onSubmit() {
    console.warn(this.testForm.value);
  }
}

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

Exploring the capabilities of ExcelJS for reading xlsx files within an Angular environment

I'm trying to access a source file, make some changes to it, and then provide it for the user to download. However, I am facing an issue with reading the source file from my project directory. Below is my implementation using excelJS for file reading: ...

Sending data to a parent component from a popup window in Angular Material using a button click while the window is still open

How can I retrieve data from an Angular Material Dialog Box and send it to the Parent component? I am able to access data after the dialog box is closed. However, I am wondering if there is a way to retrieve data while the dialog box is still open, especi ...

Error in TypeScript logEvent for Firebase Analytics

Currently utilizing firebase SDK version 8.0.2 and attempting to record a 'screen_view' event, encountering an error message stating: Error: Argument of type '"screen_view"' is not assignable to parameter of type '" ...

Requesting for a template literal in TypeScript:

Having some trouble with my typescript code, it is giving me an error message regarding string concatenation, const content = senderDisplay + ', '+ moment(timestamp).format('YY/MM/DD')+' at ' + moment(timestamp).format(&apo ...

Trouble with Angular not Displaying BootStrap 5.0 Styles

Trying to implement both BootStrap and Angular into the project, I successfully applied the styles by adding the following line in the index file: <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="sty ...

Error display in Elastic Apm Rum Angular implementation

Having some issues with incorporating the @elastic/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f5948598d8878098d8949b9280999487b5c7dbc4dbc4">[email protected]</a> package into my project. Angular is throwing console ...

What is the best way to showcase a diverse list with varying templates using Angular?

Looking to showcase a variety of items sourced from a service, each potentially belonging to different types. I am in search of a way to dynamically display distinct templates for each item based on its value or type. Is there a functionality that allows ...

Using TypeScript in the current class, transform a class member into a string

When converting a class member name to a string, I rely on the following function. However, in the example provided, we consistently need to specify the name of the Current Component. Is there a way to adjust the export function so that it always refers ...

What is a way to perform pre-increment without utilizing the ++I operator?

It is my belief that the code snippet below: i += 1 or i = i + 1 does not have the same effect as ++i. Am I incorrect in this assumption? Is there an alternative method to achieve pre-increment without utilizing the ++ operator? ...

Launching in dynamically loaded modules with bootstrapping

The Angular Guide explains that during the bootstrapping process, components listed in the bootstrap array are created and inserted into the browser DOM. However, I have noticed that I am unable to bootstrap components in my lazy loaded feature modules. E ...

Can Primeng and Angular2 be integrated into JSF without the use of npm?

I'm looking to implement the Angular2 framework for my frontend development, and I've decided to use PrimeNG for the UI components. However, I am not familiar with how npm functions. Here are some tech details: I will be using Eclipse or NetBe ...

Dealing with NPM problems during Angular 9.0.7 setup issues

I encountered a problem after a recent Windows update that corrupted my system. I had to reinstall Windows, and now I am unable to run my Angular project, which was originally in version 9.0.7 with a package.json file. I tried installing Angular 9.0.7 glob ...

Is it possible to import node_modules from a specific directory mentioned in the "main" section of the package.json file?

Is it feasible to import from a source other than what is defined by the "main" setting? In my node_modules-installed library, the main file is located at lib/index.js With es2015 imports (source generated from ts compiled js), I can use the following ...

Guide on setting up an AWS code pipeline for Elastic Beanstalk deployment on ASP.NET Core 5.0 with Angular

I am facing a challenge with deploying my ASP.NET Core 5.0 application with Angular to the EBS Windows environment using AWS CodePipeline (CI / CD). Despite searching various internet resources for solutions, I have not been able to find much help. My att ...

Establish a global variable within a TypeScript file

I am currently facing an issue where I need to add an event to the browser inside the CheckSocialMedia function. However, it keeps saying that it could not find the name. So, my question is how do I make the 'browser' variable global in the .ts ...

Problem encountered while implementing callbacks in redux-saga

I am facing a scenario in which I have a function called onGetCameras that takes a callback function named getCamerasSuccess. The idea is to invoke the external function onGetCameras, which makes an AJAX call and then calls getCamerasSuccess upon completio ...

selective ancestor label Angular 8

I am looking for a way to place my content within a different tag based on a specific condition. For instance, I need my content to be enclosed in either a <table> or <div> depending on the condition. <table|div class="someClass" ...

In the context of React Typescript, the term 'Component' is being mistakenly used as a type when it actually refers to a value. Perhaps you intended to use 'typeof Component' instead?

Looking to create a routes array and apply it to useRoutes in react-router@6. I am currently using TypeScript and Vite. However, I encountered an error when attempting to assign my component to the 'element' key. type HelloWorld = /unresolved/ ...

Error with the type of CanvasGradient in the NPM package for converting text to image

I attempted to generate an image using a specific text by utilizing npm's text-to-image package, but encountered an error during typescript compilation. The errors I encountered upon running the typescript compilation command are related to files with ...

Refused to run script from inline content on the lightweight server due to security policy restrictions

I have been adhering to Angular's best practices in order to create a Progressive Web App (PWA). After building the production version (ng build --prod --aot), I am running it from the dist folder on localhost using npm run dev ("dev": "lite-server"). ...