From 7aca27a01044c5f4d2206e82549bf5fbd29ab4e1 Mon Sep 17 00:00:00 2001 From: Zaytsev Kirill Date: Sat, 22 May 2021 20:35:57 +0300 Subject: [PATCH] refactor: Typehinting improvals, moved registry class, improved logic of shallow equal checker --- src/FormularioField.vue | 15 +++---- src/FormularioForm.vue | 10 ++--- .../registry.ts => FormularioFormRegistry.ts} | 7 ++-- src/utils/index.ts | 2 +- src/utils/shallowEqualObjects.ts | 34 --------------- src/utils/shallowEquals.ts | 42 +++++++++++++++++++ src/validation/rules.ts | 6 +-- 7 files changed, 63 insertions(+), 53 deletions(-) rename src/{form/registry.ts => FormularioFormRegistry.ts} (95%) delete mode 100644 src/utils/shallowEqualObjects.ts create mode 100644 src/utils/shallowEquals.ts diff --git a/src/FormularioField.vue b/src/FormularioField.vue index cb0232f..497c2b6 100644 --- a/src/FormularioField.vue +++ b/src/FormularioField.vue @@ -16,7 +16,7 @@ import { Prop, Watch, } from 'vue-property-decorator' -import { arrayify, has, shallowEqualObjects, snakeToCamel } from './utils' +import { arrayify, has, shallowEquals, snakeToCamel } from './utils' import { processConstraints, validate, @@ -102,7 +102,7 @@ export default class FormularioField extends Vue { private set model (value: unknown) { value = this.modelSetConverter(value, this.proxy) - if (!shallowEqualObjects(value, this.proxy)) { + if (!shallowEquals(value, this.proxy)) { this.proxy = value } @@ -153,7 +153,7 @@ export default class FormularioField extends Vue { @Watch('proxy') private onProxyChange (newValue: unknown, oldValue: unknown): void { - if (!this.hasModel && !shallowEqualObjects(newValue, oldValue)) { + if (!this.hasModel && !shallowEquals(newValue, oldValue)) { this.context.model = newValue } if (this.validationBehavior === VALIDATION_BEHAVIOR.LIVE) { @@ -165,7 +165,7 @@ export default class FormularioField extends Vue { @Watch('value') private onValueChange (newValue: unknown, oldValue: unknown): void { - if (this.hasModel && !shallowEqualObjects(newValue, oldValue)) { + if (this.hasModel && !shallowEquals(newValue, oldValue)) { this.context.model = newValue } } @@ -199,14 +199,14 @@ export default class FormularioField extends Vue { private initProxy (): void { // This should only be run immediately on created and ensures that the // proxy and the model are both the same before any additional registration. - if (!shallowEqualObjects(this.context.model, this.proxy)) { + if (!shallowEquals(this.context.model, this.proxy)) { this.context.model = this.proxy } } - runValidation (): Promise { + runValidation (): Promise { this.validationRun = this.validate().then(violations => { - const validationChanged = !shallowEqualObjects(violations, this.violations) + const validationChanged = !shallowEquals(violations, this.violations) this.violations = violations if (validationChanged) { const payload = { @@ -221,6 +221,7 @@ export default class FormularioField extends Vue { return this.violations }) + return this.validationRun } diff --git a/src/FormularioForm.vue b/src/FormularioForm.vue index a9ed7af..f8dd0f4 100644 --- a/src/FormularioForm.vue +++ b/src/FormularioForm.vue @@ -19,10 +19,10 @@ import { has, merge, setNested, - shallowEqualObjects, + shallowEquals, } from '@/utils' -import Registry from '@/form/registry' +import FormularioFormRegistry from '@/FormularioFormRegistry' import FormularioField from '@/FormularioField.vue' @@ -54,7 +54,7 @@ export default class FormularioForm extends Vue { public proxy: Record = {} - private registry: Registry = new Registry(this) + private registry: FormularioFormRegistry = new FormularioFormRegistry(this) private errorObserverRegistry = new ErrorObserverRegistry() // Local error messages are temporal, they wiped each resetValidation call @@ -174,12 +174,12 @@ export default class FormularioForm extends Vue { const oldValue = getNested(this.proxy, fqn) const newValue = getNested(values, fqn) - if (!shallowEqualObjects(newValue, oldValue)) { + if (!shallowEquals(newValue, oldValue)) { this.setFieldValue(fqn, newValue) proxyHasChanges = true } - if (!shallowEqualObjects(newValue, $field.proxy)) { + if (!shallowEquals(newValue, $field.proxy)) { $field.context.model = newValue } }) diff --git a/src/form/registry.ts b/src/FormularioFormRegistry.ts similarity index 95% rename from src/form/registry.ts rename to src/FormularioFormRegistry.ts index c806a45..7b7a968 100644 --- a/src/form/registry.ts +++ b/src/FormularioFormRegistry.ts @@ -1,4 +1,5 @@ -import { shallowEqualObjects, has, getNested } from '@/utils' +import { getNested, has, shallowEquals } from '@/utils' + import FormularioField from '@/FormularioField.vue' import FormularioForm from '@/FormularioForm.vue' @@ -6,7 +7,7 @@ import FormularioForm from '@/FormularioForm.vue' * Component registry with inherent depth to handle complex nesting. This is * important for features such as grouped fields. */ -export default class Registry { +export default class FormularioFormRegistry { private ctx: FormularioForm private registry: Map @@ -42,7 +43,7 @@ export default class Registry { // @ts-ignore component.context.model = value // @ts-ignore - } else if (hasModel && !shallowEqualObjects(component.proxy, value)) { + } else if (hasModel && !shallowEquals(component.proxy, value)) { // In this case, the field is v-modeled or has an initial value and the // form has no value or a different value, so use the field value // @ts-ignore diff --git a/src/utils/index.ts b/src/utils/index.ts index b809744..a5582e3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,7 +4,7 @@ export { default as has } from './has' export { isScalar } from './types' export { default as merge } from './merge' export { default as regexForFormat } from './regexForFormat' -export { default as shallowEqualObjects } from './shallowEqualObjects' +export { default as shallowEquals } from './shallowEquals' export { default as snakeToCamel } from './snakeToCamel' export function getNested (obj: Record, field: string): any { diff --git a/src/utils/shallowEqualObjects.ts b/src/utils/shallowEqualObjects.ts deleted file mode 100644 index 6f4a207..0000000 --- a/src/utils/shallowEqualObjects.ts +++ /dev/null @@ -1,34 +0,0 @@ -export default function shallowEqualObjects (objA: Record, objB: Record): boolean { - if (objA === objB) { - return true - } - - if (!objA || !objB) { - return false - } - - const aKeys = Object.keys(objA) - const bKeys = Object.keys(objB) - - if (bKeys.length !== aKeys.length) { - return false - } - - if (objA instanceof Date && objB instanceof Date) { - return objA.getTime() === objB.getTime() - } - - if (aKeys.length === 0) { - return objA === objB - } - - for (let i = 0; i < aKeys.length; i++) { - const key = aKeys[i] - - if (objA[key] !== objB[key]) { - return false - } - } - - return true -} diff --git a/src/utils/shallowEquals.ts b/src/utils/shallowEquals.ts new file mode 100644 index 0000000..2895688 --- /dev/null +++ b/src/utils/shallowEquals.ts @@ -0,0 +1,42 @@ +export function equalsDates (a: Date, b: Date): boolean { + return a.getTime() === b.getTime() +} + +export function shallowEqualsRecords ( + a: Record, + b: Record +): boolean { + const aKeys = Object.keys(a) + const bKeys = Object.keys(b) + + if (aKeys.length !== bKeys.length) { + return false + } + + if (aKeys.length === 0) { + return a === b + } + + return aKeys.reduce((equals: boolean, key: string): boolean => { + return equals && a[key] === b[key] + }, true) +} + +export default function shallowEquals (a: unknown, b: unknown): boolean { + if (a === b) { + return true + } + + if (!a || !b) { + return false + } + + if (a instanceof Date && b instanceof Date) { + return equalsDates(a, b) + } + + return shallowEqualsRecords( + a as Record, + b as Record + ) +} diff --git a/src/validation/rules.ts b/src/validation/rules.ts index c88f86c..9479feb 100644 --- a/src/validation/rules.ts +++ b/src/validation/rules.ts @@ -1,5 +1,5 @@ import isUrl from 'is-url' -import { has, regexForFormat, shallowEqualObjects } from '@/utils' +import { has, regexForFormat, shallowEquals } from '@/utils' import { ValidationContext, ValidationRuleFn, @@ -130,7 +130,7 @@ const rules: Record = { * Rule: Value is in an array (stack). */ in ({ value }: ValidationContext, ...stack: any[]): boolean { - return stack.some(item => typeof item === 'object' ? shallowEqualObjects(item, value) : item === value) + return stack.some(item => typeof item === 'object' ? shallowEquals(item, value) : item === value) }, /** @@ -198,7 +198,7 @@ const rules: Record = { * Rule: Value is not in stack. */ not ({ value }: ValidationContext, ...stack: any[]): boolean { - return !stack.some(item => typeof item === 'object' ? shallowEqualObjects(item, value) : item === value) + return !stack.some(item => typeof item === 'object' ? shallowEquals(item, value) : item === value) }, /**