feat: add form item rest
parent
648ab4c4b9
commit
8e111e6a7c
|
@ -74,7 +74,7 @@ export type { EmptyProps } from './empty';
|
||||||
export { default as Empty } from './empty';
|
export { default as Empty } from './empty';
|
||||||
|
|
||||||
export type { FormProps, FormItemProps } from './form';
|
export type { FormProps, FormItemProps } from './form';
|
||||||
export { default as Form, FormItem } from './form';
|
export { default as Form, FormItem, FormItemRest } from './form';
|
||||||
|
|
||||||
export { default as Grid } from './grid';
|
export { default as Grid } from './grid';
|
||||||
|
|
||||||
|
|
|
@ -272,20 +272,25 @@ export default defineComponent({
|
||||||
resetField,
|
resetField,
|
||||||
});
|
});
|
||||||
|
|
||||||
useProvideFormItemContext({
|
useProvideFormItemContext(
|
||||||
id: fieldId,
|
{
|
||||||
onFieldBlur: () => {
|
id: fieldId,
|
||||||
if (props.autoLink) {
|
onFieldBlur: () => {
|
||||||
onFieldBlur();
|
if (props.autoLink) {
|
||||||
}
|
onFieldBlur();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFieldChange: () => {
|
||||||
|
if (props.autoLink) {
|
||||||
|
onFieldChange();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearValidate,
|
||||||
},
|
},
|
||||||
onFieldChange: () => {
|
computed(() => {
|
||||||
if (props.autoLink) {
|
return !!(props.autoLink && formContext.model.value && fieldName.value);
|
||||||
onFieldChange();
|
}),
|
||||||
}
|
);
|
||||||
},
|
|
||||||
clearValidate,
|
|
||||||
});
|
|
||||||
let registered = false;
|
let registered = false;
|
||||||
watch(
|
watch(
|
||||||
fieldName,
|
fieldName,
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
import type { ComputedRef, InjectionKey } from 'vue';
|
import type { ComputedRef, InjectionKey, ConcreteComponent } from 'vue';
|
||||||
import { computed, inject, provide } from 'vue';
|
import {
|
||||||
|
watch,
|
||||||
|
computed,
|
||||||
|
inject,
|
||||||
|
provide,
|
||||||
|
ref,
|
||||||
|
onBeforeUnmount,
|
||||||
|
getCurrentInstance,
|
||||||
|
defineComponent,
|
||||||
|
} from 'vue';
|
||||||
|
import devWarning from '../vc-util/devWarning';
|
||||||
|
|
||||||
export type FormItemContext = {
|
export type FormItemContext = {
|
||||||
id: ComputedRef<string>;
|
id: ComputedRef<string>;
|
||||||
|
@ -8,24 +18,88 @@ export type FormItemContext = {
|
||||||
clearValidate: () => void;
|
clearValidate: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ContextProps = FormItemContext;
|
type InternalFormItemContext = {
|
||||||
|
addFormItemField: (key: Symbol, type: ConcreteComponent) => void;
|
||||||
const ContextKey: InjectionKey<ContextProps> = Symbol('ContextProps');
|
removeFormItemField: (key: Symbol) => void;
|
||||||
|
|
||||||
export const useProvideFormItemContext = (props: ContextProps) => {
|
|
||||||
provide(ContextKey, props);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useInjectFormItemContext = () => {
|
const ContextKey: InjectionKey<FormItemContext> = Symbol('ContextProps');
|
||||||
const defaultContext: ContextProps = {
|
|
||||||
id: computed(() => undefined),
|
const InternalContextKey: InjectionKey<InternalFormItemContext> = Symbol('InternalContextProps');
|
||||||
onFieldBlur: () => {},
|
|
||||||
onFieldChange: () => {},
|
export const useProvideFormItemContext = (
|
||||||
clearValidate: () => {},
|
props: FormItemContext,
|
||||||
|
useValidation: ComputedRef<boolean> = computed(() => true),
|
||||||
|
) => {
|
||||||
|
const formItemFields = ref(new Map<Symbol, ConcreteComponent>());
|
||||||
|
const addFormItemField = (key: Symbol, type: ConcreteComponent) => {
|
||||||
|
formItemFields.value.set(key, type);
|
||||||
|
formItemFields.value = new Map(formItemFields.value);
|
||||||
};
|
};
|
||||||
|
const removeFormItemField = (key: Symbol) => {
|
||||||
|
formItemFields.value.delete(key);
|
||||||
|
formItemFields.value = new Map(formItemFields.value);
|
||||||
|
};
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
watch([useValidation, formItemFields], () => {
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
if (useValidation.value && formItemFields.value.size > 1) {
|
||||||
|
devWarning(
|
||||||
|
false,
|
||||||
|
'Form.Item',
|
||||||
|
`FormItem can only collect one field item, you haved set ${[
|
||||||
|
...formItemFields.value.values(),
|
||||||
|
]
|
||||||
|
.map(v => `\`${v.name}\``)
|
||||||
|
.join(', ')} ${formItemFields.value.size} field items.
|
||||||
|
You can set not need to be collected fields into \`a-form-item-rest\``,
|
||||||
|
);
|
||||||
|
let cur = instance;
|
||||||
|
while (cur.parent) {
|
||||||
|
console.warn('at', cur.type);
|
||||||
|
cur = cur.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
provide(ContextKey, props);
|
||||||
|
provide(InternalContextKey, {
|
||||||
|
addFormItemField,
|
||||||
|
removeFormItemField,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultContext: FormItemContext = {
|
||||||
|
id: computed(() => undefined),
|
||||||
|
onFieldBlur: () => {},
|
||||||
|
onFieldChange: () => {},
|
||||||
|
clearValidate: () => {},
|
||||||
|
};
|
||||||
|
const defaultInternalContext: InternalFormItemContext = {
|
||||||
|
addFormItemField: () => {},
|
||||||
|
removeFormItemField: () => {},
|
||||||
|
};
|
||||||
|
export const useInjectFormItemContext = () => {
|
||||||
|
const internalContext = inject(InternalContextKey, defaultInternalContext);
|
||||||
|
const formItemFieldKey = Symbol('FormItemFieldKey');
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
internalContext.addFormItemField(formItemFieldKey, instance.type);
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
internalContext.removeFormItemField(formItemFieldKey);
|
||||||
|
});
|
||||||
// We should prevent the passing of context for children
|
// We should prevent the passing of context for children
|
||||||
|
provide(InternalContextKey, defaultInternalContext);
|
||||||
provide(ContextKey, defaultContext);
|
provide(ContextKey, defaultContext);
|
||||||
return inject(ContextKey, defaultContext);
|
return inject(ContextKey, defaultContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'AFormItemRest',
|
||||||
|
setup(_, { slots }) {
|
||||||
|
provide(InternalContextKey, defaultInternalContext);
|
||||||
|
provide(ContextKey, defaultContext);
|
||||||
|
return () => {
|
||||||
|
return slots.default?.();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -116,12 +116,14 @@ The first is to use multiple `a-form-item`:
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
```
|
```
|
||||||
|
|
||||||
The second way is to wrap it with a custom component and call `useFormItemContext` in the custom component
|
The second way is to wrap it with a custom component and call `useFormItemContext` in the custom component, It is equivalent to merging multiple form items into one.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script>
|
<script>
|
||||||
|
// custom component
|
||||||
import { Form } from 'ant-desing-vue';
|
import { Form } from 'ant-desing-vue';
|
||||||
export default {
|
export default {
|
||||||
|
name: 'custom-name',
|
||||||
setup() {
|
setup() {
|
||||||
const formItemContext = Form.useFormItemContext();
|
const formItemContext = Form.useFormItemContext();
|
||||||
},
|
},
|
||||||
|
@ -138,6 +140,15 @@ The second way is to wrap it with a custom component and call `useFormItemContex
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Third, the component library provides an `a-form-item-rest` component, which will prevent data collection. You can put form items that do not need to be collected and verified into this component. It is the same as the first This method is very similar, but it does not generate additional dom nodes.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<a-form-item>
|
||||||
|
<a-input name="a"></a-input>
|
||||||
|
<a-form-item-rest><a-input name="b"></a-input></a-form-item-rest>
|
||||||
|
</a-form-item>
|
||||||
|
```
|
||||||
|
|
||||||
#### 2.x
|
#### 2.x
|
||||||
|
|
||||||
Form.Item hijacks the only child element and listens to the `blur` and `change` events to achieve the purpose of automatic verification, so please make sure that the form field is not wrapped by other elements. If there are multiple child elements, only the change of the first child element will be monitored.
|
Form.Item hijacks the only child element and listens to the `blur` and `change` events to achieve the purpose of automatic verification, so please make sure that the form field is not wrapped by other elements. If there are multiple child elements, only the change of the first child element will be monitored.
|
||||||
|
|
|
@ -2,25 +2,28 @@ import type { App, Plugin } from 'vue';
|
||||||
import Form, { formProps } from './Form';
|
import Form, { formProps } from './Form';
|
||||||
import FormItem, { formItemProps } from './FormItem';
|
import FormItem, { formItemProps } from './FormItem';
|
||||||
import useForm from './useForm';
|
import useForm from './useForm';
|
||||||
import { useInjectFormItemContext } from './FormItemContext';
|
import FormItemRest, { useInjectFormItemContext } from './FormItemContext';
|
||||||
export type { Rule, RuleObject } from './interface';
|
export type { Rule, RuleObject } from './interface';
|
||||||
|
|
||||||
export type { FormProps } from './Form';
|
export type { FormProps } from './Form';
|
||||||
export type { FormItemProps } from './FormItem';
|
export type { FormItemProps } from './FormItem';
|
||||||
|
|
||||||
Form.useInjectFormItemContext = useInjectFormItemContext;
|
Form.useInjectFormItemContext = useInjectFormItemContext;
|
||||||
|
Form.ItemRest = FormItemRest;
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
Form.install = function (app: App) {
|
Form.install = function (app: App) {
|
||||||
app.component(Form.name, Form);
|
app.component(Form.name, Form);
|
||||||
app.component(Form.Item.name, Form.Item);
|
app.component(Form.Item.name, Form.Item);
|
||||||
|
app.component(FormItemRest.name, FormItemRest);
|
||||||
return app;
|
return app;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { FormItem, formItemProps, formProps, useForm, useInjectFormItemContext };
|
export { FormItem, formItemProps, formProps, FormItemRest, useForm, useInjectFormItemContext };
|
||||||
|
|
||||||
export default Form as typeof Form &
|
export default Form as typeof Form &
|
||||||
Plugin & {
|
Plugin & {
|
||||||
readonly Item: typeof Form.Item;
|
readonly Item: typeof Form.Item;
|
||||||
|
readonly ItemRest: typeof FormItemRest;
|
||||||
readonly useForm: typeof useForm;
|
readonly useForm: typeof useForm;
|
||||||
readonly useInjectFormItemContext: typeof useInjectFormItemContext;
|
readonly useInjectFormItemContext: typeof useInjectFormItemContext;
|
||||||
};
|
};
|
||||||
|
|
|
@ -104,7 +104,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
```
|
```
|
||||||
|
|
||||||
如上 Form.Item 并不知道需要收集 `name="a"` 还是 `name=`b``,你可以通过如下两种方式去解决此类问题:
|
如上 Form.Item 并不知道需要收集 `name="a"` 还是 `name=`b``,你可以通过如下三种方式去解决此类问题:
|
||||||
|
|
||||||
第一种,使用多个 `a-form-item`:
|
第一种,使用多个 `a-form-item`:
|
||||||
|
|
||||||
|
@ -115,10 +115,11 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
```
|
```
|
||||||
|
|
||||||
第二种,使用自定义组件包裹,并在自定义组件中调用 `useFormItemContext`
|
第二种,使用自定义组件包裹,并在自定义组件中调用 `useFormItemContext`,相当于把多个表单项合并成了一个
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script>
|
<script>
|
||||||
|
// 自定义组件
|
||||||
import { Form } from 'ant-desing-vue';
|
import { Form } from 'ant-desing-vue';
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -137,6 +138,15 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
第三种,组件库提供了一个 `a-form-item-rest` 组件,它会阻止数据的收集,你可以将不需要收集校验的表单项放到这个组件中即可,它和第一种方式很类似,但它不会产生额外的 dom 节点。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<a-form-item>
|
||||||
|
<a-input name="a"></a-input>
|
||||||
|
<a-form-item-rest><a-input name="b"></a-input></a-form-item-rest>
|
||||||
|
</a-form-item>
|
||||||
|
```
|
||||||
|
|
||||||
#### 2.x
|
#### 2.x
|
||||||
|
|
||||||
Form.Item 会对唯一子元素进行劫持,并监听 `blur` 和 `change` 事件,来达到自动校验的目的,所以请确保表单域没有其它元素包裹。如果有多个子元素,将只会监听第一个子元素的变化。
|
Form.Item 会对唯一子元素进行劫持,并监听 `blur` 和 `change` 事件,来达到自动校验的目的,所以请确保表单域没有其它元素包裹。如果有多个子元素,将只会监听第一个子元素的变化。
|
||||||
|
|
|
@ -49,6 +49,7 @@ Array [
|
||||||
"Empty",
|
"Empty",
|
||||||
"Form",
|
"Form",
|
||||||
"FormItem",
|
"FormItem",
|
||||||
|
"FormItemRest",
|
||||||
"Grid",
|
"Grid",
|
||||||
"Input",
|
"Input",
|
||||||
"InputGroup",
|
"InputGroup",
|
||||||
|
|
Loading…
Reference in New Issue