Suggested Videos
Part 10 - Angular form control valuechanges | Text | Slides
Part 11 - Loop through all form controls in formgroup in reactive form | Text | Slides
Part 12 - Move validation messages to the component class in reactive form | Text | Slides
In this video we will discuss, how to move the logic to show and hide validation messages from the view template into the component class. This is continuation to Part 12 where we discussed moving validation messages.
create-employee.component.html : Consider the following HTML in CreateEmployeeComponent view template. This HTML is for the "Full Name" field. The logic to add or remove has-error class is in the template. Also the validation message and the logic to show and hide it is also in the template at the moment.
Now, let's move all of this into the component class. Modify the HTML as shown below. Notice, now we are binding to formErrors.fullName property. All the complex logic is moved to the component class. Notice the HTML here is much less than what we have had before.
Changes in create-employee.component.ts file : The changes are commented and self-explanatory
The only problem at the moment is that when a control loses focus, our validation is not triggered. This is because valueChanges observable does not emit an event when the control loses focus. It only emits an event when the value changes.
One work around for this is to bind to the blur event and call validation function (logValidationErrors()) manually.
Here is the HTML for Email, Skill Name, Experience and Proficiency input elements.
Part 10 - Angular form control valuechanges | Text | Slides
Part 11 - Loop through all form controls in formgroup in reactive form | Text | Slides
Part 12 - Move validation messages to the component class in reactive form | Text | Slides
In this video we will discuss, how to move the logic to show and hide validation messages from the view template into the component class. This is continuation to Part 12 where we discussed moving validation messages.
create-employee.component.html : Consider the following HTML in CreateEmployeeComponent view template. This HTML is for the "Full Name" field. The logic to add or remove has-error class is in the template. Also the validation message and the logic to show and hide it is also in the template at the moment.
<div
class="form-group"
[ngClass]="{'has-error':
((employeeForm.get('fullName').touched ||
employeeForm.get('fullName').dirty) &&
employeeForm.get('fullName').errors)}">
<label class="col-sm-2
control-label" for="fullName">Full
Name</label>
<div class="col-sm-8">
<input id="fullName"
type="text"
class="form-control"
formControlName="fullName">
<span class="help-block"
*ngIf="((employeeForm.get('fullName').touched
||
employeeForm.get('fullName').dirty)
&&
employeeForm.get('fullName').errors)">
<span *ngIf="employeeForm.get('fullName').errors.required">
Full Name is required
</span>
<span *ngIf="employeeForm.get('fullName').errors.minlength
||
employeeForm.get('fullName').errors.maxlength">
Full Name must be greater than 2 characters and less than 10 characters
</span>
</span>
</div>
</div>
Now, let's move all of this into the component class. Modify the HTML as shown below. Notice, now we are binding to formErrors.fullName property. All the complex logic is moved to the component class. Notice the HTML here is much less than what we have had before.
<div
class="form-group"
[ngClass]="{'has-error':
formErrors.fullName}">
<label class="col-sm-2
control-label" for="fullName">Full
Name</label>
<div class="col-sm-8">
<input id="fullName"
type="text"
class="form-control"
formControlName="fullName">
<span class="help-block"
*ngIf="formErrors.fullName">
{{formErrors.fullName}}
</span>
</div>
</div>
Changes in create-employee.component.ts file : The changes are commented and self-explanatory
formErrors = {
'fullName': '',
'email': '',
'skillName': '',
'experienceInYears':
'',
'proficiency': ''
};
validationMessages = {
'fullName': {
'required': 'Full
Name is required.',
'minlength': 'Full
Name must be greater than 2 characters.',
'maxlength': 'Full
Name must be less than 2 characters.',
},
'email': {
'required': 'Email
is required.'
},
'skillName': {
'required': 'Skill
Name is required.',
},
'experienceInYears':
{
'required': 'Experience
is required.',
},
'proficiency': {
'required': 'Proficiency
is required.',
},
};
ngOnInit() {
this.employeeForm = this.fb.group({
fullName: ['',
[Validators.required, Validators.minLength(2),
Validators.maxLength(10)]],
email: ['',
Validators.required],
skills: this.fb.group({
skillName: ['',
Validators.required],
experienceInYears: ['',
Validators.required],
proficiency: ['',
Validators.required]
}),
});
// When any of the form control value in employee
form changes
// our validation function logValidationErrors() is
called
this.employeeForm.valueChanges.subscribe((data)
=> {
this.logValidationErrors(this.employeeForm);
});
}
logValidationErrors(group: FormGroup = this.employeeForm):
void {
Object.keys(group.controls).forEach((key: string) =>
{
const abstractControl = group.get(key);
if (abstractControl instanceof
FormGroup) {
this.logValidationErrors(abstractControl);
} else {
this.formErrors[key] = '';
if (abstractControl && !abstractControl.valid
&& (abstractControl.touched
|| abstractControl.dirty)) {
const messages = this.validationMessages[key];
for (const errorKey in
abstractControl.errors) {
if
(errorKey) {
this.formErrors[key]
+= messages[errorKey] + ' ';
}
}
}
}
});
}
The only problem at the moment is that when a control loses focus, our validation is not triggered. This is because valueChanges observable does not emit an event when the control loses focus. It only emits an event when the value changes.
One work around for this is to bind to the blur event and call validation function (logValidationErrors()) manually.
<input
id="fullName"
type="text"
class="form-control"
formControlName="fullName"
(blur)="logValidationErrors()">
Here is the HTML for Email, Skill Name, Experience and Proficiency input elements.
<div
class="form-group"
[ngClass]="{'has-error':
formErrors.email}">
<label class="col-sm-2
control-label" for="email">Email</label>
<div class="col-sm-8">
<input id="email"
type="text"
class="form-control"
formControlName="email"
(blur)="logValidationErrors()">
<span class="help-block"
*ngIf="formErrors.email">
{{formErrors.email}}
</span>
</div>
</div>
<div
class="well">
<div formGroupName="skills">
<div class="form-group"
[ngClass]="{'has-error':
formErrors.skillName}">
<label class="col-sm-2
control-label" for="skillName">
Skill
</label>
<div class="col-sm-4">
<input type="text"
class="form-control"
id="skillName"
formControlName="skillName"
(blur)="logValidationErrors()"
placeholder="C#, Java,
Angular etc...">
<span class="help-block"
*ngIf="formErrors.skillName">
{{formErrors.skillName}}
</span>
</div>
</div>
<div class="form-group"
[ngClass]="{'has-error':
formErrors.experienceInYears}">
<label class="col-sm-2
control-label" for="experienceInYears">
Experience
</label>
<div class="col-sm-4">
<input type="text"
class="form-control"
id="experienceInYears"
formControlName="experienceInYears"
placeholder="In Years"
(blur)="logValidationErrors()">
<span
class="help-block"
*ngIf="formErrors.experienceInYears">
{{formErrors.experienceInYears}}
</span>
</div>
</div>
<div class="form-group"
[ngClass]="{'has-error':
formErrors.proficiency}">
<label class="col-md-2
control-label">Proficiency</label>
<div class="col-md-8">
<label class="radio-inline">
<input
type="radio"
value="beginner"
formControlName="proficiency"
(blur)="logValidationErrors()">Beginner
</label>
<label class="radio-inline">
<input
type="radio"
value="intermediate"
formControlName="proficiency"
(blur)="logValidationErrors()">Intermediate
</label>
<label class="radio-inline">
<input
type="radio"
value="advanced"
formControlName="proficiency"
(blur)="logValidationErrors()">Advanced
</label>
<span class="help-block"
*ngIf="formErrors.experienceInYears">
{{formErrors.proficiency}}
</span>
</div>
</div>
</div>
</div>
Can anyone help me with my requirement.
ReplyDeleteif any of the input feilds are not valid in the form, Validations need to be fired after clicking submit button, it had to be achieved using formErrors of reactive forms.
You can add Click Event to that button and call the validation method inside that event.Dont call validation method inside value changes observable.
Deleteif you are following the series then you can remove the value changes observable code and call the error logging function in your function submit. that should solve.
ReplyDeletethis.formErrors[key] = ''; in this portion error is comimng
ReplyDeleteerror - Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ fullName: string; email: string; skillName: string; experienceInYears: string; proficiency: string; }'.
No index signature with a parameter of type 'string' was found on type '{ fullName: string; email: string; skillName: string; experienceInYears: string; proficiency
you can solve this issue by adding @ts-ignore comment above the line that raises this error.
DeleteExample based on the code:
// @ts-ignore
const messages = this.validationMessages[key];