From 6afe5f0a08a4dcd0594eac80428b9a4f302475d7 Mon Sep 17 00:00:00 2001 From: Zaytsev Kirill Date: Sat, 22 May 2021 22:15:47 +0300 Subject: [PATCH] refactor: Got rid of error observer to push errors into fields --- src/FormularioField.vue | 32 +++++++++--------- src/FormularioFieldGroup.vue | 9 ++--- src/FormularioForm.vue | 52 +++++++++------------------- src/FormularioFormRegistry.ts | 2 +- src/utils/types.ts | 4 ++- src/validation/ErrorObserver.ts | 60 --------------------------------- 6 files changed, 41 insertions(+), 118 deletions(-) delete mode 100644 src/validation/ErrorObserver.ts diff --git a/src/FormularioField.vue b/src/FormularioField.vue index 497c2b6..767d0d2 100644 --- a/src/FormularioField.vue +++ b/src/FormularioField.vue @@ -31,7 +31,7 @@ const VALIDATION_BEHAVIOR = { SUBMIT: 'submit', } -type Context = { +type FormularioFieldContext = { model: U; name: string; runValidation(): Promise; @@ -52,14 +52,14 @@ type Empty = null | undefined @Component({ name: 'FormularioField', inheritAttrs: false }) export default class FormularioField extends Vue { + @Inject({ default: '' }) __Formulario_path!: string @Inject({ default: undefined }) __FormularioForm_set!: Function|undefined @Inject({ default: () => (): void => {} }) __FormularioForm_emitValidation!: Function @Inject({ default: undefined }) __FormularioForm_register!: Function|undefined @Inject({ default: undefined }) __FormularioForm_unregister!: Function|undefined - @Inject({ default: () => (): Record => ({}) }) __FormularioForm_getValue!: () => Record - @Inject({ default: undefined }) __FormularioForm_addErrorObserver!: Function|undefined - @Inject({ default: undefined }) __FormularioForm_removeErrorObserver!: Function|undefined - @Inject({ default: '' }) path!: string + + @Inject({ default: () => (): Record => ({}) }) + __FormularioForm_getValue!: () => Record @Model('input', { default: '' }) value!: unknown @@ -90,8 +90,8 @@ export default class FormularioField extends Vue { private validationRun: Promise = Promise.resolve([]) - private get fullQualifiedName (): string { - return this.path !== '' ? `${this.path}.${this.name}` : this.name + public get fullQualifiedName (): string { + return this.__Formulario_path !== '' ? `${this.__Formulario_path}.${this.name}` : this.name } private get model (): unknown { @@ -113,7 +113,7 @@ export default class FormularioField extends Vue { } } - private get context (): Context { + private get context (): FormularioFieldContext { return Object.defineProperty({ name: this.fullQualifiedName, runValidation: this.runValidation.bind(this), @@ -175,18 +175,12 @@ export default class FormularioField extends Vue { if (typeof this.__FormularioForm_register === 'function') { this.__FormularioForm_register(this.fullQualifiedName, this) } - if (typeof this.__FormularioForm_addErrorObserver === 'function' && !this.errorsDisabled) { - this.__FormularioForm_addErrorObserver({ callback: this.setErrors, type: 'field', field: this.fullQualifiedName }) - } if (this.validationBehavior === VALIDATION_BEHAVIOR.LIVE) { this.runValidation() } } beforeDestroy (): void { - if (!this.errorsDisabled && typeof this.__FormularioForm_removeErrorObserver === 'function') { - this.__FormularioForm_removeErrorObserver(this.setErrors) - } if (typeof this.__FormularioForm_unregister === 'function') { this.__FormularioForm_unregister(this.fullQualifiedName) } @@ -245,10 +239,18 @@ export default class FormularioField extends Vue { }) } + /** + * @internal + */ setErrors (errors: string[]): void { - this.localErrors = arrayify(errors) + if (!this.errorsDisabled) { + this.localErrors = arrayify(errors) + } } + /** + * @internal + */ resetValidation (): void { this.localErrors = [] this.violations = [] diff --git a/src/FormularioFieldGroup.vue b/src/FormularioFieldGroup.vue index bd48408..f1fe108 100644 --- a/src/FormularioFieldGroup.vue +++ b/src/FormularioFieldGroup.vue @@ -15,7 +15,7 @@ import { @Component({ name: 'FormularioFieldGroup' }) export default class FormularioFieldGroup extends Vue { - @Inject({ default: '' }) path!: string + @Inject({ default: '' }) __Formulario_path!: string @Prop({ required: true }) readonly name!: string @@ -23,12 +23,13 @@ export default class FormularioFieldGroup extends Vue { @Prop({ default: false }) readonly isArrayItem!: boolean - @Provide('path') get groupPath (): string { + @Provide('__Formulario_path') + get groupPath (): string { if (this.isArrayItem) { - return `${this.path}[${this.name}]` + return `${this.__Formulario_path}[${this.name}]` } - if (this.path === '') { + if (this.__Formulario_path === '') { return this.name } diff --git a/src/FormularioForm.vue b/src/FormularioForm.vue index f8dd0f4..36ee368 100644 --- a/src/FormularioForm.vue +++ b/src/FormularioForm.vue @@ -26,12 +26,6 @@ import FormularioFormRegistry from '@/FormularioFormRegistry' import FormularioField from '@/FormularioField.vue' -import { - ErrorHandler, - ErrorObserver, - ErrorObserverRegistry, -} from '@/validation/ErrorObserver' - import { Violation } from '@/validation/validator' type ValidationEventPayload = { @@ -49,14 +43,10 @@ export default class FormularioForm extends Vue { // Form errors only used on FormularioForm default slot @Prop({ default: () => ([]) }) readonly formErrors!: string[] - @Provide() - public path = '' - public proxy: Record = {} private registry: FormularioFormRegistry = new FormularioFormRegistry(this) - private errorObserverRegistry = new ErrorObserverRegistry() // Local error messages are temporal, they wiped each resetValidation call private localFormErrors: string[] = [] private localFieldErrors: Record = {} @@ -87,27 +77,24 @@ export default class FormularioForm extends Vue { } @Watch('formularioValue', { deep: true }) - onFormularioValueChanged (values: Record): void { + onFormularioValueChange (values: Record): void { if (this.hasModel && values && typeof values === 'object') { this.setValues(values) } } - @Watch('mergedFormErrors') - onMergedFormErrorsChanged (errors: string[]): void { - this.errorObserverRegistry.filter(o => o.type === 'form').observe(errors) - } - @Watch('mergedFieldErrors', { deep: true, immediate: true }) - onMergedFieldErrorsChanged (errors: Record): void { - this.errorObserverRegistry.filter(o => o.type === 'field').observe(errors) + onMergedFieldErrorsChange (errors: Record): void { + this.registry.forEach((vm, path) => { + vm.setErrors(errors[path] || []) + }) } created (): void { this.initProxy() } - @Provide() + @Provide('__FormularioForm_getValue') getFormValues (): Record { return this.proxy } @@ -129,29 +116,20 @@ export default class FormularioForm extends Vue { this.$emit('validation', payload) } - @Provide('__FormularioForm_addErrorObserver') - addErrorObserver (observer: ErrorObserver): void { - this.errorObserverRegistry.add(observer) - if (observer.type === 'form') { - observer.callback(this.mergedFormErrors) - } else if (observer.field && has(this.mergedFieldErrors, observer.field)) { - observer.callback(this.mergedFieldErrors[observer.field]) - } - } - - @Provide('__FormularioForm_removeErrorObserver') - removeErrorObserver (observer: ErrorHandler): void { - this.errorObserverRegistry.remove(observer) - } - @Provide('__FormularioForm_register') - private register (field: string, component: FormularioField): void { - this.registry.add(field, component) + private register (field: string, vm: FormularioField): void { + this.registry.add(field, vm) + + if (has(this.mergedFieldErrors, field)) { + vm.setErrors(this.mergedFieldErrors[field] || []) + } } @Provide('__FormularioForm_unregister') private unregister (field: string): void { - this.registry.remove(field) + if (this.registry.has(field)) { + this.registry.remove(field) + } } initProxy (): void { diff --git a/src/FormularioFormRegistry.ts b/src/FormularioFormRegistry.ts index 7b7a968..574b9d4 100644 --- a/src/FormularioFormRegistry.ts +++ b/src/FormularioFormRegistry.ts @@ -115,7 +115,7 @@ export default class FormularioFormRegistry { /** * Iterate over the registry. */ - forEach (callback: Function): void { + forEach (callback: (component: FormularioField, field: string) => void): void { this.registry.forEach((component, field) => { callback(component, field) }) diff --git a/src/utils/types.ts b/src/utils/types.ts index 1f0647f..08a70ff 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -1,7 +1,9 @@ +export type Empty = undefined | null + export type RecordKey = string | number export type RecordLike = T[] | Record -export type Scalar = boolean | number | string | symbol | undefined | null +export type Scalar = boolean | number | string | symbol | Empty export function isScalar (value: unknown): boolean { switch (typeof value) { diff --git a/src/validation/ErrorObserver.ts b/src/validation/ErrorObserver.ts deleted file mode 100644 index 6a99625..0000000 --- a/src/validation/ErrorObserver.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { has } from '@/utils' - -export interface ErrorHandler { - (errors: Record | any[]): void; -} - -export interface ErrorObserver { - callback: ErrorHandler; - type: ErrorObserverType; - field?: string; -} - -export type ErrorObserverType = 'form' | 'field' - -export interface ErrorObserverPredicate { - (value: ErrorObserver, index: number, array: ErrorObserver[]): unknown; -} - -export class ErrorObserverRegistry { - private observers: ErrorObserver[] = [] - - constructor (observers: ErrorObserver[] = []) { - this.observers = observers - } - - public add (observer: ErrorObserver): void { - if (!this.observers.some(o => o.callback === observer.callback)) { - this.observers.push(observer) - } - } - - public remove (handler: ErrorHandler): void { - this.observers = this.observers.filter(o => o.callback !== handler) - } - - public filter (predicate: ErrorObserverPredicate): ErrorObserverRegistry { - return new ErrorObserverRegistry(this.observers.filter(predicate)) - } - - public some (predicate: ErrorObserverPredicate): boolean { - return this.observers.some(predicate) - } - - public observe (errors: Record|string[]): void { - this.observers.forEach(observer => { - if (observer.type === 'form') { - observer.callback(errors) - } else if ( - observer.field && - !Array.isArray(errors) - ) { - if (has(errors, observer.field)) { - observer.callback(errors[observer.field]) - } else { - observer.callback([]) - } - } - }) - } -}