UNPKG

element-plus

Version:

A Component Library for Vue 3

1 lines 7.77 kB
{"version":3,"file":"form2.mjs","names":[],"sources":["../../../../../../packages/components/form/src/form.vue"],"sourcesContent":["<template>\n <form ref=\"formRef\" :class=\"formClasses\">\n <slot />\n </form>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, provide, reactive, ref, toRefs, watch } from 'vue'\nimport { cloneDeep } from 'lodash-unified'\nimport {\n debugWarn,\n ensureArray,\n getProp,\n isArray,\n isFunction,\n} from '@element-plus/utils'\nimport { useNamespace } from '@element-plus/hooks'\nimport { useFormSize } from './hooks'\nimport { formContextKey } from './constants'\nimport { formEmits } from './form'\nimport { filterFields, useFormLabelWidth } from './utils'\n\nimport type { ValidateFieldsError } from 'async-validator'\nimport type { Arrayable } from '@element-plus/utils'\nimport type { FormProps } from './form'\nimport type {\n FormContext,\n FormItemContext,\n FormValidateCallback,\n FormValidationResult,\n} from './types'\nimport type { FormItemProp } from './form-item'\n\nconst COMPONENT_NAME = 'ElForm'\ndefineOptions({\n name: COMPONENT_NAME,\n})\nconst props = withDefaults(defineProps<FormProps>(), {\n labelPosition: 'right',\n requireAsteriskPosition: 'left',\n labelWidth: '',\n labelSuffix: '',\n showMessage: true,\n validateOnRuleChange: true,\n scrollIntoViewOptions: true,\n})\nconst emit = defineEmits(formEmits)\n\nconst formRef = ref<HTMLElement>()\nconst fields = reactive<FormItemContext[]>([])\nconst initialValues = new Map<string, any>()\n\nconst formSize = useFormSize()\nconst ns = useNamespace('form')\nconst formClasses = computed(() => {\n const { labelPosition, inline } = props\n return [\n ns.b(),\n ns.m(formSize.value || 'default'),\n {\n [ns.m(`label-${labelPosition}`)]: labelPosition,\n [ns.m('inline')]: inline,\n },\n ]\n})\n\nconst getField: FormContext['getField'] = (prop) => {\n return filterFields(fields, [prop])[0]\n}\n\nconst addField: FormContext['addField'] = (field) => {\n fields.push(field)\n if (field.propString) {\n if (initialValues.has(field.propString)) {\n field.setInitialValue(initialValues.get(field.propString))\n } else {\n initialValues.set(field.propString, cloneDeep(field.fieldValue))\n }\n }\n}\n\nconst removeField: FormContext['removeField'] = (field) => {\n if (field.prop) {\n fields.splice(fields.indexOf(field), 1)\n }\n}\n\nconst setInitialValues: FormContext['setInitialValues'] = (initModel) => {\n if (!props.model) {\n debugWarn(COMPONENT_NAME, 'model is required for setInitialValues to work.')\n return\n }\n if (!initModel) {\n debugWarn(\n COMPONENT_NAME,\n 'initModel is required for setInitialValues to work.'\n )\n return\n }\n\n for (const key of initialValues.keys()) {\n initialValues.set(key, cloneDeep(getProp(initModel, key).value))\n }\n fields.forEach((field) => {\n if (field.prop) {\n field.setInitialValue(getProp(initModel, field.prop).value)\n }\n })\n}\n\nconst resetFields: FormContext['resetFields'] = (properties = []) => {\n if (!props.model) {\n debugWarn(COMPONENT_NAME, 'model is required for resetFields to work.')\n return\n }\n\n filterFields(fields, properties).forEach((field) => field.resetField())\n\n const activePropStrings = new Set(\n fields.map((f) => f.propString).filter(Boolean)\n )\n const propsToCheck =\n properties.length > 0\n ? ensureArray(properties).map((p) => (isArray(p) ? p.join('.') : p))\n : [...initialValues.keys()]\n\n for (const propString of propsToCheck) {\n if (!activePropStrings.has(propString) && initialValues.has(propString)) {\n getProp(props.model, propString).value = cloneDeep(\n initialValues.get(propString)\n )\n }\n }\n}\n\nconst clearValidate: FormContext['clearValidate'] = (props = []) => {\n filterFields(fields, props).forEach((field) => field.clearValidate())\n}\n\nconst isValidatable = computed(() => {\n const hasModel = !!props.model\n if (!hasModel) {\n debugWarn(COMPONENT_NAME, 'model is required for validate to work.')\n }\n return hasModel\n})\n\nconst obtainValidateFields = (props: Arrayable<FormItemProp>) => {\n if (fields.length === 0) return []\n\n const filteredFields = filterFields(fields, props)\n if (!filteredFields.length) {\n debugWarn(COMPONENT_NAME, 'please pass correct props!')\n return []\n }\n return filteredFields\n}\n\nconst validate = async (\n callback?: FormValidateCallback\n): FormValidationResult => validateField(undefined, callback)\n\nconst doValidateField = async (\n props: Arrayable<FormItemProp> = []\n): Promise<boolean> => {\n if (!isValidatable.value) return false\n\n const fields = obtainValidateFields(props)\n if (fields.length === 0) return true\n\n let validationErrors: ValidateFieldsError = {}\n for (const field of fields) {\n try {\n await field.validate('')\n if (field.validateState === 'error' && !field.error) field.resetField()\n } catch (fields) {\n validationErrors = {\n ...validationErrors,\n ...(fields as ValidateFieldsError),\n }\n }\n }\n\n if (Object.keys(validationErrors).length === 0) return true\n return Promise.reject(validationErrors)\n}\n\nconst validateField: FormContext['validateField'] = async (\n modelProps = [],\n callback\n) => {\n let result = false\n const shouldThrow = !isFunction(callback)\n try {\n result = await doValidateField(modelProps)\n // When result is false meaning that the fields are not validatable\n if (result === true) {\n await callback?.(result)\n }\n return result\n } catch (e) {\n if (e instanceof Error) throw e\n\n const invalidFields = e as ValidateFieldsError\n\n if (props.scrollToError) {\n // form-item may be dynamically rendered based on the judgment conditions, and the order in invalidFields is uncertain.\n // Therefore, the first form field with an error is determined by directly looking for the rendered element.\n if (formRef.value) {\n const formItem = formRef.value.querySelector(`.${ns.b()}-item.is-error`)\n formItem?.scrollIntoView(props.scrollIntoViewOptions)\n }\n }\n !result && (await callback?.(false, invalidFields))\n return shouldThrow && Promise.reject(invalidFields)\n }\n}\n\nconst scrollToField = (prop: FormItemProp) => {\n const field = getField(prop)\n if (field) {\n field.$el?.scrollIntoView(props.scrollIntoViewOptions)\n }\n}\n\nwatch(\n () => props.rules,\n () => {\n if (props.validateOnRuleChange) {\n validate().catch((err) => debugWarn(err))\n }\n },\n { deep: true, flush: 'post' }\n)\n\nprovide(\n formContextKey,\n reactive({\n ...toRefs(props),\n emit,\n\n resetFields,\n clearValidate,\n validateField,\n getField,\n addField,\n removeField,\n setInitialValues,\n\n ...useFormLabelWidth(),\n })\n)\n\ndefineExpose({\n /**\n * @description Validate the whole form. Receives a callback or returns `Promise`.\n */\n validate,\n /**\n * @description Validate specified fields.\n */\n validateField,\n /**\n * @description Reset specified fields and remove validation result.\n */\n resetFields,\n /**\n * @description Clear validation message for specified fields.\n */\n clearValidate,\n /**\n * @description Scroll to the specified fields.\n */\n scrollToField,\n /**\n * @description Get a field context.\n */\n getField,\n /**\n * @description All fields context.\n */\n fields,\n /**\n * @description Set initial values for form fields. When `resetFields` is called, fields will reset to these values.\n */\n setInitialValues,\n})\n</script>\n"],"mappings":""}