Sandeep Gupta
Sandeep Gupta

Reputation: 7250

How to add custom validation for ngModelGroup in Template driven forms

I am using template driven form approach. I want to add custom validations on a ngModelGroup. Namely, if one field is filled all fields must also be filled.

<form #f="ngForm">
      <div ngModelGroup="address">
        <input type="email" name="house_number" class="form-control" ngModel>
        <input type="email" name="street_number" class="form-control" ngModel>
      </div>
</form>

I search online and only found articles like this which talk about custom validation only at input level and not at ngModelGroup level. Other articles talk about Reactive forms which I can't implement.

Any help will be deeply appreciated.

Upvotes: 2

Views: 2450

Answers (2)

dev_ui
dev_ui

Reputation: 1

In case creating a new directive is a concern, you can make changes in the existing component. You can add a div to display the error message to the user in the html file.

<form (ngSubmit)="validateData(f)" #f="ngForm">
<div  ngModelGroup="address">
<input type="email" name="house_number" class="form-control" required ngModel>
<div style="color:red;display:none;" id="errorMsg">
This field cannot be empty
</div>
<input type="email" name="street_number" class="form-control" ngModel>
</div>
<button type="submit">Submit</button>
</form>

In the component section you can access the form values and show/hide validations as per your requirement.

address:any;
validateData(f) {

 console.log("Form Values : "+ f.value);

 this.address = f.value.address;

 if(!this.address.house_number){
    //code to show/hide the message
    var x = document.getElementById("errorMsg");
    x.style.display="block";
 }

 //to check form validity
 if (f.valid) {
    //do something 
  }

 }
}

Upvotes: 0

yurzui
yurzui

Reputation: 214017

Since you deal with template driven forms it would be better to create directive that will add validator to your address group:

@Directive({
  selector: '[ngModelGroup][requiredIfOneFilledValidator]',
  providers: [{
    provide: NG_VALIDATORS,
    useExisting: forwardRef(() => AddressValidator),
    multi: true
  }]
})
export class RequiredIfOneFilledValidator implements Validator {
  validate(group: AbstractControl): ValidationErrors | null {
    const controls = (group as FormGroup).controls; // we expect FormGroup here
    const controlNames = Object.keys(controls);
    const filledCount = controlNames.filter(name => !!controls[name].value).length;

    return filledCount > 0 && filledCount < controlNames.length ? { required: true } : null;
  }
}

Now you can easily use this validator in your template:

<div ngModelGroup="address" requiredIfOneFilledValidator>

And if you want to check whether group is valid or not the simply use template reference variable:

<div ngModelGroup="address" requiredIfOneFilledValidator #addressGroup="ngModelGroup">
   ...
</div>
<p>Group valid? {{addressGroup.valid}}</p>

Or just check the status of the whole form:

Form valid? {{ f.valid }}

Ng-run Example

Upvotes: 5

Related Questions