What are the methods to alter validation for a Formfield based on the input from other Formfields?

My aim is to create a Form where input fields are required only if one or more of them are filled out. If none of the fields have been filled, then no field should be mandatory. I came across a suggestion on a website that recommended using "valueChanges" to set up a listener on the fields and implementing the logic inside the listener. After applying the logic within the listener, the method "updateValueAndValidity()" should be called.

However, an issue arises when I include "updateValueAndValidity()"; it triggers a Recursion Error in the browser. I'm puzzled as to why this error occurs since I don't see any recursion in my code. It seems like "updateValueAndValidity()" might be causing the listener to trigger again.

If I exclude "updateValueAndValidity()", the recursion error disappears but the desired logic doesn't function properly. I'm stuck on how to resolve this problem, so I'm seeking some guidance. Can anyone assist me?

isFormEmpty: boolean = true;
addressForm: FormGroup;
   
onChanges() {
        let street = this.addressForm.get('street');
        let streetNumber = this.addressForm.get('streetNumber');
        let zipCode = this.addressForm.get('zipCode');
    
        this.addressForm.valueChanges.subscribe(val => {
          console.log(val);
          this.isFormEmpty = true;
          if (val.street !== '') {
            this.isFormEmpty = false;
          } else if (val.streetNumber !== '') {
            this.isFormEmpty = false;
          } else if (val.zipCode !== '') {
            this.isFormEmpty = false;
          }
    
          if (!this.isFormEmpty) {
            street.setValidators([Validators.required, Validators.maxLength(36)]);
            streetNumber.setValidators([Validators.required, Validators.maxLength(10)]);
            zipCode.setValidators([Validators.required, Validators.maxLength(10)]);
          } else {
            street.setValidators([Validators.maxLength(36)]);
            streetNumber.setValidators([Validators.maxLength(10)]);
            zipCode.setValidators([Validators.maxLength(10)]);
          }

          street.updateValueAndValidity();
          streetNumber.updateValueAndValidity();
          zipCode .updateValueAndValidity();
            
        });
      }
    
      ngOnInit() {
        this.addressForm = this.formBuilder.group({
          street: ['', Validators.maxLength(36)],
          streetNumber: ['', Validators.maxLength(10)],
          zipCode: ['', Validators.maxLength(10)]
        });
            
        this.onChanges();
    }

Answer №1

If you want to prevent triggering valueChanges observables, you can provide the {emitEvent:false} object configuration to the updateValueAndValidity method.

Give this a go:

onChanges() {
        let street = this.deliveryAddressForm.get('street');
        let streetNumber = this.deliveryAddressForm.get('streetNumber');
        let zipCode = this.deliveryAddressForm.get('zipCode');

        this.deliveryAddressForm.valueChanges.subscribe(val => {
          console.log(val);
          this.isFormEmpty = true;
          if(val.street !== '') {
            this.isFormEmpty = false;
          } else if(val.streetNumber !== '') {
            this.isFormEmpty = false;
          } else if(val.zipCode !== '') {
            this.isFormEmpty = false;
          }

          if(this.isFormEmpty == false) {
            street.setValidators([Validators.required, Validators.maxLength(36)]);
            streetNumber.setValidators([Validators.required, Validators.maxLength(10)]);
            zipCode.setValidators([Validators.required, Validators.maxLength(10)]);
          } else {
            street.setValidators([Validators.maxLength(36)]);
            streetNumber.setValidators([Validators.maxLength(10)]);
            zipCode.setValidators([Validators.maxLength(10)]);
          }

          street.updateValueAndValidity({emitEvent:false});
          streetNumber.updateValueAndValidity({emitEvent:false});
          zipCode.updateValueAndValidity({emitEvent:false});

        });
      }

Answer №2

To ensure the validity of a formControl, you must utilize the updateValueAndValidity() method.

yourForm.controls['controlName'].updateValueAndValidity();

For your specific scenario, it should look something like this:

deliveryAddressForm.controls['streetNumber'].updateValueAndValidity();

Answer №3

One issue arises when subscribing to form value changes within the onChange function. By doing so, a new subscription is created during each change detection cycle, causing a memory leak.

To address this problem, consider moving the code to the ngOnInit function or a function that only gets called once.

With this approach, there's no need to manage the onChange lifecycle as you're directly monitoring the form's changes.

Answer №4

To streamline the process, consider creating a personalized formError for your formGroup. It's actually easier than you might think - just implement a function in your component:

   createCustomFormError() {
       return (formGroup: FormGroup) => {
          const oneFilled = formGroup.value.street || 
                  formGroup.value.streetNumber || 
                  formGroup.value.zipCode;
          if (!oneFilled)
              return null;
          const error = {};
          if (!formGroup.value.street)
              error.requiredStreet = 'Street is required';
          if (!formGroup.value.streetNumber )
              error.requiredStreetNumber = 'Street number is required';
          if (!formGroup.value.zipCode)
              error.requiredZipCode = 'Zip code is required';

          return error;
       }
    }

From there, you can easily set up the form like this:

formGroup = new FormGroup({
     street: new FormControl(null, Validators.maxLength(36)),
     streetNumber: new FormControl(null, Validators.maxLength(10)),
     zipCode: new FormControl(null, Validators.maxLength(10)),
}, this.createCustomFormError())

To validate the errors, use the following code:

<div *ngIf="formGroup.errors?.requiredStreet">Required Street</div>
<div *ngIf="formGroup.errors?.requiredStreetNumber">Required Street Number</div>
<div *ngIf="formGroup.errors?.requiredZipCode">Required Zip Code</div>

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

Select either one checkbox out of two available options

My form includes two checkboxes, allowing users to click on both of them. I'm wondering if it's possible to set it up so that only one checkbox can be selected at a time. For example, clicking on the first checkbox would turn it on while turning ...

Success Notification in ASP.net MVC after Form Submission

I am looking to implement a success alert pop-up or message after the form is submitted and action is successful. In this scenario, I want to display "successfully add": Create Action : [HttpPost] [ValidateAntiForgeryToken] public ActionResult Cr ...

Modify the property of an element during execution

I am tasked with developing a button that can change the type of a chart component (e.g., from columns to pie) upon clicking. However, I am unsure how to implement this functionality within the component structure. The goal is to modify the :series-default ...

Moving data from the bottom of the page to the top

I am facing a situation where I have several foreach loops on a page that calculate the sum of variables. By the end of these loops, a specific variable contains data. However, I now need to display this data at the top of my page before the foreach loop. ...

Using Node Express.js to access variables from routes declared in separate files

Currently, I am in the process of developing a website with numerous routes. Initially, all the routes were consolidated into one file... In order to enhance clarity, I made the decision to create separate files for each route using the Router module. For ...

Try utilizing querySelectorAll() to target the second item in the list

As I delve into the world of HTML and JS, I came across the document.querySelectorAll() API. It allows me to target document.querySelectorAll('#example-container li:first-child'); to select the first child within a list with the ID 'exampl ...

JavaScript event array

I have a JavaScript array that looks like this: var fruits=['apple','orange','peach','strawberry','mango'] I would like to add an event to these elements that will retrieve varieties from my database. Fo ...

Using an AJAX function to retrieve data from two different server-side scripts and populate two separate HTML elements on the page

My goal in this coding situation is to change values in multiple DOM targets. The example from Tizag shows the DOM being altered within the onreadystatechange function, like this: if(ajaxRequest.readyState == 4){ document.myForm.time.value = ajaxRequ ...

Combine the values of properties in an object

I have a JavaScript object that contains various properties with string values. I want to concatenate all the property values. Here's an example: tagsArray["1"] = "one"; tagsArray["2"] = "two"; tagsArray["Z"] = "zed"; result = "one,two,zed" To prov ...

Showcase a variety of random images in the navigation bar using Javascript and HTML

In order to create a unique experience for mobile users, I have implemented multiple images for my navigation bar menu icon toggle button. These images are stored in an array and a random image is selected each time the page loads using Math.Random. Howeve ...

Having trouble retrieving data from mobx store in Ionic3

I am attempting to retrieve a value from the @computed getCategories() function, but every time I try to call this function, I encounter the following error: Cannot invoke an expression whose type lacks a call signature. Type 'any[]' has no comp ...

`Need help setting the active class for a bootstrap navbar using Angular JS?`

In my bootstrap navbar, I have the following menu items: Home | About | Contact I'm looking to assign the active class to each menu item based on the current angular route. Specifically, how can I set class="active" when the angular route is at # ...

Reorganize child JSON objects into a new object that includes a parent ID

Exploring the realm of JavaScript, I am currently delving into Node.JS to interact with an API and save the data in a SQL Server. Utilizing the "request" and "mssql" Node packages for this task as they possess robust documentation and support. My query re ...

Is there a way to define an object's keys as static types while allowing the values to be dynamically determined?

I am attempting to construct an object where the keys are derived from a string union type and the values are functions. The challenge lies in wanting the function typings to be determined dynamically from each function's implementation instead of bei ...

I tried implementing enums in my Angular Material select component, but unfortunately, it seems to be malfunctioning

Here is my TypeScript file I am working on creating a select list with enums in my project, but I am encountering an error. I have shared screenshots showing the enums with both keys and values, but I only want to display their keys and save their values ...

Display the splash screen just once upon the initial loading of the app with Angular's progressive web app

Recently, I developed a sample splash screen component for my Angular PWA app. However, I am facing an issue where the splash screen appears every time the application is refreshed on any page, not just when the app starts. This is not the behavior I want. ...

What is the best way to update the mat-tab when the routeParameters are modified?

I need to reinitialize the mat-tab-group in order to make the first tab active when there is a change in the routeParams. ts file: public index = 0; ngOnInit() { this.subscription = this.route.params.subscribe((routeParams: Params) => { // some ...

When a custom icon is clicked in the vue-select dropdown, the custom method is not activated

In my current project, I am creating a vue-component based on vue-select. Within this component, I have added a custom info Icon. The issue I am facing is that when the user clicks on the Icon, instead of triggering my custom method getInfo, it opens the s ...

unable to respond when clicking an angularjs link

I'm facing an issue where I can't get a link to respond to click events in AngularJS. When I click on the anchor link, nothing happens. Here is a snippet of the AngularJS script: <script data-require="<a href="/cdn-cgi/l/email-protection" ...

Divide the identical elements and distinct elements from a provided array into two separate arrays storing unique elements and recurring elements

Can anyone help me with this query? I have an array of objects that need to be separated into repeating and non-repeating objects based on the segments they belong to, each in a separate array. For example- [ {name:"abc",id:1,segments:[1,2]}, {n ...