/* eslint-disable no-useless-constructor */ import { Vue, } from 'vue/types/vue'; type AfterValidationCallback = (validationResult: boolean) => void; export class FatalValidationError extends Error {} export interface ValidationRule { validate(model: T): boolean; } export interface ValidatableComponent extends Vue { validateValue: () => T; } export class FieldValidator { private prevValidationResult: boolean = false; private afterValidation?: AfterValidationCallback; constructor( private required: boolean = false, ) { // pass } private rules: ValidationRule[] = []; public addRule(rule: ValidationRule): void { this.rules.push(rule); } public onAfterValidation(afterValidation: AfterValidationCallback) { this.afterValidation = afterValidation; } public addRules(rules: ValidationRule[]): void { this.rules.concat(rules); } public validate(model: T): boolean { if (!this.required && (model === undefined || model === null)) { return true; } if (!this.required && typeof model === 'string' && model === '') { return true; } this.prevValidationResult = this.rules.every((rule: ValidationRule) => rule.validate(model)); if (this.afterValidation) { this.afterValidation(this.prevValidationResult); } return this.prevValidationResult; } get isValid() { return this.prevValidationResult; } } export class ComponentValidator extends FieldValidator { component?: ValidatableComponent; public setComponent(component: ValidatableComponent): void { this.component = component; } public validate(): boolean { if (!this.component) { throw new FatalValidationError('Component not initialized'); } const model = this.component.validateValue(); return super.validate(model); } } export class NotEmptyRule implements ValidationRule { public validate(model: string): boolean { return model !== ''; } } export class NotEmptyArrayRule implements ValidationRule { public validate(model: any[]): boolean { return model.length > 0; } } export class LengthInRange implements ValidationRule { constructor( public min: number, public max: number, ) { // pass } public validate(model: string): boolean { return model.length >= this.min && model.length <= this.max; } } export class PassRegexpRule implements ValidationRule { constructor( private regexp: string, private flags: string = 'gu', ) { // pass } public validate(model: string): boolean { const regexp = new RegExp(this.regexp, this.flags); return regexp.test(model); } } export class FormValidator { private validators: ComponentValidator[] = []; public addValidator(validator: ComponentValidator): void { this.validators.push(validator); } public addValidators(validators: ComponentValidator[]): void { this.validators.concat(validators); } public isValid() { return this.validators.every((validator: ComponentValidator) => validator.isValid); } public validate(forceAll: boolean = false) { if (!forceAll) { return this.validators.every((value: ComponentValidator) => value.validate()); } let valid = true; this.validators.forEach((validator: ComponentValidator) => { if (!validator.validate()) { valid = false; } }); return valid; } }