Home > Software design >  Indirectly Conditional Required Validations (nested property) on Angular
Indirectly Conditional Required Validations (nested property) on Angular

Time:11-15

I was checking this question and its answers in order to post.

public isFirst = false;
public isSecond = false;

private buildFormGroup() {
    const internalFormGroup = this.formBuilder.group({
      idTypeCode: ['', [Validators.required, Validators.min(1)]],
      master: [true],
      slave: [''],
      fieldSlave: [''], // Validations depends indirectly on idTypeCode !!! HERE
    });

    return internalFormGroup;
}

private conditionalValidator(masterName: string, masterValue: any, slaveName: string, validators: any) {
    if (this.formGroup) {
      const masterControl = this.formGroup.get(masterName);
      if (masterControl) {
        masterControl.valueChanges.subscribe((value) => {
          const slaveControl = this.formGroup.get(slaveName);
          if (slaveControl) {
            if (value === masterValue) {
              slaveControl.setValidators(validators);
            } else {
              slaveControl.clearValidators();
            }
            slaveControl.updateValueAndValidity();
          }
        });
      }
    }
}


public onTypeCodeChange(event: MatSelectChange) {
    const selectedId =  event;
    const selectedTypeCode = this.typeCodes.find((typeCode) => typeCode.id === selectedId);
    this.isFirst = selectedTypeCode?.codeEnum === CodeEnum.First;
    this.isSecond = selectedTypeCode?.codeEnum === CodeEnum.Second;
}

I skip the code that fills the observable typeCodes$

public readonly typeCodes$: Observable<TypeCodeDto[]>;
private typeCodes: TypeCodeDto[] = [];

public ngOnInit(): void {
    this.typeCodes$.subscribe((typeCodes) => {
      this.typeCodes = typeCodes;
    });
    this.conditionalValidator('master', false, 'slave', [Validators.required, Validators.min(1)]);
}

As you can see the use of conditionalValidator method was easy!!!.

But, I have an interface that includes one Enum

    export interface TypeCodeDto {
      id?: number;
      name?: string;
      codeEnum?: CodeEnum;
    }

    export enum CodeEnum {
      None = 0,
      First = 1,
      Second = 2,
    }

In my HTML, I'm using a Select with the previous observable typeCodes$

  <mat-select
    formControlName="idTypeCode"
    [required]="formGroup.controls.idTypeCode.invalid"
    (ngModelChange)="onTypeCodeChange($event)"
  >
    <mat-option *ngFor="let typeCode of typeCodes$ | async" [value]="typeCode.id">
      {{ typeCode.name }}
    </mat-option>
  </mat-select>

Now I have an complex situation, in my HTML when the Select (or idTypeCode) correspond to one TypeCodeDto with codeEnum equals to Second I need to apply:

fieldSlave: ['', [Validators.required, Validators.maxLength(256), Validators.minLength(1)]],

How to implement that?

CodePudding user response:

Add one observable:

  private subCodeEnum = new Subject<CodeEnum>();

In your method!

  public onTypeCodeChange(event: MatSelectChange) {
    const selectedId =  event;
    const selectedTypeCode = this.typeCodes.find(
      (typeCode) => typeCode.id === selectedId
    );
    this.subCodeEnum.next(selectedTypeCode?.codeEnum);
  }

Create (or replace the code of your conditionalValidator method):

  private conditionalObservableValidator(
    masterObservable: Observable<any> | null,
    masterValue: any,
    slaveControl: AbstractControl | null,
    trueValidators: ValidatorFn | ValidatorFn[] | null,
    falseValidators?: ValidatorFn | ValidatorFn[] | null
  ) {
    if (slaveControl && masterObservable) {
      masterObservable.subscribe(
        (value) => {
          if (value === masterValue) {
            slaveControl.setValidators(trueValidators);
          } else {
            slaveControl.setValidators(falseValidators);
          }
          slaveControl.updateValueAndValidity();
        }
        
      );
    }
  }

How to use it?

  public ngOnInit(): void {
    //
    
    this.conditionalObservableValidator(
      this.formGroup.get('master').valueChanges,
      false,
      this.formGroup.get('slave'),
      [Validators.required, Validators.min(1),]
    );

    this.conditionalObservableValidator(
      this.subCodeEnum,
      CodeEnum.Second,
      this.formGroup.get('fieldSlave'),
      [Validators.required, Validators.maxLength(256), Validators.minLength(1)],
      null // Last argument is Not necessary
    );
  }
  • Related