Skip to content

Commit

Permalink
feat(vue): improve expression scope (#2875)
Browse files Browse the repository at this point in the history
* feat(vue): improve expression scope

* chore(test): improve funcs coverage
  • Loading branch information
frehaiku authored Feb 28, 2022
1 parent f1766ec commit 22a3d2b
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 2 deletions.
16 changes: 14 additions & 2 deletions packages/element/src/array-base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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),
}
)
}
},
})
Expand Down
1 change: 1 addition & 0 deletions packages/vue/docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ module.exports = {
'/api/components/recursion-field-with-component',
'/api/components/form-provider',
'/api/components/form-consumer',
'/api/components/expression-scope',
],
},
{
Expand Down
22 changes: 22 additions & 0 deletions packages/vue/docs/api/components/expression-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
order: 8
---

# ExpressionScope

## 描述

用于自定义组件内部给 json-schema 表达式传递局部作用域

## 签名

```ts
interface IExpressionScopeProps {
value?: any
}
type ExpressionScope = Vue.Component<any, any, any, IExpressionScopeProps>
```
## 用例
<dumi-previewer demoPath="api/components/expression-scope" />
56 changes: 56 additions & 0 deletions packages/vue/docs/demos/api/components/expression-scope.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<template>
<FormProvider :form="form">
<SchemaField :scope="{ $outerScope: 'outer scope value' }">
<SchemaVoidField x-component="Container">
<SchemaVoidField
name="div"
x-component="Text"
:x-component-props="{ text: `{{$innerScope + ' ' + $outerScope}}` }"
/>
</SchemaVoidField>
</SchemaField>
</FormProvider>
</template>

<script>
import { defineComponent } from '@vue/composition-api'
import { createForm } from '@formily/core'
import {
FormProvider,
h,
createSchemaField,
ExpressionScope,
} from '@formily/vue'
const Container = defineComponent({
setup(_props, { slots }) {
return () =>
h(
ExpressionScope,
{
props: { value: { $innerScope: 'inner scope value' } },
},
slots
)
},
})
const Text = defineComponent({
props: ['text'],
setup(props) {
return () => h('div', {}, { default: () => props.text })
},
})
const SchemaField = createSchemaField({
components: { Container, Text },
})
export default {
components: { FormProvider, ...SchemaField },
data() {
return {
Text,
form: createForm(),
}
},
}
</script>
55 changes: 55 additions & 0 deletions packages/vue/src/__tests__/expression.scope.spec.ts
Original file line number Diff line number Diff line change
@@ -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: `<FormProvider :form="form">
<SchemaField :scope="{ $outerScope: 'outer scope value' }">
<SchemaVoidField x-component="Container">
<SchemaVoidField
name="div"
x-component="Input"
:x-component-props='{ text: "{{$innerScope + $outerScope}}"}'
/>
</SchemaVoidField>
</SchemaField>
</FormProvider>`,
})

expect(getByTestId('test-input').textContent).toBe(
'inner scope valueouter scope value'
)
})
19 changes: 19 additions & 0 deletions packages/vue/src/components/ExpressionScope.ts
Original file line number Diff line number Diff line change
@@ -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<Ref>(SchemaExpressionScopeSymbol)
const expressionScopeRef = computed(() => ({
...scopeRef.value,
...props.value,
}))

provide(SchemaExpressionScopeSymbol, expressionScopeRef)

return () => h(Fragment, {}, slots)
},
})
1 change: 1 addition & 0 deletions packages/vue/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
4 changes: 4 additions & 0 deletions packages/vue/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,7 @@ export type ISchemaTypeFieldProps<
Decorator extends ComponentPath<Components> = ComponentPath<Components>,
Component extends ComponentPath<Components> = ComponentPath<Components>
> = Omit<ISchemaMarkupFieldProps<Components, Decorator, Component>, 'type'>

export type IExpressionScopeProps = {
value: any
}

0 comments on commit 22a3d2b

Please sign in to comment.