Anubha Gupta
Anubha Gupta

Reputation: 189

How to set "At least one field should be filled out" validation in Angular 7 with reactive forms?

I have one reactive form in angular 7 which has three fields email, phone and pager. My requirement is that at least one of them should be filled out by user otherwise we should throw error like this "Please specify one of the notifications (Email, SMS or Pager)."

I have tried writing a custom notification but its not working. Could you please help me that where I am going wrong.

Below is my HTML code:

<form class="form-horizontal" [formGroup]="editorForm">
  <div class="form-group" required>
      <label for="emailAdressInput">Email Addresses</label>
      <input type="text" id="emailAdressInput" formControlName="emailAdresses">
  </div>

<div class="form-group" required>
      <label for="phoneNumberInput">Email Addresses</label>
      <input type="text" id="phoneNumberInput" formControlName="phoneNumber">
  </div>

<div class="form-group" required>
      <label for="pagerNumberInput">Email Addresses</label>
      <input type="text" id="pagerNumberInput" formControlName="pagerNumber">
</div>
</form>

Below is component.ts code:

this.editorForm = this._formBuilder.group({
          displayLabel: ['', Validators.required],
          emailAdresses: [''],
          phoneNumber: [''],
          notification: this._formBuilder.group({
            pagerNumber: [''],
            phoneNumber: [''],
            emailAdresses: ['']
          }, this.atLeastOneValidator()),
          pagerNumber: [''],
       });

       public atLeastOneValidator = () => {
         return (controlGroup) => {
           let controls = controlGroup.controls;
            if ( controls ) {
                let theOne = Object.keys(controls).find(key=> controls[key].value!=='');
               if ( !theOne ) {
                return {
                    atLeastOneRequired : {
                        text : 'At least one should be selected'
                    }
                }
            }
        }
        return null;
    };
};

Upvotes: 1

Views: 10969

Answers (3)

Eliseo
Eliseo

Reputation: 57981

To indicate validators using formBuilder in a formgroup is adding {validators:yourValidator}

group(controlsConfig: { [key: string]: any; }, options: AbstractControlOptions | { [key: string]: any; } = null)

See in the docs AbstractControlOptions

(if you want to create a formControl, can add the validator directly)

this.editorForm = this._formBuilder.group({
          displayLabel: ['', Validators.required],
          emailAdresses: [''],
          phoneNumber: [''],
          notification: this._formBuilder.group({
            pagerNumber: [''],
            phoneNumber: [''],
            emailAdresses: ['']
          },{ validators:this.atLeastOneValidator()}), //<--THIS
          pagerNumber: [''],
       });
  }

FutherMore, you forget a <div formGroupName="notification"> in your .html

Anyway, I recomended not use the formBuilder, you can use teh constructor of form group and formControl like

  this.editorForm = new FormGroup({
          displayLabel: new FormControl('', Validators.required),
          emailAdresses: new FormControl(''),
          phoneNumber: new FormControl(''),
          notification: new FormGroup({
            pagerNumber: new FormControl(''),
            phoneNumber: new FormControl(''),
            emailAdresses: new FormControl('')
          },this.atLeastOneValidator()), 
          pagerNumber: new FormControl(),
       });
  }

Upvotes: 1

Ankit Prajapati
Ankit Prajapati

Reputation: 2820

I have solved it in stackblitz : https://stackblitz.com/edit/angular-j3i4yg

export class AppComponent {
  name = 'Angular';

  editorForm: FormGroup;
  // _formBuilder: FormBuilder = new FormBuilder();
  constructor(private _formBuilder: FormBuilder) {

    this.editorForm = this._formBuilder.group({
      displayLabel: ['', Validators.required],
      emailAdresses: [''],
      phoneNumber: [''],
      notification: this._formBuilder.group({
        pagerNumber: [''],
        phoneNumber: [''],
        emailAdresses: ['']
      }, { validators: this.atLeastOneValidator }),
      pagerNumber: [''],
    });
  }

  public atLeastOneValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {

    let controls = control.controls;
    console.log(controls);
    if (controls) {
      let theOne = Object.keys(controls).findIndex(key => controls[key].value !== '');
      if (theOne === -1) {
        console.log(theOne);
        return {
          atLeastOneRequired: {
            text: 'At least one should be selected'
          }
        }
      }
    };

  }
}

Template :

<form class="form-horizontal" [formGroup]="editorForm">
  <fieldset formGroupName="notification">
  <div class="form-group" required>
      <label for="emailAdressInput">Email Addresses</label>
      <input type="text" id="emailAdressInput" formControlName="emailAdresses">
  </div>

<div class="form-group" required>
      <label for="phoneNumberInput">Email Addresses</label>
      <input type="text" id="phoneNumberInput" formControlName="phoneNumber">
  </div>

<div class="form-group" required>
      <label for="pagerNumberInput">Email Addresses</label>
      <input type="text" id="pagerNumberInput" formControlName="pagerNumber">
</div>
  </fieldset>

{{editorForm.get('notification')?.errors| json}}
<br>
<br>
<span *ngIf="editorForm.get('notification')?.errors?.atLeastOneRequired"> {{editorForm.get('notification')?.errors?.atLeastOneRequired.text}}</span>

</form>

Check and let me know if you have any doubts.

Upvotes: 3

user4676340
user4676340

Reputation:

atLeastOneValue(form: FormGroup): ValidationErrors {
  return Object.keys(form.value).some(key => !!form.value[key]) ? 
    null : 
    { atLeastOneRequired : 'At least one should be selected' };
}

And call it through

builder.group({...}, [this.atLeastOneValue]);

Upvotes: 2

Related Questions