Tips for accessing the FormControlName of the field that has been modified in Angular reactive forms

My reactive form consists of more than 10 form controls and I currently have a subscription set up on the valueChanges observable to detect any changes. While this solution works well, the output always includes the entire form value object, which includes all form controls along with their values. Is there a way to specifically identify the name of the form control that was changed?

this.form = this.fb.group({
    field1: ['', Validators.required],
    field2: ['', Validators.required],
    field3: ['', Validators.required],
    field4: ['', Validators.required],
    field5: ['', Validators.required],
    field6: ['', Validators.required],
    field7: ['', Validators.required],
    field8: ['', Validators.required],
    field9: ['', Validators.required],
    field10: ['', Validators.required],
    field11: ['', Validators.required],
    field12: ['', Validators.required],
    field13: [{ value: '', disabled: true }, Validators.required]
});

this.form.valueChanges.subscribe(
    result => this.calculateParams(result)
);

calculateParams(result) {
    console.log(result); // giving the entire form.value object
}

Answer №1

Eliseo's solution using rxjs here

this.form.valueChanges.pipe(
    startWith(this.form.value),
    pairwise(),
    map(([previousValues, currentValues]) => {
        return Object.keys(currentValues).find(key => currentValues[key] != previousValues[key]);
    }),
).subscribe(updatedKey => {
    console.log(updatedKey)
});

Answer №2

One method to consider is storing the previous values and implementing a solution similar to this:

this.previousValues={...this.formData.value}
this.formData.valueChanges.subscribe(response=>{
  const key=Object.keys(response).find(property=>response[property]!=this.previousValues[property])
  this.previousValues={...this.formData.value}
})

Answer №3

To separate the form control from the form group, simply utilize the get function and then access the valueChanges method.

this.form.get('formControlName').valueChanges()
.

Please keep in mind that form.get will give you the AbstractControl, which also includes the valueChanges method.

Answer №4

Although not thoroughly tested, the concept involves matching controls with their respective keys and listening to valueChanges on each control simultaneously. Instead of returning the value, the object key is returned (and both the value and key can be mapped to the output).

const inputFields = {
    fieldA: ['', Validators.required],
    fieldB: ['', Validators.required],
    fieldC: ['', Validators.required],
    fieldD: ['', Validators.required],
    fieldE: ['', Validators.required],
    fieldF: ['', Validators.required]
}

zip(
    from(Object.values(fb.group(inputFields).controls)),
    from(Object.keys(inputFields))
).pipe(mergeMap([control, key]) => control.valueChanges.pipe(mapTo(key)))

Answer №5

If you want to monitor each change individually, you can subscribe to them separately.

for (const ctrlProp in this.myForm.controls) {
    if (this.myForm.controls.hasOwnProperty(ctrlProp)) {
      this.myForm.controls[ctrlProp].valueChanges.pipe(untilDestroyed(this)).subscribe(res => {
        console.log(ctrlProp + " has been updated to ", res);
      });
    }
}

This approach gives it more of an Angular feel.

Answer №6

To ensure proper functionality, employ the valueChanges method for each control after dynamically adding a new control to the FormGroup.

const newControl = {key: 'myNewControl'};    
this.myForm.addControl(newControl.key, new FormControl('', Validators.required))
this.myForm.controls[newControl.key].valueChanges
.subscribe(data => {
    // the newControl object will remain accessible as a closure within this context, allowing us to manipulate each form control accordingly
    console.log(data, newControl.key);
})

The newControl object can be accessed within the subscribe function as a closure.

Answer №7

Utilizing the approach by @alex-walker () appears to be advantageous, however, it is most effective on flat forms containing simple key-value pairs. In scenarios where there are nested forms with FormArrays of FormGroups, the key property in that technique will solely return the top-level key of the form, which may not be very practical if you wish to specify to the user the specific input field that triggered the event.

To address this limitation, I merged this method with a deep object difference function explained here:

This hybrid solution yields:

this.form.valueChanges
    .pipe(
        startWith(this.form.value),
        pairwise(),
        map(([oldValues, newValues]) => {
            return bdiff(oldValues, newValues);
        })
    )
    .subscribe(keys => {
        console.log(keys);
    });

bdiff(a: Record<string, any>, b: Record<string, any>): string[] {
    return _reduce(
        a,
        (res, val, key) =>
            res.concat(
                (_isPlainObject(val) || Array.isArray(val)) && b
                    ? this.bdiff(val, b[key]).map(x => key + (key.trim ? '' : ']') + (x.search(/^\d/) ? '.' : '[') + x)
                    : !b || val !== b[key]
                    ? [key + (key.trim ? '' : ']')]
                    : []
            ),
        []
    );
}

This will produce an array of nested keys like

keys = ['fieldgroup2.fieldArray[3].myNestedField']
which can be easily accessed and marked as invalid using form.get(keys[0]).valid.

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

Printing Multiple Pages Using JavaScript and Cascading Style Sheets

Encountering difficulties displaying page numbers when printing multiple multipage reports Here is the provided HTML Format : <style type="text/css> body { counter-reset: report 1 page 0; } td.footer:after { counter-increment: page; content ...

