9

I have a bank loan application which consists of alot of input fields, some of which are hidden (the hidden fields are shown dynamically based on a set of contidions). E.g if you choose option 1, a hidden field gets shown, and some other fields are hidden. If you choose option 2, some fields gets show, other fields get hidden. In the end of the form i have a which means the button will be disabled until the whole form is valid, but my problem now is that the hidden fields also get validated so the form will never be valid. Is there a way to tell angular to not validate fields if they are hidden?

The way i hide my fields is like the example below:

<form [formGroup]="form">

<select formControlName="loanType"> 
 <option value="0">Car loan</option>
 <option value="1">Student loan</option>
</select>

<div *ngIf="loanType === 0"> 
 <input type="text" required>
</div>

<div *ngIf="loanType === 1">
 <input type="text" required>
</div>

<button type="submit" [disabled]="!form.isValid">

</form>
7
  • Which form syntax are you using: template-driven forms or model-driven forms? For model-driven form, I'd say hiding the field in the template is not enough, you also have to update the model (in the component class). You could use a method like AbstractControl.clearValidators() to programmatically clear the validators of a specific field. Commented Jan 31, 2017 at 20:59
  • 1
    (Side note: you're not just "hiding" the fields with *ngIf, you're actually removing them from the DOM.) Commented Jan 31, 2017 at 21:05
  • 1
    Have a look at this blog: scotch.io/tutorials/… Commented Jan 31, 2017 at 21:09
  • @AngularFrance he is removing it with witih *ngIf, yes, so it should not affect the form in any way! Weird if it does. Interestingly enough, in Angular 1, a form is correctly .$valid, even if a required input with ng-show="false" is empty. Commented Jan 31, 2017 at 21:15
  • 1
    @Blauhirn this is a documented behavior with Angular 2's Reactive Forms and is expected, see Kara's reply to this issue: github.com/angular/angular/issues/7970 Commented Jan 31, 2017 at 21:20

3 Answers 3

12

You are using reactive form. Even the fields are hidden from user the fields are active in the from. So simply you need to disable the field from the reactive from by using following code

form.get("fieldName").disable();

In reactive form you don't require the "required" in input tag. Also add the formControlName as below.

<input formControlName="inputFieldName" type="text">

So the html file will be like

<form [formGroup]="form" novalidate>

<select formControlName="loanType"> 
 <option value="0">Car loan</option>
 <option value="1">Student loan</option>
</select>

<div *ngIf="loanType === 0"> 
 <input formControlName="inputField1" type="text">
</div>

<div *ngIf="loanType === 1">
 <input formControlName="inputField2" type="text">
</div>

<button type="submit" [disabled]="!form.isValid">

</form>

We add formControlName to 2 input fields and it is declared in ts file.

this.form = this.formBuilder.group({
    loanType: ["", [Validators.required]],
    inputField1: ["", [Validators.required]],
    inputField2: ["", [Validators.required]],
});

We can create value change listener for loanType field

this.form.get("loanType").valueChanges.subscribe((loanType) => {
    this.form.get("inputField1").disable();
    this.form.get("inputField2").disable();
    if(loanType === 1) {
         this.form.get("inputField1").enable();
    } else if (loanType === 2) {
         this.form.get("inputField2").disable();
    }
});

So when the loan type is 1 inputField1 will be enabled and when loan type is 2 inputField2 is enabled.

By this form will be valid when the fields are hidden since it is disabled.

Sign up to request clarification or add additional context in comments.

3 Comments

I think it is a better user experience if the asterix (*) is displayed on required fields, thus I would not omit the required attribute for those fields.
This answer should be the accepted one, because removing and adding controllers does not make sense when you can disable them instead. You don't have to think about adding the controllers again later if you need to, you can just enable them.
N.B : valueChanges gets called every time you call disable or enable.
6

UPDATE:

After doing some research, i found that i need to dynamically update the formGroup by using FormGroup.addControl() and FormGroup.removeControl().

The articles i read to come to my conclusion was: https://github.com/angular/angular/issues/7970 (check Karas answer)

https://scotch.io/tutorials/how-to-implement-conditional-validation-in-angular-2-model-driven-forms

just to give an example of what my code looks like for the next man with the same problem:

if (this.loanTypeId === 1) {
   this.form.addControl('name', new FormControl("", Validators.required));
} else {
   this.form.removeControl('name')
}

3 Comments

I haven't tried this, but if you remove the controller, and add the controller later, will the value previously written still be available? E.g. If I write 'John' in the name-field. Will the name, 'John', still be visible when you remove and add the controller later?
I would have possibly gone down the route of writing a custom validator, that emulates required, but takes an extra property you bind to it for hidden. When the hidden is true always return valid. Be wary of double negatives in heroes example of custom validators!
Here's an example I gave of custom validator without the confusion. stackoverflow.com/a/54756903/495157
0

First you can create group of template basis of your option. We can use *ngIf in template to hide and show group of elements in form. Then use formBuilder to create form instance of form each time pass only object of those form controls which are enable.

Example

    <form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<label for="name">First Name:</label>
<input type="text" formControlName="fname"
placeholder="Enter name">
<br /><br />
<div *ngIf="lNameEmail1">
<label for="name">Last Name:</label>
<input type="text" formControlName="lname"
placeholder="Enter name">
<br /><br />

<label for="email">Email:</label>
<input type="email" formControlName="email"
placeholder="Enter email">
</div>
<div *ngIf="lNameEmail2">

<label for="name">Last Name2:</label>
<input type="text" formControlName="lname2"
placeholder="Enter name">

<br /><br />

<label for="email">Email2:</label>
<input type="email" formControlName="email2"
placeholder="Enter email">
</div>
<br /><br />
<button type="submit" [disabled]="!myForm.valid">Submit
</button>
<button type="submit" (click)='formGrp1()'> Form 1</button>
<button type="submit" (click)='formGrp2()'> Form 2</button>
</form> 

Angualr class

export class AppComponent implements AfterViewInit {
    public myForm: FormGroup;
    lNameEmail1 = false;
    lNameEmail2 = false;
    myFormProperty1 = {
    "fname": new FormControl("", Validators.required)
    };

    myFormProperty2 = {
    "fname": new FormControl("", Validators.required),
    "lname": new FormControl("", Validators.required),
    "email": new FormControl("")

    };
    myFormProperty3 = {
    "fname": new FormControl("", Validators.required),
    "lname2": new FormControl("", Validators.required),
    "email2": new FormControl("")

    };

    constructor(public fb: FormBuilder) {
    this.myForm = this.fb.group(this.myFormProperty1);
    }


    formGrp1(){
    alert('Form 1 enable')

    this.lNameEmail1 = true;
    this.lNameEmail2 = false;

    this.myForm = this.fb.group(this.myFormProperty2);


    this.myForm.valueChanges.subscribe(data =>
    console.log('form object ====' + JSON.stringify(data)
    )); 
    }
    formGrp2(){
    alert('Form 2 enable')

    this.lNameEmail1 = false;
    this.lNameEmail2 = true;

    this.myForm = this.fb.group(this.myFormProperty3);

    this.myForm.valueChanges.subscribe(data =>
    console.log('form object ====' + JSON.stringify(data)
    )); 

    }
    onSubmit() {
    console.log('Form submitted Value=='+ JSON.stringify(this.myForm.value));
    }



    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.