Created
July 25, 2018 12:33
-
-
Save im4aLL/9d1c6b8e824dff187789cfff240197ae to your computer and use it in GitHub Desktop.
angular form custom validation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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