skip to Main Content

I have been working an app with Angular 15. I use a hand-coded JSON and JSON server to perform CRUD operations on a "employees" JSON.

I run into a problem while working on the validation of the form intended to add a new employee.

In employee-form.component.ts I have:

import { Component } from '@angular/core';
import { Employee } from '../../models/empModel';
import { NgForm, FormGroup, Validator } from '@angular/forms';
import { HttpErrorResponse } from '@angular/common/http';
import { EmployeeService } from '../../services/employee.service';
import { EmployeeListComponent } from '../employee-list/employee-list.component';


@Component({
  selector: 'app-employee-form',
  templateUrl: './employee-form.component.html',
  styleUrls: ['./employee-form.component.scss']
})

export class EmployeeFormComponent {

  constructor(private employeeService: EmployeeService, private employeeListComponent: EmployeeListComponent) {
    this.deptno = -1;
  }

  public empsArray: Employee[] = [];
  public newEmployee: any = {}
  public empno: number = 0;
  public deptno: number = 0;
  public firstname: string = '';
  public lastname: string = '';
  public gender: string = '';
  public avatar: string = '';
  public job: string = '';
  public bio: string = '';
  public skills: string = '';
  public isSuccess: boolean = false;

  public pickAvatar(event: any) {
    let file = event.target.files[0];
    this.avatar = file.name;
  }

  public setAvatar() {
    this.newEmployee.avatar = this.avatar.length ? this.avatar : `${this.gender}.png`;
  }

  public doSkillsArray() {
    this.newEmployee.skills = this.skills.split(',');
  }

  public addEmployee(form: NgForm) {

    this.newEmployee = {
      empno: this.empno,
      deptno: this.deptno,
      firstname: this.firstname,
      lastname: this.lastname,
      gender: this.gender,
      avatar: this.avatar,
      job: this.job,
      bio: this.bio,
      skills: this.skills
    };

    this.setAvatar();

    this.doSkillsArray();

    this.employeeService.addEmployee(this.newEmployee).subscribe(
      (_response: Employee) => {
        // Show success alert
        this.isSuccess = true;

        // Render the employee list after adding a new employee
        this.employeeListComponent.getEmployees();
      },
      (error: HttpErrorResponse) => {
        console.log(error.message);
      }
    );
  }
}

In employee-form.component.html I have:

