Validator in Angular FormControl ensures that two fields have the same value or both are empty

When filling out a form with four fields, I have encountered a specific requirement. Two of the fields are mandatory, which is straightforward. However, the other two must either both be empty or both have a value - essentially resembling an XNOR logic state.

s1_male     s1_female      s1_male_valid     s1_female_valid
empty       empty          true              true
value       empty          false             false
empty       value          false             false
value       value          true              true

Although the code below functions correctly, it does not update the UI appropriately. For example, if I input a value in 's1_male', it marks as invalid but 's1_female' remains unflagged. Even after entering a value in 's1_female', it still appears valid while 's1_male' is indicated as invalid. In such cases, I find myself going back to 's1_male' and either modifying or deleting the value to resolve the issue.

I attempted calling updateValueAndValidity() on both fields within the provided function, yet this action triggers an endless loop causing Chrome to eventually terminate.

formGroup.addControl('rx_male', new FormControl('', Validators.required));
formGroup.addControl('rx_female', new FormControl('', Validators.required));
formGroup.addControl('s1_male', new FormControl('', this.scaleOptionValidator(formGroup)));
formGroup.addControl('s1_female', new FormControl('', this.scaleOptionValidator(formGroup)));

// these line cause endless loop
formGroup.controls.s1_male.valueChanges.subscribe(value => {
  formGroup.controls.s1_female.updateValueAndValidity();
});
formGroup.controls.s1_female.valueChanges.subscribe(value => {
  formGroup.controls.s1_male.updateValueAndValidity();
});

...

private scaleOptionValidator(formGroup: FormGroup): ValidatorFn {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
      let isValid = false;
      const female = formGroup.controls.s1_female;
      const male = formGroup.controls.s1_male;
      if (male && female) {

        if ((male.value && female.value) || (!male.value && !female.value)) {
          isValid = true;
        }

        return isValid ? null : { scalseNotValid: true };
      }
      return null;
    };
  }

Any suggestions would be greatly appreciated.

UPDATE: It seems that the formgroup approach is effective, however, I am also using Ionic here where, frustratingly, the ng-invalid class is applied but the ion-valid class overrides the UI styling until selecting and then deselecting the input field.

Answer №1

When faced with the task of validating two password fields in my sign up form, this was the solution I came up with.

validatePasswords(group: FormGroup): ValidationErrors | null {
    const password = group.get("password")
    const retypedPassword = group.get("retypedPassword")

    if (password !== null && retypedPassword !== null) {
        return password.value === retypedPassword.value ? null : { mismatch: true }
    } else {
        throw new Error("The form group is invalid. The group does not have a 'password' and/or a 'retypedPassword' control.")
    }
}

You can implement a similar method by creating an inner FormGroup for the passwords within your sign up form. Then, add a validator to that specific group and use its validity status to display error messages.


To streamline your code within the template, consider adding a getter function like this:

get passwords(): FormGroup {
    return this.signupForm.get("passwords") as FormGroup
}

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

Creating dynamic class fields when ngOnInit() is called in Angular

