refactor: Got rid of error observer to push errors into fields
This commit is contained in:
parent
7aca27a010
commit
6afe5f0a08
@ -31,7 +31,7 @@ const VALIDATION_BEHAVIOR = {
|
||||
SUBMIT: 'submit',
|
||||
}
|
||||
|
||||
type Context<U> = {
|
||||
type FormularioFieldContext<U> = {
|
||||
model: U;
|
||||
name: string;
|
||||
runValidation(): Promise<Violation[]>;
|
||||
@ -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<string, unknown> => ({}) }) __FormularioForm_getValue!: () => Record<string, unknown>
|
||||
@Inject({ default: undefined }) __FormularioForm_addErrorObserver!: Function|undefined
|
||||
@Inject({ default: undefined }) __FormularioForm_removeErrorObserver!: Function|undefined
|
||||
@Inject({ default: '' }) path!: string
|
||||
|
||||
@Inject({ default: () => (): Record<string, unknown> => ({}) })
|
||||
__FormularioForm_getValue!: () => Record<string, unknown>
|
||||
|
||||
@Model('input', { default: '' }) value!: unknown
|
||||
|
||||
@ -90,8 +90,8 @@ export default class FormularioField extends Vue {
|
||||
|
||||
private validationRun: Promise<Violation[]> = 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<unknown> {
|
||||
private get context (): FormularioFieldContext<unknown> {
|
||||
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 {
|
||||
if (!this.errorsDisabled) {
|
||||
this.localErrors = arrayify(errors)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
resetValidation (): void {
|
||||
this.localErrors = []
|
||||
this.violations = []
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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<string, unknown> = {}
|
||||
|
||||
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<string, string[]> = {}
|
||||
@ -87,27 +77,24 @@ export default class FormularioForm extends Vue {
|
||||
}
|
||||
|
||||
@Watch('formularioValue', { deep: true })
|
||||
onFormularioValueChanged (values: Record<string, unknown>): void {
|
||||
onFormularioValueChange (values: Record<string, unknown>): 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<string, string[]>): void {
|
||||
this.errorObserverRegistry.filter(o => o.type === 'field').observe(errors)
|
||||
onMergedFieldErrorsChange (errors: Record<string, string[]>): void {
|
||||
this.registry.forEach((vm, path) => {
|
||||
vm.setErrors(errors[path] || [])
|
||||
})
|
||||
}
|
||||
|
||||
created (): void {
|
||||
this.initProxy()
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Provide('__FormularioForm_getValue')
|
||||
getFormValues (): Record<string, unknown> {
|
||||
return this.proxy
|
||||
}
|
||||
@ -129,30 +116,21 @@ 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 {
|
||||
if (this.registry.has(field)) {
|
||||
this.registry.remove(field)
|
||||
}
|
||||
}
|
||||
|
||||
initProxy (): void {
|
||||
if (this.hasInitialValue) {
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -1,7 +1,9 @@
|
||||
export type Empty = undefined | null
|
||||
|
||||
export type RecordKey = string | number
|
||||
export type RecordLike<T> = T[] | Record<RecordKey, T>
|
||||
|
||||
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) {
|
||||
|
@ -1,60 +0,0 @@
|
||||
import { has } from '@/utils'
|
||||
|
||||
export interface ErrorHandler {
|
||||
(errors: Record<string, any> | 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, string[]>|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([])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user