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',
|
SUBMIT: 'submit',
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context<U> = {
|
type FormularioFieldContext<U> = {
|
||||||
model: U;
|
model: U;
|
||||||
name: string;
|
name: string;
|
||||||
runValidation(): Promise<Violation[]>;
|
runValidation(): Promise<Violation[]>;
|
||||||
@ -52,14 +52,14 @@ type Empty = null | undefined
|
|||||||
|
|
||||||
@Component({ name: 'FormularioField', inheritAttrs: false })
|
@Component({ name: 'FormularioField', inheritAttrs: false })
|
||||||
export default class FormularioField extends Vue {
|
export default class FormularioField extends Vue {
|
||||||
|
@Inject({ default: '' }) __Formulario_path!: string
|
||||||
@Inject({ default: undefined }) __FormularioForm_set!: Function|undefined
|
@Inject({ default: undefined }) __FormularioForm_set!: Function|undefined
|
||||||
@Inject({ default: () => (): void => {} }) __FormularioForm_emitValidation!: Function
|
@Inject({ default: () => (): void => {} }) __FormularioForm_emitValidation!: Function
|
||||||
@Inject({ default: undefined }) __FormularioForm_register!: Function|undefined
|
@Inject({ default: undefined }) __FormularioForm_register!: Function|undefined
|
||||||
@Inject({ default: undefined }) __FormularioForm_unregister!: 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: () => (): Record<string, unknown> => ({}) })
|
||||||
@Inject({ default: undefined }) __FormularioForm_removeErrorObserver!: Function|undefined
|
__FormularioForm_getValue!: () => Record<string, unknown>
|
||||||
@Inject({ default: '' }) path!: string
|
|
||||||
|
|
||||||
@Model('input', { default: '' }) value!: unknown
|
@Model('input', { default: '' }) value!: unknown
|
||||||
|
|
||||||
@ -90,8 +90,8 @@ export default class FormularioField extends Vue {
|
|||||||
|
|
||||||
private validationRun: Promise<Violation[]> = Promise.resolve([])
|
private validationRun: Promise<Violation[]> = Promise.resolve([])
|
||||||
|
|
||||||
private get fullQualifiedName (): string {
|
public get fullQualifiedName (): string {
|
||||||
return this.path !== '' ? `${this.path}.${this.name}` : this.name
|
return this.__Formulario_path !== '' ? `${this.__Formulario_path}.${this.name}` : this.name
|
||||||
}
|
}
|
||||||
|
|
||||||
private get model (): unknown {
|
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({
|
return Object.defineProperty({
|
||||||
name: this.fullQualifiedName,
|
name: this.fullQualifiedName,
|
||||||
runValidation: this.runValidation.bind(this),
|
runValidation: this.runValidation.bind(this),
|
||||||
@ -175,18 +175,12 @@ export default class FormularioField extends Vue {
|
|||||||
if (typeof this.__FormularioForm_register === 'function') {
|
if (typeof this.__FormularioForm_register === 'function') {
|
||||||
this.__FormularioForm_register(this.fullQualifiedName, this)
|
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) {
|
if (this.validationBehavior === VALIDATION_BEHAVIOR.LIVE) {
|
||||||
this.runValidation()
|
this.runValidation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeDestroy (): void {
|
beforeDestroy (): void {
|
||||||
if (!this.errorsDisabled && typeof this.__FormularioForm_removeErrorObserver === 'function') {
|
|
||||||
this.__FormularioForm_removeErrorObserver(this.setErrors)
|
|
||||||
}
|
|
||||||
if (typeof this.__FormularioForm_unregister === 'function') {
|
if (typeof this.__FormularioForm_unregister === 'function') {
|
||||||
this.__FormularioForm_unregister(this.fullQualifiedName)
|
this.__FormularioForm_unregister(this.fullQualifiedName)
|
||||||
}
|
}
|
||||||
@ -245,10 +239,18 @@ export default class FormularioField extends Vue {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
setErrors (errors: string[]): void {
|
setErrors (errors: string[]): void {
|
||||||
|
if (!this.errorsDisabled) {
|
||||||
this.localErrors = arrayify(errors)
|
this.localErrors = arrayify(errors)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
resetValidation (): void {
|
resetValidation (): void {
|
||||||
this.localErrors = []
|
this.localErrors = []
|
||||||
this.violations = []
|
this.violations = []
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
|
|
||||||
@Component({ name: 'FormularioFieldGroup' })
|
@Component({ name: 'FormularioFieldGroup' })
|
||||||
export default class FormularioFieldGroup extends Vue {
|
export default class FormularioFieldGroup extends Vue {
|
||||||
@Inject({ default: '' }) path!: string
|
@Inject({ default: '' }) __Formulario_path!: string
|
||||||
|
|
||||||
@Prop({ required: true })
|
@Prop({ required: true })
|
||||||
readonly name!: string
|
readonly name!: string
|
||||||
@ -23,12 +23,13 @@ export default class FormularioFieldGroup extends Vue {
|
|||||||
@Prop({ default: false })
|
@Prop({ default: false })
|
||||||
readonly isArrayItem!: boolean
|
readonly isArrayItem!: boolean
|
||||||
|
|
||||||
@Provide('path') get groupPath (): string {
|
@Provide('__Formulario_path')
|
||||||
|
get groupPath (): string {
|
||||||
if (this.isArrayItem) {
|
if (this.isArrayItem) {
|
||||||
return `${this.path}[${this.name}]`
|
return `${this.__Formulario_path}[${this.name}]`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.path === '') {
|
if (this.__Formulario_path === '') {
|
||||||
return this.name
|
return this.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +26,6 @@ import FormularioFormRegistry from '@/FormularioFormRegistry'
|
|||||||
|
|
||||||
import FormularioField from '@/FormularioField.vue'
|
import FormularioField from '@/FormularioField.vue'
|
||||||
|
|
||||||
import {
|
|
||||||
ErrorHandler,
|
|
||||||
ErrorObserver,
|
|
||||||
ErrorObserverRegistry,
|
|
||||||
} from '@/validation/ErrorObserver'
|
|
||||||
|
|
||||||
import { Violation } from '@/validation/validator'
|
import { Violation } from '@/validation/validator'
|
||||||
|
|
||||||
type ValidationEventPayload = {
|
type ValidationEventPayload = {
|
||||||
@ -49,14 +43,10 @@ export default class FormularioForm extends Vue {
|
|||||||
// Form errors only used on FormularioForm default slot
|
// Form errors only used on FormularioForm default slot
|
||||||
@Prop({ default: () => ([]) }) readonly formErrors!: string[]
|
@Prop({ default: () => ([]) }) readonly formErrors!: string[]
|
||||||
|
|
||||||
@Provide()
|
|
||||||
public path = ''
|
|
||||||
|
|
||||||
public proxy: Record<string, unknown> = {}
|
public proxy: Record<string, unknown> = {}
|
||||||
|
|
||||||
private registry: FormularioFormRegistry = new FormularioFormRegistry(this)
|
private registry: FormularioFormRegistry = new FormularioFormRegistry(this)
|
||||||
|
|
||||||
private errorObserverRegistry = new ErrorObserverRegistry()
|
|
||||||
// Local error messages are temporal, they wiped each resetValidation call
|
// Local error messages are temporal, they wiped each resetValidation call
|
||||||
private localFormErrors: string[] = []
|
private localFormErrors: string[] = []
|
||||||
private localFieldErrors: Record<string, string[]> = {}
|
private localFieldErrors: Record<string, string[]> = {}
|
||||||
@ -87,27 +77,24 @@ export default class FormularioForm extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Watch('formularioValue', { deep: true })
|
@Watch('formularioValue', { deep: true })
|
||||||
onFormularioValueChanged (values: Record<string, unknown>): void {
|
onFormularioValueChange (values: Record<string, unknown>): void {
|
||||||
if (this.hasModel && values && typeof values === 'object') {
|
if (this.hasModel && values && typeof values === 'object') {
|
||||||
this.setValues(values)
|
this.setValues(values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Watch('mergedFormErrors')
|
|
||||||
onMergedFormErrorsChanged (errors: string[]): void {
|
|
||||||
this.errorObserverRegistry.filter(o => o.type === 'form').observe(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Watch('mergedFieldErrors', { deep: true, immediate: true })
|
@Watch('mergedFieldErrors', { deep: true, immediate: true })
|
||||||
onMergedFieldErrorsChanged (errors: Record<string, string[]>): void {
|
onMergedFieldErrorsChange (errors: Record<string, string[]>): void {
|
||||||
this.errorObserverRegistry.filter(o => o.type === 'field').observe(errors)
|
this.registry.forEach((vm, path) => {
|
||||||
|
vm.setErrors(errors[path] || [])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
created (): void {
|
created (): void {
|
||||||
this.initProxy()
|
this.initProxy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provide()
|
@Provide('__FormularioForm_getValue')
|
||||||
getFormValues (): Record<string, unknown> {
|
getFormValues (): Record<string, unknown> {
|
||||||
return this.proxy
|
return this.proxy
|
||||||
}
|
}
|
||||||
@ -129,30 +116,21 @@ export default class FormularioForm extends Vue {
|
|||||||
this.$emit('validation', payload)
|
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')
|
@Provide('__FormularioForm_register')
|
||||||
private register (field: string, component: FormularioField): void {
|
private register (field: string, vm: FormularioField): void {
|
||||||
this.registry.add(field, component)
|
this.registry.add(field, vm)
|
||||||
|
|
||||||
|
if (has(this.mergedFieldErrors, field)) {
|
||||||
|
vm.setErrors(this.mergedFieldErrors[field] || [])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provide('__FormularioForm_unregister')
|
@Provide('__FormularioForm_unregister')
|
||||||
private unregister (field: string): void {
|
private unregister (field: string): void {
|
||||||
|
if (this.registry.has(field)) {
|
||||||
this.registry.remove(field)
|
this.registry.remove(field)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initProxy (): void {
|
initProxy (): void {
|
||||||
if (this.hasInitialValue) {
|
if (this.hasInitialValue) {
|
||||||
|
@ -115,7 +115,7 @@ export default class FormularioFormRegistry {
|
|||||||
/**
|
/**
|
||||||
* Iterate over the registry.
|
* Iterate over the registry.
|
||||||
*/
|
*/
|
||||||
forEach (callback: Function): void {
|
forEach (callback: (component: FormularioField, field: string) => void): void {
|
||||||
this.registry.forEach((component, field) => {
|
this.registry.forEach((component, field) => {
|
||||||
callback(component, field)
|
callback(component, field)
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
export type Empty = undefined | null
|
||||||
|
|
||||||
export type RecordKey = string | number
|
export type RecordKey = string | number
|
||||||
export type RecordLike<T> = T[] | Record<RecordKey, T>
|
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 {
|
export function isScalar (value: unknown): boolean {
|
||||||
switch (typeof value) {
|
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