Skip to content

Commit d9382b0

Browse files
committed
validation directive
1 parent a7698bf commit d9382b0

10 files changed

+293
-0
lines changed

download.png

28.7 KB
Loading

src/app/api.service.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { TestBed, inject } from '@angular/core/testing';
2+
3+
import { ApiService } from './api.service';
4+
5+
describe('ApiService', () => {
6+
beforeEach(() => {
7+
TestBed.configureTestingModule({
8+
providers: [ApiService]
9+
});
10+
});
11+
12+
it('should be created', inject([ApiService], (service: ApiService) => {
13+
expect(service).toBeTruthy();
14+
}));
15+
});

src/app/api.service.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
3+
4+
@Injectable({
5+
providedIn: 'root'
6+
})
7+
export class ApiService {
8+
9+
constructor(
10+
private http: HttpClient
11+
) { }
12+
13+
/**
14+
* Get Form validation error message
15+
*/
16+
getValidationErrorMessage() {
17+
// tslint:disable-next-line:no-shadowed-variable
18+
const promise = new Promise((resolve, reject) => {
19+
const apiURL = 'http://localhost:4201/assets/validationerrors.json';
20+
return this.http.get<{vlderrors: any}>(apiURL).toPromise().then(
21+
res => {
22+
resolve(res);
23+
},
24+
msg => {
25+
reject(msg);
26+
}
27+
);
28+
});
29+
return promise;
30+
}
31+
32+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.registerBox {
2+
background: #FFF;
3+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .08);
4+
border: 1px solid #eaeaea;
5+
margin-top: 30px;
6+
padding: 30px;
7+
}
8+
.registerBox h3{
9+
margin-bottom: 30px;
10+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<div class="container" *ngIf="!isLoading">
2+
<div class="col-xl-6 offset-xl-3 col-md-10 offset-md-1 registerBox">
3+
<h3 class="text-center">Register</h3>
4+
<form [formGroup]="registerForm" (ngSubmit)="verifyForm()">
5+
<div class="row form-group clearfix">
6+
<div class="col-md-4 col-12">
7+
<label for="firstName">First Name</label>
8+
<input type="text" appValidationLabel formControlName="firstName" class="form-control" />
9+
</div>
10+
<div class="col-md-4 col-12">
11+
<label for="middleName">Middle Name</label>
12+
<input type="text" appValidationLabel formControlName="middleName" class="form-control" />
13+
</div>
14+
<div class="col-md-4 col-12">
15+
<label for="lastName">Last Name</label>
16+
<input type="text" appValidationLabel formControlName="lastName" class="form-control" />
17+
</div>
18+
</div>
19+
<div class="row form-group clearfix">
20+
<label for="emailId" class="col-md-4 col-sm-5 col-12">Email ID</label>
21+
<div class="col-md-8 col-sm-7 col-12">
22+
<input type="text" appValidationLabel formControlName="emailId" class="form-control" />
23+
</div>
24+
</div>
25+
<div class="row form-group clearfix">
26+
<label for="mobile" class="col-md-4 col-sm-5 col-12">Mobile No</label>
27+
<div class="col-md-8 col-sm-7 col-12">
28+
<input type="text" appValidationLabel formControlName="mobile" class="form-control" />
29+
</div>
30+
</div>
31+
<div class="row form-group clearfix">
32+
<label for="umail" class="col-md-4 col-sm-5 col-12">Password</label>
33+
<div class="col-md-8 col-sm-7 col-12">
34+
<input type="password" appValidationLabel formControlName="password" class="form-control" />
35+
</div>
36+
</div>
37+
<div class="row form-group clearfix">
38+
<div class="col-12 text-center">
39+
<button class="btn btn-primary" type="submit" appFormSubmit>Register With Us</button>
40+
</div>
41+
</div>
42+
</form>
43+
</div>
44+
</div>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { RegisterComponent } from './register.component';
4+
5+
describe('RegisterComponent', () => {
6+
let component: RegisterComponent;
7+
let fixture: ComponentFixture<RegisterComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ RegisterComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(RegisterComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Component, OnInit, ElementRef } from '@angular/core';
2+
import { ApiService } from '../api.service';
3+
import { ValidationMessageService } from '../validation-msg.service';
4+
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
5+
6+
@Component({
7+
selector: 'app-register',
8+
templateUrl: './register.component.html',
9+
styleUrls: ['./register.component.css']
10+
})
11+
export class RegisterComponent implements OnInit {
12+
isLoading: Boolean = true;
13+
registerForm: FormGroup;
14+
15+
constructor(
16+
private formBuilder: FormBuilder,
17+
private apiService: ApiService,
18+
private validErrorMsgService: ValidationMessageService,
19+
private el: ElementRef
20+
) { }
21+
22+
ngOnInit() {
23+
this.createForm();
24+
this.validationErrorMsg();
25+
}
26+
27+
createForm() {
28+
this.registerForm = this.formBuilder.group({
29+
firstName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(30)]],
30+
middleName: ['', [Validators.minLength(2), Validators.maxLength(30)]],
31+
lastName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(30)]],
32+
emailId: ['', [Validators.required, Validators.email]],
33+
mobile: ['', [Validators.required, Validators.pattern('^[0-9]*$'), Validators.minLength(10), , Validators.maxLength(10)]],
34+
password: ['', [Validators.required, Validators.minLength(6)]]
35+
});
36+
}
37+
38+
verifyForm() {
39+
const invalidElements = this.el.nativeElement.querySelectorAll('.form-control.ng-invalid');
40+
if (invalidElements.length > 0) {
41+
invalidElements[0].focus();
42+
} else {
43+
console.log('Registration details => ', this.registerForm.value);
44+
}
45+
}
46+
47+
/*
48+
*** Get API response as validation error json and load response in validationErrorObj of validErrorMsgService
49+
*/
50+
validationErrorMsg() {
51+
this.apiService.getValidationErrorMessage().then(
52+
(res) => {
53+
if (this.validErrorMsgService.validationErrorObj.length === 0) {
54+
this.validErrorMsgService.validationErrorObj = res['validationErrors'];
55+
console.log('Validation Error => ', this.validErrorMsgService.validationErrorObj);
56+
this.isLoading = false;
57+
}
58+
}, (error) => {
59+
console.log(error);
60+
this.isLoading = false;
61+
});
62+
}
63+
64+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Directive, Input, HostListener, ElementRef, OnInit, OnDestroy } from '@angular/core';
2+
import { NgControl, ValidationErrors } from '@angular/forms';
3+
import { Subscription } from 'rxjs';
4+
import { ValidationMessageService } from './validation-msg.service';
5+
6+
@Directive({
7+
selector: '[appValidationLabel]'
8+
})
9+
export class ValidationLabelDirective implements OnInit {
10+
11+
constructor(private elRef: ElementRef,
12+
private control: NgControl,
13+
private validationMsgService: ValidationMessageService
14+
) { }
15+
16+
@Input('formControlName') formControlName: string;
17+
errorSpanId = '';
18+
statusChangeSubscription: Subscription;
19+
20+
ngOnInit(): void {
21+
this.errorSpanId = this.formControlName + '-error';
22+
this.statusChangeSubscription = this.control.statusChanges.subscribe(
23+
(status) => {
24+
if (status === 'INVALID') {
25+
this.showError();
26+
} else {
27+
this.removeError();
28+
}
29+
}
30+
);
31+
}
32+
33+
@HostListener('blur', ['$event'])
34+
handleBlurEvent(event) {
35+
// This is needed to handle the case of clicking a required field and moving out.
36+
// Rest all are handled by status change subscription
37+
console.log(event);
38+
if (this.control.value === null || this.control.value === '') {
39+
if (this.control.errors) {
40+
this.showError();
41+
} else {
42+
this.removeError();
43+
}
44+
}
45+
}
46+
47+
private showError() {
48+
this.removeError();
49+
const valErrors: ValidationErrors = this.control.errors;
50+
const firstKey = Object.keys(valErrors)[0];
51+
const errorMsgKey = this.formControlName + '-' + firstKey;
52+
const errorMsg = this.validationMsgService.getValidationMsg(errorMsgKey);
53+
const errSpan = '<span style="color:red;" id="' + this.errorSpanId + '">' + errorMsg + '</span>';
54+
this.elRef.nativeElement.parentElement.insertAdjacentHTML('beforeend', errSpan);
55+
this.elRef.nativeElement.classList.add('is-invalid');
56+
}
57+
58+
private removeError(): void {
59+
const errorElement = document.getElementById(this.errorSpanId);
60+
if (errorElement) {
61+
this.elRef.nativeElement.classList.remove('is-invalid');
62+
errorElement.remove();
63+
}
64+
}
65+
66+
}

src/app/validation-msg.service.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Injectable } from '@angular/core';
2+
3+
@Injectable({
4+
providedIn: 'root'
5+
})
6+
7+
export class ValidationMessageService {
8+
9+
validationErrorObj = [];
10+
11+
public getValidationMsg(validationId: string): string {
12+
return this.validationErrorObj[validationId];
13+
}
14+
15+
}

src/assets/validationerrors.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"validationErrors": {
3+
"firstName-required" : "Firstname is a required field",
4+
"firstName-minlength" : "Firstname must have 2 characters",
5+
"firstName-maxlength" : "Firstname can have maximum 30 characters",
6+
7+
"lastName-required" : "Lastname is a required field",
8+
"lastName-minlength" : "Lastname must have 2 characters",
9+
"lastName-maxlength" : "Lastname can have maximum 30 characters",
10+
11+
"emailId-required": "Email is a required field",
12+
"emailId-email": "Email is not in valid format",
13+
"emailId-minlength" : "Email must have 6 characters",
14+
15+
"mobile-required" : "Mobile is a required field",
16+
"mobile-minlength" : "Mobile must have 10 characters",
17+
18+
"password-required" : "Password is a required field",
19+
"password-minlength" : "Password must have 6 characters",
20+
"password-maxlength" : "Password can have maximum 20 characters"
21+
}
22+
}

0 commit comments

Comments
 (0)