Skip to content

Commit 8528067

Browse files
authored
fix(vue): decorator event props not work in vue2 (#3884)
1 parent 1e62b24 commit 8528067

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed

packages/vue/src/__tests__/schema.json.spec.ts

+79-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { createForm } from '@formily/core'
1+
import { createForm, Field } from '@formily/core'
22
import { observer } from '@formily/reactive-vue'
33
import { Schema } from '@formily/json-schema'
4-
import { render, waitFor } from '@testing-library/vue'
4+
import { fireEvent, render, waitFor } from '@testing-library/vue'
55
import { mount } from '@vue/test-utils'
66
import Vue, { FunctionalComponentOptions } from 'vue'
77
import {
@@ -46,10 +46,32 @@ const Input2: FunctionalComponentOptions = {
4646
},
4747
}
4848

49+
const FormItem: FunctionalComponentOptions = {
50+
functional: true,
51+
render(h, { props, slots, data }) {
52+
return h(
53+
'div',
54+
{
55+
...data,
56+
style: {
57+
width: '300px',
58+
height: '30px',
59+
background: 'yellow',
60+
},
61+
attrs: {
62+
'data-testid': 'formitem',
63+
...data.attrs,
64+
},
65+
},
66+
[props.label || 'unknown ', slots().default]
67+
)
68+
},
69+
}
70+
4971
const ArrayItems = observer(
5072
defineComponent({
5173
setup() {
52-
const fieldRef = useField()
74+
const fieldRef = useField<Field>()
5375
const schemaRef = useFieldSchema()
5476

5577
return () => {
@@ -1259,3 +1281,57 @@ describe('schema controlled', () => {
12591281
})
12601282
})
12611283
})
1284+
1285+
describe('x-decorator', () => {
1286+
test('x-decorator-props', async () => {
1287+
const form = createForm()
1288+
const { SchemaField } = createSchemaField({
1289+
components: {
1290+
Input,
1291+
FormItem,
1292+
},
1293+
})
1294+
1295+
const atBlurFn = jest.fn()
1296+
const onClickFn = jest.fn()
1297+
const atClickFn = jest.fn()
1298+
const { queryByTestId, getByText } = render({
1299+
components: { SchemaField },
1300+
data() {
1301+
return {
1302+
form,
1303+
schema: new Schema({
1304+
type: 'string',
1305+
'x-component': 'Input',
1306+
'x-component-props': {
1307+
'@blur': function atBlur() {
1308+
atBlurFn()
1309+
},
1310+
},
1311+
'x-decorator': 'FormItem',
1312+
'x-decorator-props': {
1313+
label: 'Label ',
1314+
onClick: function onClick() {
1315+
onClickFn()
1316+
},
1317+
'@click': function atClick() {
1318+
atClickFn()
1319+
},
1320+
},
1321+
}),
1322+
}
1323+
},
1324+
template: `
1325+
<FormProvider :form="form">
1326+
<SchemaField
1327+
name="string"
1328+
:schema="schema"
1329+
/>
1330+
</FormProvider>`,
1331+
})
1332+
expect(queryByTestId('formitem')).toBeVisible()
1333+
await fireEvent.click(getByText('Label'))
1334+
expect(atClickFn).toBeCalledTimes(1)
1335+
expect(onClickFn).toBeCalledTimes(0)
1336+
})
1337+
})

packages/vue/src/components/ReactiveField.ts

+31-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { inject, provide, Ref, ref, shallowRef, watch, isVue2 } from 'vue-demi'
22
import { GeneralField, isVoidField } from '@formily/core'
3-
import { FormPath } from '@formily/shared'
3+
import { each, FormPath } from '@formily/shared'
44
import { observer } from '@formily/reactive-vue'
55
import { toJS, reaction } from '@formily/reactive'
66
import { SchemaOptionsSymbol, FieldSymbol, h, Fragment } from '../shared'
@@ -160,10 +160,28 @@ export default observer({
160160
FormPath.getIn(options?.components, field.decoratorType as string) ??
161161
field.decoratorType
162162
const componentAttrs = toJS(field.decorator[1]) || {}
163+
164+
const events: Record<string, any> = {}
165+
each(componentAttrs, (value, eventKey) => {
166+
const onEvent = eventKey.startsWith('on')
167+
const atEvent = eventKey.startsWith('@')
168+
if (!onEvent && !atEvent) return
169+
if (onEvent) {
170+
const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}`
171+
// '@xxx' has higher priority
172+
events[eventName] = events[eventName] || value
173+
} else if (atEvent) {
174+
const eventName = eventKey.slice(1)
175+
events[eventName] = value
176+
delete componentAttrs[eventKey]
177+
}
178+
})
179+
163180
const componentData = {
164181
attrs: componentAttrs,
165182
style: componentAttrs?.style,
166183
class: componentAttrs?.class,
184+
on: events,
167185
}
168186
delete componentData.attrs.style
169187
delete componentData.attrs.class
@@ -186,20 +204,20 @@ export default observer({
186204
const originFocus = originData['@focus'] || originData['onFocus']
187205
const originBlur = originData['@blur'] || originData['onBlur']
188206

189-
// '@xxx' has higher priority
190-
Object.keys(originData)
191-
.filter((key) => key.startsWith('on'))
192-
.forEach((eventKey) => {
207+
each(originData, (value, eventKey) => {
208+
const onEvent = eventKey.startsWith('on')
209+
const atEvent = eventKey.startsWith('@')
210+
if (!onEvent && !atEvent) return
211+
if (onEvent) {
193212
const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}`
194-
events[eventName] = originData[eventKey]
195-
})
196-
197-
Object.keys(originData)
198-
.filter((key) => key.startsWith('@'))
199-
.forEach((eventKey) => {
200-
events[eventKey.slice(1)] = originData[eventKey]
213+
// '@xxx' has higher priority
214+
events[eventName] = events[eventName] || value
215+
} else if (atEvent) {
216+
const eventName = eventKey.slice(1)
217+
events[eventName] = value
201218
delete originData[eventKey]
202-
})
219+
}
220+
})
203221

204222
events.change = (...args: any[]) => {
205223
if (!isVoidField(field)) field.onInput(...args)

0 commit comments

Comments
 (0)