Ashish Raut
Ashish Raut

Reputation: 71

How to get form control validation errors in template in Angular

I have a form which changes its validation based on some value from the dropdown. If college A is selected then min percentage required in 60, if college B is selected then min percentage drops down to 55. Although I have figured out a way to get the min error in the template in so that I didn't have to hard code the percentage value in my template. Although I am not sure if this is the correct way or not.

<div formGroupName="marks">
  <div class="form-group">
    <label for="email" class="col-lg-3 control-label">Semester 1</label>
    <div class="col-lg-3">
      <input autocomplete="off"
             #sem1
             type="text"
             min="0"
             max="99"
             maxlength="1"
             maxlength="2"
             class="form-control"
             id="email"
             placeholder="Sem 1 (%)"
             (keypress)="onlyNumberKey($event)"
             formControlName="sem1">
      <span *ngIf="registrationForm.controls['marks'].controls['sem1'].hasError('required') &&
                   registrationForm.controls['marks'].controls['sem1'].touched"
                   class="text-danger">
        Sem 1 percentage required
      </span>
      <span *ngIf="registrationForm.controls['marks'].controls['sem1'].hasError('min') ||
                   registrationForm.controls['marks'].controls['sem1'].hasError('max') ||
                   registrationForm.controls['marks'].controls['sem1'].hasError('minlength')||
                   registrationForm.controls['marks'].controls['sem1'].hasError('maxlength') &&
                   registrationForm.controls['marks'].controls['sem1'].touched"
                   class="text-danger">
        Percenatge must be {{ registrationForm.get('marks.sem1')._errors.min.min }}  and above.
      </span>
    </div>
  </div>
</div>

Component

registrationForm = new FormGroup({
  college: new FormControl('', [
    Validators.required, dropDrownValidator
  ]),
  marks: new FormGroup({
    sem1: new FormControl('',
      [
        Validators.required,
        Validators.min(60),
        Validators.max(99),
        Validators.minLength(1),
        Validators.maxLength(2)
      ]
    )
  })
});

Upvotes: 6

Views: 18413

Answers (1)

developer033
developer033

Reputation: 24874

To achieve what you want, you have to watch the changes in your college control.

You can do this using (change) in template:

<select formControlName="college" (change)="handleChange($event.target.value)">
  ...
</select>

or even using valueChanges in component:

this.registrationForm.get('college').valueChanges.subscribe(college => this.handleChange(college));

And in your function, based on what college user selects, you can set the min value to your validator using AbstractControl#setValidators + AbstractControl#updateValueAndValidity (to apply ), as below:

handleChange(selectedCollege: string): void {
  const min = selectedCollege === 'CollegeA' ? 60 : 55;
  const control = this.registrationForm.get('marks.sem1');
  control.setValidators([Validators.required, customMin(min)]);
  control.updateValueAndValidity();
}

Tips:

1 - Since, you're using model-driven forms you shouldn't put validations in your HTML, keep it clean and put everything in component.

2 - You can use FormBuilder in order to simplify the construction of form (see the demo at the bottom).

3 - You should never access private properties like this:

{{ registrationForm.get('marks.sem1')._errors.min.min }}

Just use errors instead of _errors if you need to.

4 - You can simplify even more your markup:

Instead of:

<span *ngIf="registrationForm.controls['marks'].controls['sem1'].hasError('required') &&
             registrationForm.controls['marks'].controls['sem1'].touched" 
             class="text-danger">
  Sem 1 percentage required
</span>
<span *ngIf="registrationForm.controls['marks'].controls['sem1'].hasError('min') ||
             registrationForm.controls['marks'].controls['sem1'].hasError('max') ||
             registrationForm.controls['marks'].controls['sem1'].hasError('minlength')||
             registrationForm.controls['marks'].controls['sem1'].hasError('maxlength') &&
             registrationForm.controls['marks'].controls['sem1'].touched"
             class="text-danger">
  Percenatge must be {{ registrationForm.get('marks.sem1')._errors.min.min }}  and above.
</span>

You can have:

<div class="text-danger" *ngIf="registrationForm.get('marks.sem1').touched">
  <span *ngIf="registrationForm.hasError('required', 'marks.sem1')">
    Sem 1 percentage required
  </span>
  <span *ngIf="registrationForm.getError('customMin', 'marks.sem1') as error">
    Percentage must be greater than or equal to {{ error.min }}.
  </span>
</div>

WORKING DEMO

Upvotes: 7

Related Questions