<form class="modal-content" #employeeForm="ngForm" (ngSubmit)="addEmployee(employeeForm)">
    <!-- Modal Header -->
    <div class="modal-header py-2">
      <h4 class="modal-title">New employee</h4>
      <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
    </div>

    <!-- Modal body -->
    <div class="modal-body py-2">
      <div *ngIf="isSuccess" class="text-center alert alert-success">
        Employee added successfully!
      </div>

      <div class="position-relative mb-1">
          <label class="form-label" for="firstName">First name</label>
          <input type="text" class="form-control" name="firstname" id="firstName" placeholder="First name" [(ngModel)]="firstname" #first_name="ngModel" required/>
          <div *ngIf="first_name.touched && first_name.errors?.['required']"  class="invalid-feedback">First name is required.</div>
      </div>

      <div class="position-relative mb-1">
          <label class="form-label" for="lastName">Last name</label>
          <input type="text" class="form-control" name="lastname" id="lastName" placeholder="Last name" [(ngModel)]="lastname" #last_name="ngModel" required />
          <div *ngIf="last_name.touched && last_name.errors?.['required']"  class="invalid-feedback">Last name is required.</div>
      </div>

      <div class="position-relative mb-1">
        <label class="form-label d-block" for="avatar">Photo</label>
        <input type="file" class="file-upload-btn" name="avatar" id="avatar" [(ngModel)]="avatar" (change)="pickAvatar($event)">
      </div>

      <div class="position-relative mb-1">
          <label class="form-label" for="job">Job</label>
          <input type="text" class="form-control" name="job" id="job" placeholder="Job" [(ngModel)]="job" #job_title="ngModel" required  />
          <div *ngIf="job_title.touched && job_title.errors?.['required']" class="invalid-feedback">Job is required.</div>
      </div>

      <div class="position-relative mb-1">
        <label class="form-label" for="job">Skills</label>
        <input type="text" class="form-control" name="skills" id="skills" placeholder="Skills separated by comma" [(ngModel)]="skills"/>
    </div>

      <div class="position-relative mb-1">
        <label class="form-label" for="department">Department</label>
        <select class="form-select" name="department" id="department" [(ngModel)]="deptno" #emp_department="ngModel" required>
          <option selected value="-1">Pick a department</option>
          <option value="10">Management</option>
          <option value="20">Sales</option>
          <option value="30">Software Engineering</option>
          <option value="40">Finance</option>
        </select>
        <div *ngIf="emp_department.touched && emp_department.value == -1" class="invalid-feedback">You must pick a department</div>
      </div>

      <div class="position-relative mb-0">
          <label class="form-label d-block">Gender</label>
          <div class="form-check form-check-inline">
              <input type="radio" class="form-check-input" name="gender" id="male" value="male" [(ngModel)]="gender" #emp_gender="ngModel" required />
              <label class="form-label" for="male">male</label>
          </div>
          <div class="form-check form-check-inline">
              <input type="radio" class="form-check-input" name="gender" id="femele" value="femele" [(ngModel)]="gender" #emp_gender="ngModel" required />
              <label class="form-label" for="femele">femele</label>
          </div>
          <div *ngIf="emp_gender.touched && emp_gender.errors?.['required']" class="invalid-feedback">You must pick a gender</div>
      </div>

      <div class="position-relative mb-1">
          <label class="form-label" for="bio">Bio</label>
          <textarea class="form-control" name="bio" id="bio" type="text" placeholder="Bio" [(ngModel)]="bio"></textarea>
      </div>
    </div>

    <!-- Modal footer -->
    <div class="modal-footer py-2">
      <button type="submit" class="btn btn-success" [disabled]="!employeeForm.valid">Add employee</button>
      <button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
    </div>
</form>

The problem

Even though the gender is required and the submit button remains disabled until a gender is selected, the error message "You must pick a gender" is never visible.

Questions

  1. What am I doing wrong?
  2. Alternatively, how can select a gender by default?

2

Answers


  1. Apparently, You are adding a condition "emp_gender.touched" which will restrict validations until and unless it’s been interacted with by the user.

    <div class="form-check form-check-inline">
                  <input type="radio" class="form-check-input" name="gender" id="femele" value="femele" [(ngModel)]="gender" #emp_gender="ngModel" required />
                  <label class="form-label" for="femele">femele</label>
              </div>
              <div *ngIf="emp_gender.errors?.['required']" class="invalid-feedback">You must pick a gender</div>
    

    Example: https://stackblitz.com/edit/angular-e1jup8?file=src%2Fapp%2Fapp.component.html You can refer to this to understand how touched works

    Login or Signup to reply.
  2. In general, we show an error when a input has error and when is touched. So, in submit we need check if the form is valid, else mark the controls as touched

    So, our submit should be like

      onSubmit(f:NgForm)
      {
        if (f.valid)
          ..do something...
        else
           f.form.markAllAsTouched()
      }
    

    In old versions of Angular we need loop over the controls of the form

    Object.keys(f.controls).forEach(x=>{
      f.controls[x].markAsTouched()
    })
    

    }

    NOTE: If we are using ReactiveForms, we pass to the fucntion the FormGroup and simply

      onSubmit(f:FormGroup)
      {
        if (f.valid)
          ..do something...
        else
           f.markAllAsTouched()
      }
    

    NOTE2: Instead of check if touched and error we can take an aproach based in .css.

    Imagine a .html like

      <input name="first" ngModel required #first="ngModel">
      <div class="error">Error first</div>
    
      <input name="last" ngModel required>
      <div class="error">Error last</div>
    

    We can use

    input:not(.ng-invalid.ng-touched) + .error
    {
      display:none;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search