Assistance needed with updating custom validators in Angular 14

I've set up my form along with a custom validator that I believe should be functioning correctly. This form consists of 15 fields, out of which the custom validator is designed to refer to three specific ones ('icmp','tcpPorts','udpPorts'). In addition to other mandatory fields, it is necessary for at least ONE of these three fields to be filled in order to proceed with submission.

Below is the code snippet from my component.ts file:

 newFWXForm = this.fb.group(
{
  sspSelect: ["", Validators.required],
  requester: [this.loggedInUser],
  requesterContactInfo: [this.loggedInUserEmail],
  fwxDescription: ["", Validators.required],
  durationTypeSelect: ["Permanent", Validators.required],
  durationDate: [""],
  infraSelect: [""],
  sourceIPs: ["", Validators.required],
  DestAnyCheck: [false],
  SrcAnyCheck: [false],
  icmp: [false],
  destinationIPs: ["", Validators.required],
  tcpPorts: [],
  udpPorts: [],
  addDirectory: new FormControl(false),
},
{
  Validators: this.atleastOnePortValue("icmp", "tcpPorts", "udpPorts"),
}
);

 private atleastOnePortValue( controlNameA: string,controlNameB: string,controlNameC: string): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
  const formGroup = control as FormGroup;
  const valueOfControlA = formGroup.get(controlNameA)?.value;
  const valueOfControlB = formGroup.get(controlNameB)?.value;
  const valueOfControlC = formGroup.get(controlNameC)?.value;

  if (
    valueOfControlA === false &&
    valueOfControlB === null &&
    valueOfControlC === null
  ) {
    return { atLeastOne: true};
  } else {
    return null;
  }
};
}

Can anyone provide insights or assistance on why this setup is not working as expected? Your help is greatly appreciated, thank you in advance!

Answer №1

Important: This answer demonstrates how to create a validator for "one control" rather than the entire form.

It's similar to this Stack Overflow thread, but it includes an additional field.

export function conditionalValidator(field: string[]): ValidatorFn {
  return (formControl) => {
    if (!formControl.parent) {
      return null;
    }
    const otherControls = field.map((x) => formControl.parent.get(x));
    if (otherControls.filter((x) => x).length == field.length) {
      const error =
        formControl.value || otherControls.find((x) => x.value)
          ? null
          : { error: 'this field or ' + field + ' is required' };

      const controlCheck = error
        ? otherControls.filter((x) => x.valid)
        : otherControls.filter((x) => x.invalid);
      if (controlCheck.length) {
        setTimeout(() => {
          controlCheck.forEach((x) =>
            x.updateValueAndValidity({ emitEvent: false })
          );
        });
      }
      return error;
    }
  };
}

For example, you can use it like this:

form = new FormGroup({
    control1: new FormControl(
      null,
      conditionalValidator(['control2', 'control3'])
    ),
    control2: new FormControl(
      null,
      conditionalValidator(['control1', 'control3'])
    ),
    control3: new FormControl(
      null,
      conditionalValidator(['control1', 'control2'])
    ),
  });

Take a look at the StackBlitz example.

Answer №2

Just a simple issue :-)

When adding validators to a form using the Formbuilder, remember to use the parameter "validators" instead of "Validators".

newFWXForm = this.fb.group(
  {
    ...
  },
  {
    validators: this.atleastOnePortValue("icmp", "tcpPorts", "udpPorts"),
  }
);

The rest of your code seems good, but make sure to also check for empty strings, not just null. This is important because when a user clears a field, it will not be set back to null. The initial value should be null.

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

In Angular, updating an input field with text when a button is clicked can be done by utilizing two separate components to render the input field and button

Related Question However, my scenario is slightly different. I am dealing with two components: one with an input field and the other with a button. My goal is to insert text into the input field when the button is clicked, even though they are render ...

Eliminate the "email address" input field from Stripe checkout in Angular 2

I am currently using Angular 2 for my project and need some help. Is there a way to remove the email address field from Stripe checkout? https://i.stack.imgur.com/auf6C.png Thank you in advance! ...

Issue encountered when attempting to assign a value to an array property in Angular

Having trouble setting an array property in angular 6 using the following code: this.addupdate.roleids=this.selectedRole; An error is being thrown: ERROR TypeError: Cannot set property 'roleids' of undefined at AccessLevelComponent.pus ...

Struggling with manipulating arrays in Angular 7

