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
- What am I doing wrong?
- Alternatively, how can select a gender by default?
2
Answers
Apparently, You are adding a condition "emp_gender.touched" which will restrict validations until and unless it’s been interacted with by the user.
Example: https://stackblitz.com/edit/angular-e1jup8?file=src%2Fapp%2Fapp.component.html You can refer to this to understand how touched works
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
In old versions of Angular we need loop over the controls of the form
}
NOTE: If we are using ReactiveForms, we pass to the fucntion the FormGroup and simply
NOTE2: Instead of check if touched and error we can take an aproach based in .css.
Imagine a .html like
We can use