Angular Custom Validator Conditional Required

6 min read Oct 16, 2024
Angular Custom Validator Conditional Required

Angular Custom Validator: Conditional Required Fields

Angular's built-in validation features are robust, but sometimes you need more control. This is where custom validators shine. They allow you to create validation logic tailored to your specific needs, including conditionally requiring fields.

When to Use Conditional Validators

Imagine a form where the requirement of a field depends on the selection in another field. For example, in an online store checkout form, you might need the customer's billing address only if they choose a different shipping address. This is a perfect scenario for a conditional validator.

Building Your Custom Validator

Let's dive into creating a custom validator for a conditional required field:

import { FormControl, FormGroup, ValidatorFn, AbstractControl } from '@angular/forms';

// Custom validator function
export function conditionalRequiredValidator(controlName: string, dependentControlName: string): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } | null => {
    // Get the dependent control
    const dependentControl = control.parent?.get(dependentControlName) as FormControl;

    // Check if the dependent control has a value
    if (dependentControl?.value && !control.value) {
      // If the dependent control has a value and the current control is empty, return an error
      return { 'conditionalRequired': true };
    }
    // Otherwise, return no error
    return null;
  };
}

Explanation:

  • conditionalRequiredValidator function: Takes two parameters:
    • controlName: The name of the control being validated.
    • dependentControlName: The name of the control that determines the requirement.
  • control.parent?.get(dependentControlName): Accesses the dependent control within the form group.
  • dependentControl?.value && !control.value: Checks if the dependent control has a value and the current control is empty.
  • { 'conditionalRequired': true }: Returns an error object if the conditions are met.

Integrating with Your Form

  1. Declare the Validator: In your component's Validators array, include the custom validator:

    import { Component } from '@angular/core';
    import { FormGroup, FormBuilder, Validators } from '@angular/forms';
    import { conditionalRequiredValidator } from './conditional-required-validator';
    
    @Component({
      selector: 'app-my-component',
      templateUrl: './my-component.html',
      styleUrls: ['./my-component.css']
    })
    export class MyComponent {
      myForm: FormGroup;
    
      constructor(private fb: FormBuilder) {
        this.myForm = this.fb.group({
          shippingAddress: ['', Validators.required],
          billingAddress: ['', [Validators.required, conditionalRequiredValidator('billingAddress', 'shippingAddress')]]
        });
      }
    }
    
  2. Handle Validation Errors: In your template, use the formControlName directive and display validation errors:

    Billing address is required when a different shipping address is provided.

Conditional Logic with Observables

For more complex scenarios, you can use RxJS observables to observe changes in the dependent control:

import { FormControl, FormGroup, ValidatorFn, AbstractControl } from '@angular/forms';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

export function conditionalRequiredValidator(controlName: string, dependentControlName: string): ValidatorFn {
  return (control: AbstractControl): Observable<{ [key: string]: any } | null> => {
    // Get the dependent control as an observable
    const dependentControl$: Observable = control.parent?.get(dependentControlName)?.valueChanges as Observable;

    // Combine latest values of dependent control and current control
    return combineLatest([dependentControl$, control.valueChanges]).pipe(
      map(([dependentValue, currentValue]) => {
        // Apply conditional logic based on combined values
        if (dependentValue && !currentValue) {
          return { 'conditionalRequired': true };
        }
        return null;
      })
    );
  };
}

Key Points:

  • The validator now returns an observable that emits error objects based on the combined values of the dependent and current controls.
  • The combineLatest operator combines the latest emissions from both observables.
  • The map operator transforms the combined values to either an error object or null.

Conclusion

Custom validators empower you to implement complex and dynamic validation logic within your Angular applications. Conditional validation, in particular, provides flexibility by making field requirements dependent on other values in your form. By mastering this technique, you can build more user-friendly and robust forms that meet the specific needs of your application.

Featured Posts