I am trying to dynamically create variables in a class to store values and use them in ngModel and other places. I understand that I can assign values to variables in the ngOnInit() function like this: export class Component implements OnInit{ name: st ...

What is the process of unloading pages in Angular 2?

In the process of developing an Angular 2 application consisting of approximately 200 pages, we have considered various loading strategies such as lazy loading, eager loading, and pre-loading. A question that arises is whether a page that has been lazily ...

Steps for adding an array of JSON objects into a single JSON object

I have a JSON array that looks like this: var finalResponse2 = [ {Transaction Amount: {type: "number"}}, {UTR number: {type: "string"}} ] My goal is to convert it into the following format: responses : [ { Transaction Amount: {type: "number"}, UTR numbe ...

Changing the color of a Chart.js chart in Angular: A step-by-step guide

I've been struggling to change the color of my chart without success. Any assistance on this matter would be greatly appreciated. Despite trying to assign color values to datasets, I am still unable to achieve the desired result. This is a snippet f ...

The Angular app.component.html is failing to display the newly added component

Having some trouble rendering my new component in the browser. I've set up a fresh project using the Angular CLI and created a component named list-employees. Upon running ng serve -o, the project compiles without errors, but the browser displays a b ...

Vue.js 3 with TypeScript is throwing an error: "Module 'xxxxxx' cannot be located, or its corresponding type declarations are missing."

I developed a pagination plugin using Vue JS 2, but encountered an error when trying to integrate it into a project that uses Vue 3 with TypeScript. The error message displayed is 'Cannot find module 'l-pagination' or its corresponding type ...

What is the best way to link a specific version of the @types package to its corresponding JavaScript package version?

Currently, I am in the process of working on a nodejs project with typescript 2.2 that relies on node version 6.3.1. My goal is to transition from using typings to utilizing @types. However, during this migration, several questions have arisen regarding th ...

Using memoization for React Typescript callback passed as a prop

My component is designed to display data retrieved from a callback provided in the props. To prevent an infinite loop caused by mistakenly passing anonymous functions, I am looking for a method to enforce the usage of memoized callbacks. const DataRenderer ...

What steps can I take to make my animation work in the opposite direction as well?

I'm currently working with an angular slider that is set to TRUE/OPEN by default. The issue I am facing is that while I am able to slide it using angular animations in one direction, I am unable to see the transition when sliding it back. Any assistan ...

Develop a versatile factory using Typescript

For my current project, I am developing a small model system. I want to allow users of the library to define their own model for the API. When querying the server, the API should return instances of the user's model. // Library Code interface Instanc ...

The component is failing to store its value within the database

I'm encountering an problem when attempting to save an option in the database. To address this issue, I created a component in Svelte called StatePicker that is responsible for saving US States. However, when I try to save it in the database using a ...

What steps should we take to ensure that the container beside the fixed left navigation bar is responsive?

Currently, I am working on creating a user interface that features a fixed navigation bar on the left hand side and a responsive content window. Here is what I have implemented so far: <div style="width: 15%; float: left; display: inline;"> <na ...

What steps should be taken to resolve the error message "This Expression is not constructable"?

I'm trying to import a JavaScript class into TypeScript, but I keep getting the error message This expression is not constructable.. The TypeScript compiler also indicates that A does not have a constructor signature. Can anyone help me figure out how ...

Spring Boot - The Cross-Origin Resource Sharing filter is effective for handling GET requests, however it does not properly handle other

In my current project, I am working on a Spring Boot 2.2.5 application paired with an Angular 9 frontend. One of the challenges I have faced is configuring a CORS filter in the Spring Boot backend to allow any origin, headers, and requests. After thoroug ...

What is the best way to transform this date string into a valid Firestore timestamp?

Currently, I am facing an issue in my Angular application that is integrated with Firebase Firestore database. The problem lies in updating a date field from a Firestore timestamp field. To provide some context, I have set up an update form which triggers ...

The NextAuth getServerSession function is functional when used with a POST API method, but it

Encountering an issue where getServerSession functions correctly on a POST route but not on a GET route. import { getServerSession } from "next-auth" import { authOptions } from "../../auth/[...nextauth]/route" import { NextResponse } f ...

The term 'ObjectId' is typically used as a type, but in this context it is being incorrectly used as a value

I've been scouring for an answer without success. As a newcomer to both stackoverflow and typescript, I've encountered an issue while creating a Mongoose Schema. Here's a snippet of my code: import { Schema, ObjectId } from 'mongoose&a ...

Using p5.js with TypeScript and Webpack is not supported

I'm currently working on a library project that involves utilizing p5.js. Specifications Here is a snippet of my Webpack configuration: const path = require('path'); module.exports = { entry: './start.ts', output: { ...

Advantages of using index.js within a component directory

It seems to be a common practice to have an index file in the component/container/module folders of react or angular2 projects. Examples of this can be seen in: angular2-webpack-starter react-boilerplate What advantages does this bring? When is it recom ...

retrieving and presenting information stored in a Firebase repository

I am currently working on retrieving data from a firebase database and storing it in an array called courses that I have declared within my class. Here's the code I have so far: import { AngularFireDatabase, AngularFireList } from 'angularfire2 ...