Skip to content

Instantly share code, notes, and snippets.

@im4aLL
Created July 25, 2018 12:33
Show Gist options
  • Select an option

  • Save im4aLL/9d1c6b8e824dff187789cfff240197ae to your computer and use it in GitHub Desktop.

Select an option

Save im4aLL/9d1c6b8e824dff187789cfff240197ae to your computer and use it in GitHub Desktop.
angular form custom validation
interface FieldInterface {
name: string;
rules: string[];
}
interface ErrorFieldInterface {
name: string;
message?: string;
}
export class Validator {
private rules: object;
private fieldArray: FieldInterface[] = [];
private data: object;
private errors: ErrorFieldInterface[] = [];
private message: object;
private showingError: boolean;
private showSingleError: boolean;
constructor(rules: object, message?: object) {
this.rules = rules;
this.message = message || {};
}
passed(data: object): boolean {
this.errors.length = 0;
this.fieldArray.length = 0;
this.data = data;
this.validate();
return this.errors.length === 0;
}
checkForErrors(data: object): void {
this.passed(data);
if (this.showingError) {
this.showErrors();
}
}
showOnlyOneError() {
this.showSingleError = true;
}
validate() {
this.fieldArray = this.getRuleArray(this.rules);
if (this.fieldArray.length === 0) {
return false;
}
for (const field of this.fieldArray) {
this.check(field);
}
}
private check(field: FieldInterface) {
if (field.rules.length === 0) {
return false;
}
for (const rule of field.rules) {
if (rule === 'required') {
this.validateForRequired(field);
} else if (rule === 'email') {
this.validateForEmail(field);
} else if (rule.includes('min')) {
this.validateForMinLength(field, rule);
}
}
}
private resetErrors() {
Object.keys(this.data).forEach(name => document.querySelector(`[name="${name}"]`).classList.remove('is-invalid'));
Array.from(document.querySelectorAll('.invalid-feedback')).forEach(element => {
element.parentNode.removeChild(element);
});
}
showErrors() {
this.showingError = true;
this.resetErrors();
const obj = {};
for (const error of this.errors) {
if (!obj.hasOwnProperty(error.name)) {
obj[error.name] = [];
}
obj[error.name].push(error.message);
}
for (const fieldName of Object.keys(obj)) {
const dom = document.querySelector(`[name="${fieldName}"]`);
dom.classList.add('is-invalid');
const errorDom = document.createElement('div');
errorDom.classList.add('invalid-feedback');
if (this.showSingleError) {
errorDom.innerHTML = `<div>${obj[fieldName][0]}</div>`;
} else {
obj[fieldName].forEach(msg => errorDom.innerHTML += `<div>${msg}</div>`);
}
this.insertAfter(errorDom, dom);
}
}
private insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
getErrors() {
return this.errors;
}
private getRuleArray(rules): FieldInterface[] {
for (const rule of Object.keys(rules)) {
if (rules.hasOwnProperty(rule)) {
const ruleObject: FieldInterface = {
name: null,
rules: null
};
ruleObject.name = rule;
ruleObject.rules = this.parseRuleString(rules[rule]);
this.fieldArray.push(ruleObject);
}
}
return this.fieldArray;
}
private parseRuleString(str: string) {
let arr = [];
if (str.includes('|')) {
arr = [...str.split('|')];
} else {
arr.push(str);
}
return arr;
}
private validateForRequired(field: FieldInterface) {
const value = this.data[field.name].trim();
let message = null;
if (this.message.hasOwnProperty(field.name) && this.message[field.name].required !== undefined) {
message = this.message[field.name].required;
} else {
message = `This field is required`; // ${this.capitalizeFirstLetter(field.name)}
}
if (value.length === 0) {
this.errors.push({ name: field.name, message: message });
}
}
private capitalizeFirstLetter(string): string {
return string.charAt(0).toUpperCase() + string.slice(1);
}
private validateForEmail(field: FieldInterface) {
const value = this.data[field.name].trim();
let message = null;
if (this.message.hasOwnProperty(field.name) && this.message[field.name].email !== undefined) {
message = this.message[field.name].email;
} else {
message = `Must be valid email address`;
}
if (this.validateEmail(value) === false) {
this.errors.push({ name: field.name, message: message });
}
}
private validateEmail(email): boolean {
// tslint:disable-next-line:max-line-length
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
private validateForMinLength(field: FieldInterface, rule: string) {
const minArray = rule.split(':');
if (minArray.length < 2) {
return false;
}
const value = this.data[field.name].trim();
const requiredLength: number = parseInt(minArray[1], 16);
let message = null;
if (this.message.hasOwnProperty(field.name) && this.message[field.name].min !== undefined) {
message = this.message[field.name].min;
} else {
message = `${this.capitalizeFirstLetter(field.name)} must be minimum ${requiredLength} characters`;
}
if (value.length < requiredLength) {
this.errors.push({ name: field.name, message: message });
}
}
}
/*
HTML
===========================
<section class="login__container">
<div class="login">
<h4 class="login__headline">Scribe Login</h4>
<form (ngSubmit)="onFormSubmit()" (change)="onFormChange()">
<div class="form-group">
<input type="email" class="form-control" placeholder="Enter email" [(ngModel)]="model.email" name="email">
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Password" [(ngModel)]="model.password" name="password">
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="Enter OTP" [(ngModel)]="model.otp" name="otp">
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="accessType" id="liveProviders" value="liveProviders" [(ngModel)]="model.accessType">
<label class="form-check-label" for="liveProviders">Access live providers</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="accessType" id="trainingVideos" value="trainingVideos" [(ngModel)]="model.accessType">
<label class="form-check-label" for="trainingVideos">Access training videos</label>
</div>
<div class="login__actions">
<button type="submit" class="btn btn-primary">Login</button>
<a href="#" class="login__reset__pass">Forgot Password?</a>
</div>
</form>
</div>
</section>
Component
===========================
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../services/auth.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Validator } from '../../helper/Validator';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
public form: FormGroup;
public isSubmitted: boolean;
model = {
email: '',
password: '',
otp: '',
accessType: 'trainingVideos'
};
modelRules = {
email: 'required|email',
password: 'required|min:4',
otp: 'required'
};
validator: Validator;
constructor(
private authService: AuthService,
private formBuilder: FormBuilder,
private router: Router
) { }
ngOnInit() {
// this.authService.redirectIfLoggedIn();
this.isSubmitted = false;
this.validator = new Validator(this.modelRules);
this.validator.showOnlyOneError();
this.rules();
}
rules() {
this.form = this.formBuilder.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(4)]],
otp: [''],
accessType: ['liveProviders', [Validators.required]]
});
}
get f() {
return this.form.controls;
}
onSubmit() {
console.log(this.form);
this.isSubmitted = true;
if (this.form.invalid) {
return;
}
// Considering form is valid
this.router.navigate(['dashboard']);
}
onFormSubmit() {
if (this.validator.passed(this.model)) {
console.log(this.model);
} else {
console.log(this.validator.getErrors());
this.validator.showErrors();
}
}
onFormChange() {
this.validator.checkForErrors(this.model);
}
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment