Skip to content

Commit 0fcf671

Browse files
authored
Merge pull request #170 from Jenesius/issue_151
test: Resolve #151, add static method getEventAvailabilityByName.
2 parents 08be025 + fb5ae0d commit 0fcf671

File tree

6 files changed

+167
-34
lines changed

6 files changed

+167
-34
lines changed

project/pages/test/App.vue

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,10 @@
66
<div :key = "pureValue">Pure values: {{pureValue}}</div>
77
<div :key = "pureAvailabilities">Pure av: {{pureAvailabilities}}</div>
88

9-
<input type = "number" step = "10"/>
9+
<form-field name = "age" type = "number" step = "10" v-if = "show"/>
10+
<button @click = "show = !show">{{labelButton}}</button>
11+
<br>
1012

11-
<form-field type = "number" name = "age" label = "Age" step = "10"/>
12-
<form-field type = "country" name = "country" label = "Country" />
13-
14-
<form-field type = "tel" name = "phone" label = "Phone" required />
15-
<form-field type = "date" name = "birthday" label = "Bithday" />
16-
17-
18-
<form-field :name="name" :label = "name === 'username' ? 'Username' : 'Age'"/>
19-
<form-field name="username" label = "Username" />
20-
<form-field name="name" label = "Name"/>
21-
<widget-address/>
22-
<form-field name="address.city.index" label = "City Index" />
2313
<button @click = "change">change field name</button>
2414
<button @click = "clean">clean values</button>
2515

