Anant Dhas
Anant Dhas

Reputation: 115

Angular2 reactive form control validation depends on the outside property of a form

I have used some validators in angular2 like equal password validator. In these scenarios, both two controls are in the same form and I can access both controls in a directive's validate method.

But in my scenario, Form has a one form-control with name Email and it is required only when EmailFlag is true. EmailFlag is not a form-control and will update its value any time. I am handling the change value of EmailFlag in the validator's ngOnChanges and handling change of Email in the validate method of a validator.

I can access and set validation to form-control only when validate method get called. But on change of flag, I am unable to get form-control and set validation to it. Is there any way to get form-control on flag change and set validations?

Directive

@Directive({
  selector: '[requiredIfTrue]',
  providers: [{
    provide: NG_VALIDATORS,
    useExisting: RequiredIfTrueValidator,
    multi: true
  }]
})
export class RequiredIfTrueValidator implements OnChanges {
  _emailFlag: boolean;
  @Input('requiredIfTrue') public requiredIfTrue: boolean;

  validate(control: AbstractControl): { [key: string]: any } | null {
    if (this._emailFlag) {
      //set validation
    }
    else {
      //remove validation
    }
  }

  ngOnChanges(change: SimpleChanges) {
    if (change.requiredIfTrue) {
      this._emailFlag = change.requiredIfTrue.currentValue;
    }
  }
}

component.ts

EmailFlag = false;
//flag declared and changes value time by time

component.html

<input type="text" formControlName="Email" class="form-control" [requiredIfTrue]="EmailFlag" />

Upvotes: 1

Views: 3518

Answers (3)

Chellappan வ
Chellappan வ

Reputation: 27303

Inject NgControl inside custom directive to get FormControl diretive instance inside custom directive like this

import { Directive, OnInit, Input } from '@angular/core';
import { NgControl, Validators } from '@angular/forms';
@Directive({
  selector: '[RequiredIfTrue]'
})
export class RequiredIfTrueDirective{
  @Input('RequiredIfTrue') set requited(value){
    console.log(value);
    if(value){
       this.control.control.setValidators(this.control.control.validator ? [this.control.control.validator, Validators.required] : Validators.required );
       this.control.control.updateValueAndValidity();
    }else{
            this.control.control.clearValidators();
            this.control.control.updateValueAndValidity();
    }
  };
  constructor(private control:NgControl) {

   }
}

Working Example

Upvotes: 1

Eliseo
Eliseo

Reputation: 57929

If your want use a validator that depending of a variable in your .ts, you can use bind(this)

emailFlag:boolean=true;
email=new FormControl("email",this.requiredIf().bind(this))

requiredIf()
  {
    return (control)=>{
      return this.emailFlag?!control.value?{required:true}:null:null
    }
  }

Be carefult, when you change the variable you need make an updateValueAndValidity

click(){
   this.EmailFlag=!this.EmailFlag
   this.email.updateValueAndValidity()
}

see stackblitz

NOTE: I use the way of this.requiredIf, to allow, e.g. if you has an array required=[true,false,false,true], to pass the "index" to the function

That's

<form [formGroup]="form">
  <input formControlName="one">
  <small *ngIf="form.get('one').invalid">*</small>
  <button (click)="clickArray(0)">click</button>
  required:{{required[0]}}<br/>
    <input formControlName="two">
    <small *ngIf="form.get('two').invalid">*</small>
    <button (click)="clickArray(1)">click</button>
    required:{{required[1]}}<br/>
  <input formControlName="three">
  <small *ngIf="form.get('three').invalid">*</small>
  <button (click)="clickArray(2)">click</button>
  required:{{required[2]}}<br/>
</form>

And you change as

form = new FormGroup({
    one: new FormControl("", this.requiredIfArray(0).bind(this)),
    two: new FormControl("", this.requiredIfArray(1).bind(this)),
    three: new FormControl("", this.requiredIfArray(2).bind(this))
  });

requiredIfArray(index) {
  return control => {
    return this.required[index]? !control.value? { required: true }: null: null;
  };
}

Upvotes: 1

jgerstle
jgerstle

Reputation: 1684

You can force check of validity using updateValueAndValidity. You can also run it on the formGroup. See here: https://netbasal.com/three-ways-to-dynamically-alter-your-form-validation-in-angular-e5fd15f1e946

Upvotes: 0

Related Questions