Verify whether the value of the Reactive form has been modified from its initial state

I am currently dealing with a reactive form that has 3 controls, and I need to detect any changes made to the form. I have implemented a method for detecting value changes in the form and subscribed to it. However, I am facing an issue where the event is being emitted every time any of the control values are changed.

In a scenario where a user adds a value to any of these 3 controls and attempts to leave the page, I want to display a modal indicating that there are unsaved changes in the form.

How can I verify whether the emitted value on form change is different from its initial value?

public createGroupForm: FormGroup;

ngOnInit() {
  this.createGroupForm = this._formBuilder.group({
      groupName: new FormControl("", [Validators.required]),
      groupPrivacy: new FormControl(null, [Validators.required]),
      groupTagline: new FormControl('')
  });

  this.onCreateGroupFormValueChange();
}


onCreateGroupFormValueChange(){
    this.createGroupForm.valueChanges.subscribe(value => {
      console.log(value);
      //emitting every time if any control value is changed
    });
}

Answer №1

It is important to verify the form values every time a subscription is emitted in order to compare them with the initial values. The hasChange property can be utilized whenever necessary.

public groupForm: FormGroup;
hasChange: boolean = false;

ngOnInit() {
  this.groupForm = this._formBuilder.group({
      groupName: new FormControl("", [Validators.required]),
      groupPrivacy: new FormControl(null, [Validators.required]),
      groupTagline: new FormControl('')
  });

  this.onFormValueChange();
}


onFormValueChange(){
    const initialValue = this.groupForm.value
    this.groupForm.valueChanges.subscribe(value => {
      this.hasChange = Object.keys(initialValue).some(key => this.form.value[key] != 
                        initialValue[key])
    });
}

Answer №2

You don't have to sign up, simply view it whenever the user is about to leave:

class UserDataComponent implements OnInit {
  userDetailsForm!: FormGroup;
  initialValues!: User;

  ngOnInit() {
    this.userDetailsForm = this.formBuilder.group({
      firstName: ['', [Validators.required]],
      lastName: ['', [Validators.required]],
      // More fields can be added here
    });
    this.initialValues = this.userDetailsForm.value;
  }

  formIsModified(): boolean {
    return Object.entries(this.initialValues).some(
      ([field, value]) => value !== this.userDetailsForm.value[field]
    );
  }

  leavingPage() {
    if (this.formIsModified()) {
      // Show confirmation modal
    } else {
      // Proceed with user redirection
    };
  }
}

Answer №3

It is recommended to utilize the ngOnDestroy lifecycle hook method. Within this method, you can compare the current value with the initial value of a form and trigger the opening of a modal if there is a difference.

@Component({ selector: 'my-cmp', template: `...` })
class MyComponent implements OnDestroy {
    public createGroupForm: FormGroup;
    public initialValues;

    ngOnInit() {
        this.createGroupForm = this._formBuilder.group({
            groupName: new FormControl("", [Validators.required]),
            groupPrivacy: new FormControl(null, [Validators.required]),
            groupTagline: new FormControl('')
        });
        this.initialValues = this.createGroupForm.value;
    }
    ngOnDestroy() {
        if (this.initalValues != this.createGroupForm.value) {
            //open modal
        }
    }
}

Answer №4

Utilize rxjs operators for value validation.

 import { FormGroup, FormBuilder, Validators, FormControl, FormArray, NgForm } from '@angular/forms';  
import { pairwise, startWith } from 'rxjs/operators';

this.createGroupForm.valueChanges
      .pipe(startWith(''), pairwise())
      .subscribe(([previousValue, currentValue]) => {
        console.log('Previous value:', previousValue);
        console.log('Current value:', currentValue);
        console.log('---');

        if (confirm('Confirm value change')) {
          console.log('Value changed');
        } else {
          console.log('Value not changed');
        }

Answer №5

One alternative approach involves subscribing to form.valueChanges :

import { isEqual } from 'lodash';

public groupForm : FormGroup;
hasModified: boolean = false;

private readonly destroyRef = inject(DestroyRef);

ngOnInit() {
  this.groupForm = this._formBuilder.group({
      name: new FormControl("", [Validators.required]),
      privacyStatus: new FormControl(null, [Validators.required]),
      tagline: new FormControl('')
  });

  this.onGroupFormValueChange();
}


onGroupFormValueChange(){
    const initialData = this.groupForm.value
    this.groupForm.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(
        formData => this.hasModified = !isEqual(initialData , formData))
      )
}

Answer №6

To implement a check for unsaved changes when leaving the page, you can utilize the dirty method in the following way:

if(this.createGroupForm.get('groupName').dirty || 
 this.createGroupForm.get('groupPrivacy').dirty || 
 this.createGroupForm.get('groupTagline').dirty) {
   // display alert for any unsaved modifications
}

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

Determine if the current page is the root page in Ionic 2 - here's how!

Currently, I am trying to verify the name of the current page in Ionic 2. To achieve this, I utilized NavController in my app.component.ts file. However, an error stating No provider for NavController is being displayed. I would appreciate any suggestions ...

Is there a more efficient method for crafting this Angular Testing code?

Currently, I am trying to enhance my knowledge of Angular testing. However, I am struggling to locate reliable resources that can guide me on the correct approach to take. My dilemma lies in my spec file where I am utilizing a function setDashboardGreetin ...

What is the process for triggering property decorators during runtime?

Wondering about dynamically invoking a property decorator at runtime. If we take a look at the code snippet below: function PropertyDecorator( target: Object, // The prototype of the class propertyKey: string | symbol // The name of th ...

Received Angular Error: Attempting to differentiate '[object Object]'. Permissible are only arrays and iterables

I've encountered an error while working on my front-end in Angular with Node.js as the backend. I'm struggling to identify the cause of this issue. Here is a snippet of my code. country.ts export class Country { id?: string; name?: string; ...

An ERROR occurred indicating that the Object(...) is not functioning properly when utilizing the UniqueDeviceID

I successfully completed the steps outlined in the tutorial found here: https://ionicframework.com/docs/native/unique-device-id/ and have version 5.0.0 installed. ts file : import { UniqueDeviceID } from '@ionic-native/unique-device-id/ngx'; ...

I'm looking to receive the specific data types for express request arguments. How can I

Currently, I am working on incorporating authentication into an express application using passport and typescript. For defining the user model, I have utilized typegoose. Once the login request passes through the passport strategy, a login method is called ...

Export interface for material-ui wrapper to cast any type in TypeScript (React)

I work with React using TypeScript. Recently, I encountered an issue with exporting. I'm creating an interface that encapsulates components from Material-ui. Here is a simplified example: Wrapping.tsx import { default as Component, ComponentProps ...

Creating a nested/child route structure within the Angular 2 router

Looking to implement nested routing for mypage/param1/1/param2/2 format in the URL. The first parameter is represented by 1 and the second one by 2. It's imperative that there are always two parameters, otherwise an error should be displayed. However, ...

Tips for troubleshooting Bootstrap Dropdown in a vertical navigation bar

Struggling to implement a vertical navigation bar with a dropdown menu on the third list item in Bootstrap 4, but facing issues as the dropdown options are not showing up. The template is specifically designed for an Angular component. I've gone thro ...

Achieving vertical alignment of placeholder in mat-select-field within Angular

I am trying to vertically center a placeholder in a mat-select element. How can I achieve this? It needs to be closer to the bottom of the select input. Here is an image showing my current issue Below are the relevant sections of my code: ...

Get the latest version of Node Stream using Angular 2

I am currently working on generating a PDF file on the server side using node.js and then downloading it on the client side with Angular 4. It's similar to how Google Drive allows you to download files as PDFs. Here is an example of the Node JS code ...

Angular CLI produced the Git command

After starting a project with the Angular CLI, I know it should create a git for me. I typed the following commands in my project directory: git add . git commit -m "some message" Now I want to push. Where do I push this to? Or where is the GitHub r ...

Error Encountered When Trying to Import BrowserAnimationsModule in Angular 5: Unexpected Token "<"

As I try to integrate the BrowserAnimationModule into my Angular 5 project, a rather foolish error keeps popping up. Error: (SystemJS) Unexpected token < SyntaxError: Unexpected token < at eval (<anonymous>) at Object.eval ...

Can you explain the distinction between implementing tab content styles?

My method for displaying tab content is as follows: <mat-tab-group> <mat-tab label="First"> <ng-template matTabContent> The First Content </ng-template> </mat-tab> </mat-tab-group> What sets this apar ...

Is it a common issue that sound.stop() doesn't seem to function properly, while sound.play() operates successfully within Howler

I am having trouble utilizing Howler.js in Reactjs with typescript. While I can successfully play the sound, pausing or stopping it seems to be an issue. Below is the code snippet: This component takes all the audio details as props. I used console.log() ...

What is the best way to update the current route in Angular 2 programmatically?

In my Angular 2 application, I am facing an issue with the navigation flow between the home component and the nav panel component. When a user clicks on the logout button in the nav panel, the current URL is correctly shown as "/login" in the console log. ...

Issues with sending emails through Nodemailer in a Next.js project using Typescript

I'm currently working on a personal project using Nodemailer along with Next.js and Typescript. This is my first time incorporating Nodemailer into my project, and I've encountered some issues while trying to make it work. I've been followin ...

What is the procedure for cancelling a file upload in the FileUpload component of PrimeNG?

1. Issue Overview Looking to terminate file upload in PrimeNG's FileUpload component when certain filename patterns are detected. Using Angular 6.0.7 and PrimeNG 6.0.2. 2. Initial Strategy 2.1. HTML Code <p-fileUpload #fileUploader name="file" ...

Troubleshooting Angular's efficiency problems when dealing with extensive datasets and prime NG datagrids

An angular application (V4.1) has been developed using primeNG components, focusing on data tables. The app was originally created for small clients and had no issues handling 2K-3K table rows with efficient filtering capabilities akin to a spreadsheet. R ...

Struggling to determine the type of constant after a specific type check? (TS2349: Unable to call a function on a type that does not have a call signature

Encountered a puzzling issue with TypeScript where it appears that I should be able to recognize that a constant is an array, and then utilize array methods on it. However, TypeScript seems unable to confirm that a value is truly an array even after a dire ...