diff --git a/packages/element/src/array-base/index.ts b/packages/element/src/array-base/index.ts index 1d9771d55bd..ac46e9caa34 100644 --- a/packages/element/src/array-base/index.ts +++ b/packages/element/src/array-base/index.ts @@ -9,7 +9,13 @@ import { onBeforeUnmount, PropType, } from '@vue/composition-api' -import { Fragment, useField, useFieldSchema, h } from '@formily/vue' +import { + Fragment, + useField, + useFieldSchema, + h, + ExpressionScope, +} from '@formily/vue' import { isValid, uid, clone } from '@formily/shared' import { ArrayField } from '@formily/core' import { stylePrefix } from '../__builtins__/configs' @@ -166,7 +172,13 @@ const ArrayBaseItem = defineComponent({ setup(props: IArrayBaseItemProps, { slots }) { provide(ItemSymbol, props) return () => { - return h(Fragment, {}, slots) + return h( + ExpressionScope, + { props: { value: { $record: props.record, $index: props.index } } }, + { + default: () => h(Fragment, {}, slots), + } + ) } }, }) diff --git a/packages/vue/docs/.vuepress/config.js b/packages/vue/docs/.vuepress/config.js index 663b08eef0b..04c665515f7 100644 --- a/packages/vue/docs/.vuepress/config.js +++ b/packages/vue/docs/.vuepress/config.js @@ -53,6 +53,7 @@ module.exports = { '/api/components/recursion-field-with-component', '/api/components/form-provider', '/api/components/form-consumer', + '/api/components/expression-scope', ], }, { diff --git a/packages/vue/docs/api/components/expression-scope.md b/packages/vue/docs/api/components/expression-scope.md new file mode 100644 index 00000000000..dfda6905041 --- /dev/null +++ b/packages/vue/docs/api/components/expression-scope.md @@ -0,0 +1,22 @@ +--- +order: 8 +--- + +# ExpressionScope + +## 描述 + +用于自定义组件内部给 json-schema 表达式传递局部作用域 + +## 签名 + +```ts +interface IExpressionScopeProps { + value?: any +} +type ExpressionScope = Vue.Component +``` + +## 用例 + + diff --git a/packages/vue/docs/demos/api/components/expression-scope.vue b/packages/vue/docs/demos/api/components/expression-scope.vue new file mode 100644 index 00000000000..f0b5558974f --- /dev/null +++ b/packages/vue/docs/demos/api/components/expression-scope.vue @@ -0,0 +1,56 @@ + + + diff --git a/packages/vue/src/__tests__/expression.scope.spec.ts b/packages/vue/src/__tests__/expression.scope.spec.ts new file mode 100644 index 00000000000..5be5dbc85d9 --- /dev/null +++ b/packages/vue/src/__tests__/expression.scope.spec.ts @@ -0,0 +1,55 @@ +import { render } from '@testing-library/vue' +import { createForm } from '@formily/core' +import { FormProvider, ExpressionScope, createSchemaField, h } from '..' +import { defineComponent } from '@vue/composition-api' + +test('expression scope', async () => { + const Container = defineComponent({ + setup(_props, { slots }) { + return () => + h( + ExpressionScope, + { + props: { value: { $innerScope: 'inner scope value' } }, + }, + slots + ) + }, + }) + const Input = defineComponent({ + props: ['text'], + setup(props) { + return () => + h( + 'div', + { attrs: { 'data-testid': 'test-input' } }, + { default: () => props.text } + ) + }, + }) + const SchemaField = createSchemaField({ + components: { Container, Input }, + }) + const form = createForm() + const { getByTestId } = render({ + components: { ...SchemaField, FormProvider }, + data() { + return { form } + }, + template: ` + + + + + + `, + }) + + expect(getByTestId('test-input').textContent).toBe( + 'inner scope valueouter scope value' + ) +}) diff --git a/packages/vue/src/components/ExpressionScope.ts b/packages/vue/src/components/ExpressionScope.ts new file mode 100644 index 00000000000..3994548ecc5 --- /dev/null +++ b/packages/vue/src/components/ExpressionScope.ts @@ -0,0 +1,19 @@ +import { computed, defineComponent, inject, provide, Ref } from 'vue-demi' +import { SchemaExpressionScopeSymbol, Fragment, h } from '../shared' +import { IExpressionScopeProps } from '../types' + +export const ExpressionScope = defineComponent({ + name: 'ExpressionScope', + props: ['value'], + setup(props: IExpressionScopeProps, { slots }) { + const scopeRef = inject(SchemaExpressionScopeSymbol) + const expressionScopeRef = computed(() => ({ + ...scopeRef.value, + ...props.value, + })) + + provide(SchemaExpressionScopeSymbol, expressionScopeRef) + + return () => h(Fragment, {}, slots) + }, +}) diff --git a/packages/vue/src/components/index.ts b/packages/vue/src/components/index.ts index 56dd1940f83..a06273c73d4 100644 --- a/packages/vue/src/components/index.ts +++ b/packages/vue/src/components/index.ts @@ -6,3 +6,4 @@ export { default as VoidField } from './VoidField' export { default as RecursionField } from './RecursionField' export { default as Field } from './Field' export { createSchemaField } from './SchemaField' +export { ExpressionScope } from './ExpressionScope' diff --git a/packages/vue/src/types/index.ts b/packages/vue/src/types/index.ts index faa9fd3dadd..bfd1804a2d3 100644 --- a/packages/vue/src/types/index.ts +++ b/packages/vue/src/types/index.ts @@ -133,3 +133,7 @@ export type ISchemaTypeFieldProps< Decorator extends ComponentPath = ComponentPath, Component extends ComponentPath = ComponentPath > = Omit, 'type'> + +export type IExpressionScopeProps = { + value: any +}