@@ -33,7 +23,7 @@
3323
<script setup lang='ts'>
3424
import Form from "../../../src/classes/Form";
3525
import FormField from "./../../../src/widgets/form-field.vue";
36-
import {ref} from "vue";
26+
import {computed, ref} from "vue";
3727
import WidgetAddress from "./widget-address.vue"
3828
import copyObject from "./../../../src/utils/copy-object";
3929
@@ -42,7 +32,8 @@ const form = window.form = new Form({
4232
name: "main",
4333
parent:false
4434
});
45-
35+
const show = ref(false);
36+
const labelButton = computed(() => show.value ? 'Hide' : 'Show')
4637
4738
setInterval(() => {
4839
if (!form) return;
@@ -54,9 +45,8 @@ setInterval(() => {
5445
}, 50);
5546
5647
57-
form.oninput('birthday', v => {
58-
console.log(v)
59-
})
48+
49+
6050
const values = ref(0);
6151
const changes = ref({});
6252
const pureValue= ref({});

src/classes/Form.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ export default class Form extends EventEmitter implements FormDependence {
5656
static getEventValueByName(name: string) {
5757
return `${Form.EVENT_VALUE}:${name}`
5858
}
59-
59+
static getEventAvailabilityByName(name: string) {
60+
return `${Form.EVENT_AVAILABLE}:${name}`
61+
}
6062
static restoreFullName<T extends { name?: string, parent?: Form }>(elem: T): string {
6163
if (elem.parent) return `${Form.restoreFullName(elem.parent)}.${elem.name}`;
6264
return elem.name || '';
@@ -661,15 +663,12 @@ export default class Form extends EventEmitter implements FormDependence {
661663
return this.isAvailable;
662664
}
663665

664-
private getAvailableEventName(fieldName: string) {
665-
return `${Form.EVENT_AVAILABLE}:${fieldName}`
666-
}
667666
onavailable(callback: (disabled: boolean) => any): any
668667
onavailable(fieldName: string, callback: (disabled: boolean) => any): any
669668
onavailable(arg1: ((disabled: boolean) => any) | string, arg2?: (disabled: boolean) => any):any {
670669
if (typeof arg1 === 'string') {
671670
if (!arg2) throw new Error('For named handler you need provided callback.');
672-
return this.on(this.getAvailableEventName(arg1), arg2);
671+
return this.on(Form.getEventAvailabilityByName(arg1), arg2);
673672
}
674673

675674
return this.on(Form.EVENT_AVAILABLE, arg1);

src/hooks/use-form-input.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export default function useFormInput(parentForm: Form) {
66

77
if (!parentForm) return null;
88
let offDependency: undefined | (() => void );
9+
let offOnInput: undefined | (() => void )
10+
let offOnAvailability: undefined | (() => void )
11+
912
const input = reactive<Partial<FormInput>>({ setValidation, setValue, setName, validationRules: [], deactivate }) as FormInput
1013

1114
return input;
@@ -21,9 +24,8 @@ export default function useFormInput(parentForm: Form) {
2124
// Отписываемся от формы для старого поля, подписываемся для нового
2225
deactivate()
2326
offDependency = parentForm.subscribe(getNewDependency(input))
24-
25-
parentForm.oninput(name, updateInput)
26-
parentForm.onavailable(name, updateAvailability)
27+
offOnInput = parentForm.oninput(name, updateInput)
28+
offOnAvailability = parentForm.onavailable(name, updateAvailability)
2729

2830
// Начальная инициализация состояния Input
2931
updateInput();
@@ -46,7 +48,9 @@ export default function useFormInput(parentForm: Form) {
4648
}
4749

4850
function deactivate() {
49-
if (offDependency) offDependency();
51+
offDependency?.();
52+
offOnInput?.();
53+
offOnAvailability?.()
5054
}
5155
}
5256

src/hooks/use-form-state.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import Form from "../classes/Form";
2-
import {reactive} from "vue";
2+
import {onUnmounted, reactive} from "vue";
33

44
export default function useFormState(form: Form) {
55
const state = reactive<FormReactiveState>({
66
changed: form.changed,
77
disabled: form.disabled,
88
wait: form.wait
99
})
10-
11-
form.on(Form.EVENT_CHANGED, () => state.changed = form.changed);
12-
form.onavailable(v => state.disabled = !v); // Так значение является isAvailable. Значит нам нужно инверсировать его.
13-
form.on(Form.EVENT_WAIT, v => state.wait = v)
10+
11+
const offArray:any[] = []
12+
13+
offArray[0] = form.on(Form.EVENT_CHANGED, () => state.changed = form.changed);
14+
offArray[1] = form.onavailable(v => state.disabled = !v); // Так значение является isAvailable. Значит нам нужно инверсировать его.
15+
offArray[2] = form.on(Form.EVENT_WAIT, v => state.wait = v)
16+
17+
onUnmounted(() => {
18+
offArray.forEach(off => off?.())
19+
})
1420

1521
return state
1622
}

src/hooks/use-form-values.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import Form from "../classes/Form";
22
import mergeObjects from "../utils/merge-objects";
33
import grandObject from "../utils/grand-object";
4-
import {reactive} from "vue";
4+
import {onUnmounted, reactive} from "vue";
55

66
/**
77
* @description Return dynamic form values.
88
* */
99
export default function useFormValues(form: Form) {
1010
const values = reactive(form.values || {});
11-
form.onvalue(data => {
11+
const off = form.onvalue(function (data) {
1212
const newValues = {
1313
[data.name]: data.newValue
1414
}
1515
mergeObjects(values, grandObject(newValues));
1616
})
17+
18+
onUnmounted(() => {
19+
off?.()
20+
})
21+
1722
return values;
1823
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Здесь проверяется очистка event из формы после уничтожения поля
3+
* */
4+
import {FormField, Form, useFormState, useFormValues} from "../../src";
5+
import {mount} from "@vue/test-utils";
6+
import {defineComponent, ref} from "vue";
7+
8+
9+
describe("Clean event handlers", () => {
10+
test("count of event should be empty when form was created", () => {
11+
const form = new Form();
12+
expect(Object.keys(form.events).length).toBe(0)
13+
})
14+
test("Add/Remove input should add 2 events and then remove them", async () => {
15+
const component = defineComponent({
16+
props: {
17+
show: {
18+
type: Boolean
19+
}
20+
},
21+
template: `<div><form-field name = "age" v-if = "show"/></div>`,
22+
components: {FormField},
23+
data: () => ({
24+
form: new Form()
25+
})
26+
})
27+
28+
const app = mount(component, {
29+
attachTo: document.body
30+
}) as any
31+
32+
const form = app.vm.form as Form
33+
34+
expect(Object.keys(form.events).length).toBe(0)
35+
await app.setProps({ show: true })
36+
37+
expect(Object.keys(form.events).length).toBe(2)
38+
expect(Object.keys(form.events[Form.getEventValueByName('age')]).length).toBe(1)
39+
expect(Object.keys(form.events[Form.getEventAvailabilityByName('age')]).length).toBe(1)
40+
41+
await app.setProps({ show: false })
42+
43+
expect(Object.keys(form.events[Form.getEventValueByName('age')]).length).toBe(0)
44+
expect(Object.keys(form.events[Form.getEventAvailabilityByName('age')]).length).toBe(0)
45+
46+
})
47+
test("useFormState should clean hooks after component was removed", async () => {
48+
49+
const ComponentWithHook = defineComponent({
50+
template: `<div> </div>`,
51+
setup() {
52+
const form = Form.getParentForm() as Form;
53+
const formState = useFormState(form);
54+
}
55+
})
56+
57+
const app = mount({
58+
props: {
59+
show: {
60+
type: Boolean
61+
}
62+
},
63+
template: `<div><ComponentWithHook v-if = "show"/></div>`,
64+
components: {ComponentWithHook},
65+
data: () => ({
66+
form: new Form()
67+
})
68+
}, {
69+
attachTo: document.body
70+
}) as any
71+
72+
const form = app.vm.form as Form;
73+
74+
expect(Object.keys(form.events).length).toBe(0);
75+
await app.setProps({
76+
show: true
77+
})
78+
expect(Object.keys(form.events).length).toBe(3);
79+
80+
expect(Object.keys(form.events[Form.EVENT_WAIT]).length).toBe(1);
81+
expect(Object.keys(form.events[Form.EVENT_CHANGED]).length).toBe(1);
82+
expect(Object.keys(form.events[Form.EVENT_AVAILABLE]).length).toBe(1);
83+
84+
await app.setProps({
85+
show: false
86+
})
87+
expect(Object.keys(form.events[Form.EVENT_WAIT]).length).toBe(0);
88+
expect(Object.keys(form.events[Form.EVENT_CHANGED]).length).toBe(0);
89+
expect(Object.keys(form.events[Form.EVENT_AVAILABLE]).length).toBe(0);
90+
})
91+
test("useFormValues should clean hooks after component was removed", async () => {
92+
93+
const ComponentWithHook = defineComponent({
94+
template: `<div> </div>`,
95+
setup() {
96+
const form = Form.getParentForm() as Form;
97+
const values = useFormValues(form);
98+
}
99+
})
100+
101+
const app = mount({
102+
props: {
103+
show: {
104+
type: Boolean
105+
}
106+
},
107+
template: `<div><ComponentWithHook v-if = "show"/></div>`,
108+
components: {ComponentWithHook},
109+
data: () => ({
110+
form: new Form()
111+
})
112+
}, {
113+
attachTo: document.body
114+
}) as any
115+
116+
const form = app.vm.form as Form;
117+
118+
expect(Object.keys(form.events).length).toBe(0);
119+
await app.setProps({
120+
show: true
121+
})
122+
expect(Object.keys(form.events).length).toBe(1);
123+
await app.setProps({
124+
show: false
125+
})
126+
127+
expect(form.events[Form.EVENT_VALUE].length).toBe(0);
128+
})
129+
})

0 commit comments

Comments
 (0)