diff --git a/docs/guide/development.md b/docs/guide/development.md new file mode 100644 index 0000000..9ffee79 --- /dev/null +++ b/docs/guide/development.md @@ -0,0 +1,10 @@ +## Почему значения нужно рекурсивно копировать? + +Может возникнуть вопрос, почему в форме у нас есть переменная values, которая является чистым хранилищем +значений, почему нельзя реализовать её как гетер и проходить по всем зависимостям и получать их значение. +Это решение позволяет обойти 2 проблемы: + +1. При удалении поля для ввода - будет теряться его значение +2. Как поступать, если в начале мы инициализировали форму `form.setValues(data)`, а затем добавили input. + +Именно данная подход сосредоточить модель в одном месте, позволяет упростить разработку. \ No newline at end of file diff --git a/package.json b/package.json index eccdcb8..6a3ea3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jenesius-vue-form", - "version": "2.3.20", + "version": "2.3.21", "description": "Heavy form system for Vue.js", "author": "Jenesius", "license": "MIT", diff --git a/plugin/classes/Form.ts b/plugin/classes/Form.ts index 681e579..9bb30d3 100644 --- a/plugin/classes/Form.ts +++ b/plugin/classes/Form.ts @@ -1,5 +1,5 @@ import EventEmitter from "jenesius-event-emitter"; -import {inject as injectVue, provide as provideVue} from "vue"; +import {getCurrentInstance, inject as injectVue, provide as provideVue} from "vue"; import FormErrors from "./FormErrors"; import {FormDependence, FunctionHandleData, Value, Values} from "../types"; @@ -124,6 +124,7 @@ export default class Form extends EventEmitter implements FormDependence{ * @return true - if #changes includes some values or one of dependencies stay in changed status. */ get changed() { + if (this.parentForm && this.name) return this.parentForm.checkDependenceForChangedStatus(this.name); return !!Object.keys(this.#changes).length || !!this.dependencies.find(d => d.changed); } @@ -157,6 +158,7 @@ export default class Form extends EventEmitter implements FormDependence{ * @description Method used for set values. New values don't overwrite previous, Mixing, GrandValues used for this. * */ setValues(values?: Values){ + const prettyData = grandObject(values); debug.msg(`New Values:`, prettyData); @@ -174,14 +176,17 @@ export default class Form extends EventEmitter implements FormDependence{ this.name = params.name; debug.msg(`Creating new Form${this.name? `[${this.name}]`: ''}`); + const currentInstance = !!getCurrentInstance() // If params don't include parent: false, looking for a form, in case of success subscribe current form to parent. if (params.parent !== false) { - this.parentForm = Form.getParentForm(); + if (currentInstance) + this.parentForm = Form.getParentForm(); if (this.parentForm) this.parentForm.subscribe(this); } - provideVue(Form.PROVIDE_NAME, this); // Default providing current form for children. + if (currentInstance) + provideVue(Form.PROVIDE_NAME, this); // Default providing current form for children. } private markChanges(values: any) { @@ -269,8 +274,19 @@ export default class Form extends EventEmitter implements FormDependence{ } change(values?: Values){ + if (this.parentForm && this.name) { + this.parentForm.change({ + [this.name]: { + ...this.values, + ...values + } + }) + return; + } + this.setValues(values); if (values) this.markChanges(values); + } protected setValuesByName(name: string, value: any) { @@ -305,6 +321,7 @@ export default class Form extends EventEmitter implements FormDependence{ * @description Merging values. * */ protected mergeValues(values: Values) { + console.log(this.values, values) mergeObjects(this.values, values); } @@ -315,8 +332,15 @@ export default class Form extends EventEmitter implements FormDependence{ debug.msg(`New subscription${'name' in item ? `(${item.name})` : ''}`) this.dependencies.push(item); - this.emit(Form.EVENT_SUBSCRIBE, item); + // Install parentForm to this + try { + item.parentForm = this; + } catch (e) { + + } + + this.emit(Form.EVENT_SUBSCRIBE, item); try { item.on(Form.EVENT_CHANGED, () => this.emit(Form.EVENT_CHANGED, this.changed)); diff --git a/plugin/classes/FormProxy.ts b/plugin/classes/FormProxy.ts index 3511455..0667928 100644 --- a/plugin/classes/FormProxy.ts +++ b/plugin/classes/FormProxy.ts @@ -25,7 +25,7 @@ export default class FormProxy extends Form{ * @description Get values just for current names from parent form. */ get values() { - return getPropFromObject(this.parentForm?.values, this.name); + return getPropFromObject(this.parentForm?.values, this.name) || {}; } /** diff --git a/plugin/utils/soft-replace-object.ts b/plugin/utils/soft-replace-object.ts deleted file mode 100644 index 0dd4e6b..0000000 --- a/plugin/utils/soft-replace-object.ts +++ /dev/null @@ -1,17 +0,0 @@ -export default function softReplaceObject(target: Record, data: object) { - const cast: Record = {}; - - Object.entries(data) - .forEach(([key, value]) => { - cast[key] = true; - - // Values is equal for key - if (target[key] === value) return; - target[key] = value - }) - - Object.keys(target) - .forEach(key => { - if (cast[key]) delete target[key]; - }) -} \ No newline at end of file diff --git a/plugin/utils/utils.ts b/plugin/utils/utils.ts index 6eab0ce..412a1e4 100644 --- a/plugin/utils/utils.ts +++ b/plugin/utils/utils.ts @@ -15,7 +15,6 @@ import checkCompositeName from "./check-composite-name"; import bypassObject from "./bypass-object"; import convertOptionsObject from "./convert-options-object"; import copyObject from "./copy-object"; -import softReplaceObject from "./soft-replace-object"; const utils = { updateInputPosition, @@ -35,6 +34,5 @@ const utils = { bypassObject, convertOptionsObject, copyObject, - softReplaceObject } export default utils; diff --git a/src/pages/test/App.vue b/src/pages/test/App.vue index 1dfb316..62b3a72 100644 --- a/src/pages/test/App.vue +++ b/src/pages/test/App.vue @@ -6,10 +6,6 @@ - - - - @@ -24,7 +20,6 @@ const form = new Form(); - function test() { throw new Error('test') } diff --git a/src/pages/test/input-coord.vue b/src/pages/test/input-coord.vue index cd94b13..5d2dbbc 100644 --- a/src/pages/test/input-coord.vue +++ b/src/pages/test/input-coord.vue @@ -2,7 +2,7 @@
- + {{values}}
@@ -14,6 +14,11 @@ const props = defineProps<{ name: string, }>() const {form} = useProxyState(props.name) +function random() { + form.change({ + X: Math.random() + }) +} const values = useFormValues(form) diff --git a/tests/form-proxy/subscribe.spec.ts b/tests/form-proxy/subscribe.spec.ts new file mode 100644 index 0000000..dc1e6b8 --- /dev/null +++ b/tests/form-proxy/subscribe.spec.ts @@ -0,0 +1,70 @@ +import {Form, FormProxy} from "../../plugin"; + +describe("Default test For FormProxy", () => { + + test("Values of parent form should be changed", () => { + const parentForm = new Form(); + + const form = new FormProxy({ + name: "address" + }) + parentForm.subscribe(form); + + parentForm.setValues({ + name: "jenesius" + }) + + form.change({ + city: "XXX" + }) + expect(parentForm.values).toEqual({ + address: { + city: "XXX" + }, + name: "jenesius" + }) + expect(parentForm.changes).toEqual({ + address: { + city: "XXX" + } + }) + }) + test("Changed prop must be true after change", () => { + const parentForm = new Form(); + + const form = new FormProxy({ + name: "address" + }) + parentForm.subscribe(form); + + expect(form.changed).toBe(false); + expect(parentForm.changed).toBe(false); + + form.change({ + city: "XXX" + }) + + expect(parentForm.changed).toBe(true) + expect(form.changed).toBe(true); + + }) + /* + test("New values after setValues", () => { + const parentForm = new Form(); + + const form = new FormProxy({ + name: "address" + }) + parentForm.subscribe(form); + + + form.setValues({ + planet: "Earth" + }) + expect(parentForm.values).toEqual({ + address: { + planet: "Earth" + } + }) + })*/ +}) \ No newline at end of file