Alright, let me give you the gist: Here's the deal - I've got a dataset chilling in sessionStorage, looking something like this: [{ "id": "123:456", "streetAddress": "1020 15th St", "point": { "lati": 35.74633, "longi": ...

Is it possible to localize a German date without using the dot?

When utilizing a date pipe in Angular with a German localized weekday, an automatic addition of a dot/full stop can be observed behind the weekday. <span>{{ day | date:'EE'}}</span> The desired output: Mo, Di, Mi However, the curr ...

What is the most effective method for testing event emitters?

Imagine I have a basic component structured like this: @Component({ selector: 'my-test', template: '<div></div>' }) export class test { @Output selected: EventEmitter<string> = new EventEmitter<string>() ...

Tips on eliminating expansion upon button click in header within an Angular application

While utilizing Angular Materials, I encountered a challenge with the mat-expansion component. Every time I click on the buttons within the expansion panel, it closes due to the default behavior of mat-panel. Requirement - The panel should remain expanded ...

The parameter type '(value: any) => any[]' does not match the expected parameter type 'string[]'

I'm encountering an issue where I can't push a value to a state array due to a TS error. Let me highlight the relevant components where the error occurs. The error specifically lies in the child component, within the handleValue(value => [...v ...

Utilize Angular 9 with an M1 Mac device

Struggling to get my project, which utilizes Node 12 and Angular 9, up and running on my new M1 Mac. I used nvm to install node and the latest npm version, then ran the command npm i -g @angular/cli@9 to install angular. Even though which ng confirms that ...

Utilizing ES6 Map type in TypeScript for Angular 2 Response Data Transfer Object Definition

Is it possible to utilize the es6 Map type in an HTTP Response DTO? Let's consider an Angular 2 request: public loadFoos(): Observable<FoosWrapper> { return this.http.get("/api/foo") .map(res => res.json()); } Now, take a loo ...

What is the best way to utilize the currency pipe with just the country code?

Currently in the process of developing an application using NativeScript and Angular. I am facing a challenge where I need to utilize the currency pipe for certain output, but I only have a locale/country code without a currency code. How can I ensure th ...

Unexpected TypeScript issue: Unable to access the 'flags' property of an undefined entity

Upon creating a new project and running the serve command, I encountered the following error: ERROR in TypeError: Cannot read property 'flags' of undefined Node version: 12.14 NPM version: 6.13 Contents of package.json: { "name": "angular-t ...

Learn how to securely download files from an Azure Storage Container using Reactjs

I'm currently working on applications using reactjs/typescript. My goal is to download files from azure storage v2, following a specific path. The path includes the container named 'enrichment' and several nested folders. My objective is to ...

Here's a new take on the topic: "Implementing image change functionality for a specific div in Angular 8 using data from a loop"

I need to create a list with dynamic data using a loop. When I click on any item in the list, I want the image associated with that item to change to a second image (dummyimage.com/300.png/09f/fff) to indicate it's active. This change should persist e ...

How to adjust the font size and font style for the [pageSizeOption] in mat-paginator

I am having trouble adjusting the font-size of the [pageSizeOptions] in my mat-paginator element in the application. <mat-paginator [pageSizeOptions]="[10, 20, 30]"></mat-paginator> The "10" appears too small compared to the text "items per p ...

Issues with sorting in Angular Material table after making a call to http.get()

When using Angular Material tables to display data, I noticed an issue with sorting when retrieving JSON data via http.get(). Unlike hardcoded inline JSON, the sorting functionality seems to break in this scenario. To better illustrate this problem, I&apo ...

Utilizing the power of Promise and async operators in conjunction with Angular's ng

Update: I'm happy to report that my alternative solution is now also functional. It seems there was a glitch in the local storage that was causing the previous issue. Query: I am seeking guidance on how to effectively use the combination of promise ...

Combine the object with TypeScript

Within my Angular application, the data is structured as follows: forEachArrayOne = [ { id: 1, name: "userOne" }, { id: 2, name: "userTwo" }, { id: 3, name: "userThree" } ] forEachArrayTwo = [ { id: 1, name: "userFour" }, { id: ...

Integrating modules in Angular 2

Exploring the functionalities of Angularjs 2.0, I encountered an issue when attempting to inject a service into a class. Below is the code snippet that's causing trouble: import {Component, View, bootstrap, NgFor, HttpService, Promise} from 'ang ...