A circular reference occurs when a base class creates a new instance of a child class within its own definition

My goal is to instantiate a child class within a static method of the base class using the following code: class Baseclass { public static create(){ const newInstance = new Childclass(); return newInstance; } } class Childclass ex ...

Navigating through different components in Angular without using templates

Searching for a straightforward solution using Angular to manage routes. I have a webpage generated by the server featuring a basic Google map and some logic spread across three separate controllers. Now, I aim to integrate routing into this setup. Nothi ...

You have encountered an error: [ERR_HTTP_HEADERS_SENT]. This means that you cannot set headers after they have already been sent to the client, even if a return

I've encountered a 'Cannot set headers after they are sent to the client' error with the /api/users/profile route and have been attempting to resolve it. I stumbled upon some solutions on stackoverflow suggesting to add a return statement - ...

Using Typescript to inherit from several classes with constructors

I am trying to have my class extend multiple classes, but I haven't been able to find a clean solution. The examples I came across using TypeScript mixins did not include constructors. Here is what I am looking for: class Realm { private _realms: C ...

Create a consistent number based on quantity

Looking to generate a sequence of numbers equal to the count in PHP or JavaScript for my application. For example, if my total count is 7: <?php $total = 7; I would like to generate seven 1's like this: $split_one = [1,1,1,1,1,1,1]; If my count ...

I'm only appending the final element to the JavaScript array

Currently, I have the following code: I'm endeavoring to create a new JSON object named dataJSON by utilizing properties from the GAJSON object. However, my issue arises when attempting to iterate over the GAJSOn object; only its last element is added ...

After the click event, the variable in the Angular .ts file does not get refreshed

Great! I have a service in my .ts component that loops through an array of court names. Every time I click on a next or back arrow event, a counter is incremented starting at 0, where index 0 corresponds to field 1 and so on. The issue I'm facing is ...

Step-by-step guide to utilizing instance functions in AngularJS

Recently I started working with angular in my project but I'm struggling to figure out how to access instance functions from the original code. Here is a snippet of my function: $scope.collapsedrow = true; $scope.PlusClick = function(event) ...

Creating an HTML element that can zoom, using dimensions specified in percentages but appearing as if they were specified in pixels

This question may seem simple, but I have been searching for an answer and haven't found one yet. Imagine we have an HTML element with dimensions specified in pixels: <div style="width:750px; height: 250px"></div> We can easily resize i ...

What is the solution for breaking a querySnapshot in Firestore?

Is there a way to exit a querysnapshot loop prematurely? I attempted using a for loop, but I keep encountering the following error message. How can this error be resolved or is there an alternative method to break out of a snapshot loop? code return ...

Encountered an error during npm installation: Fetch Package Metadata error occurred while attempting to request from http://registry.npmjs.org/concurrently, the cause being a socket hangup

I am encountering the following errors: "An unexpected fetchPackageMetaData error occurred while making a request to http://registry.npmjs.org/concurrently failed due to a socket hang up." I am currently connected through a corporate proxy with the firew ...

Is it possible to implement a customized pathway for the functions within an Azure function app?

Recently, I set up a new function app on Azure using Azure Functions Core Tools with Typescript as the language. The app includes a test function named MyTestFunction that responds with an HTTP response when called. This particular function is located in ...

When a page first loads in Next.js with Firebase, the useEffect hook may return an undefined value

My component is designed to retrieve a subcollection from firestore: import { useEffect, useState } from "react"; import { db } from '../firebase/firebaseInit' import {useAuth} from '../components/AuthContextProvider' import { ...

The parent element of a 3D div is causing issues with hovering and clicking on the child elements

In my scenario, the parent div is transformed in 3D with rotation, causing it to move to the backside. The issue arises with the child div containing a button that becomes unclickable due to the parent div position. Setting backface-visibility to hidden al ...

Why use getElementById(id) to obtain an element in JavaScript when it already exists in the JS?

Recently, I have observed that a reference to an HTML element with an id can be easily accessed in JavaScript by using a variable named after that id (jsbin). What is the reason for this behavior? Why do we need to use getElementById(id) when we could sim ...

The debounced function in a React component not triggering as expected

I am facing an issue with the following React component. Even though the raiseCriteriaChange method is being called, it seems that the line this.props.onCriteriaChange(this.state.criteria) is never reached. Do you have any insights into why this.props.onC ...

Passing data back from an asynchronous function to its parent function in Node.js

Exploring the world of asynchronous programming is a new adventure for me as I delve into implementing Twilio video calls through Node.js. I've been grappling with calling a server-side function that in turn invokes another asynchronous function retu ...

react componentdidupdate triggers never-ending iteration

When I trigger an API call to elasticsearch using onChange, it prompts a list for autocomplete. To make sure that my store is updated before rerendering, I included componentDidMount so that I am not lagging behind by one tick. Here is the code snippet: c ...

Angular 14 is throwing an error due to an indent issue - the expected indentation is 2 spaces but 4 spaces were found

Currently using "eslint": "^8.23.0" with angular 14. Everything was functioning properly with "eslint": "^8.22.0", but after updating to 8.23.0, I encountered the following error: This is my eslint configuration: ...