FormItem.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import { resolveDirective as _resolveDirective, createVNode as _createVNode, Fragment as _Fragment } from "vue";
  4. import { watch, defineComponent, computed, nextTick, ref, watchEffect, onBeforeUnmount, toRaw } from 'vue';
  5. import cloneDeep from 'lodash-es/cloneDeep';
  6. import PropTypes from '../_util/vue-types';
  7. import Row from '../grid/Row';
  8. import { filterEmpty } from '../_util/props-util';
  9. import { validateRules as validateRulesUtil } from './utils/validateUtil';
  10. import { getNamePath } from './utils/valueUtil';
  11. import { toArray } from './utils/typeUtil';
  12. import { warning } from '../vc-util/warning';
  13. import find from 'lodash-es/find';
  14. import { tuple } from '../_util/type';
  15. import useConfigInject from '../_util/hooks/useConfigInject';
  16. import { useInjectForm } from './context';
  17. import FormItemLabel from './FormItemLabel';
  18. import FormItemInput from './FormItemInput';
  19. import { useProvideFormItemContext } from './FormItemContext';
  20. import useDebounce from './utils/useDebounce';
  21. var ValidateStatuses = tuple('success', 'warning', 'error', 'validating', '');
  22. function getPropByPath(obj, namePathList, strict) {
  23. var tempObj = obj;
  24. var keyArr = namePathList;
  25. var i = 0;
  26. try {
  27. for (var len = keyArr.length; i < len - 1; ++i) {
  28. if (!tempObj && !strict) break;
  29. var key = keyArr[i];
  30. if (key in tempObj) {
  31. tempObj = tempObj[key];
  32. } else {
  33. if (strict) {
  34. throw Error('please transfer a valid name path to form item!');
  35. }
  36. break;
  37. }
  38. }
  39. if (strict && !tempObj) {
  40. throw Error('please transfer a valid name path to form item!');
  41. }
  42. } catch (error) {
  43. console.error('please transfer a valid name path to form item!');
  44. }
  45. return {
  46. o: tempObj,
  47. k: keyArr[i],
  48. v: tempObj ? tempObj[keyArr[i]] : undefined
  49. };
  50. }
  51. export var formItemProps = function formItemProps() {
  52. return {
  53. htmlFor: String,
  54. prefixCls: String,
  55. label: PropTypes.any,
  56. help: PropTypes.any,
  57. extra: PropTypes.any,
  58. labelCol: {
  59. type: Object
  60. },
  61. wrapperCol: {
  62. type: Object
  63. },
  64. hasFeedback: {
  65. type: Boolean,
  66. default: false
  67. },
  68. colon: {
  69. type: Boolean,
  70. default: undefined
  71. },
  72. labelAlign: PropTypes.oneOf(tuple('left', 'right')),
  73. prop: {
  74. type: [String, Number, Array]
  75. },
  76. name: {
  77. type: [String, Number, Array]
  78. },
  79. rules: [Array, Object],
  80. autoLink: {
  81. type: Boolean,
  82. default: true
  83. },
  84. required: {
  85. type: Boolean,
  86. default: undefined
  87. },
  88. validateFirst: {
  89. type: Boolean,
  90. default: undefined
  91. },
  92. validateStatus: PropTypes.oneOf(tuple('', 'success', 'warning', 'error', 'validating')),
  93. validateTrigger: {
  94. type: [String, Array]
  95. },
  96. messageVariables: {
  97. type: Object
  98. },
  99. hidden: Boolean,
  100. noStyle: Boolean
  101. };
  102. };
  103. var indexGuid = 0;
  104. // default form item id prefix.
  105. var defaultItemNamePrefixCls = 'form_item';
  106. export default defineComponent({
  107. compatConfig: {
  108. MODE: 3
  109. },
  110. name: 'AFormItem',
  111. inheritAttrs: false,
  112. __ANT_NEW_FORM_ITEM: true,
  113. props: formItemProps(),
  114. slots: ['help', 'label', 'extra'],
  115. setup: function setup(props, _ref) {
  116. var slots = _ref.slots,
  117. attrs = _ref.attrs,
  118. expose = _ref.expose;
  119. warning(props.prop === undefined, "`prop` is deprecated. Please use `name` instead.");
  120. var eventKey = "form-item-".concat(++indexGuid);
  121. var _useConfigInject = useConfigInject('form', props),
  122. prefixCls = _useConfigInject.prefixCls;
  123. var formContext = useInjectForm();
  124. var fieldName = computed(function () {
  125. return props.name || props.prop;
  126. });
  127. var errors = ref([]);
  128. var validateDisabled = ref(false);
  129. var inputRef = ref();
  130. var namePath = computed(function () {
  131. var val = fieldName.value;
  132. return getNamePath(val);
  133. });
  134. var fieldId = computed(function () {
  135. if (!namePath.value.length) {
  136. return undefined;
  137. } else {
  138. var formName = formContext.name.value;
  139. var mergedId = namePath.value.join('_');
  140. return formName ? "".concat(formName, "_").concat(mergedId) : "".concat(defaultItemNamePrefixCls, "_").concat(mergedId);
  141. }
  142. });
  143. var getNewFieldValue = function getNewFieldValue() {
  144. var model = formContext.model.value;
  145. if (!model || !fieldName.value) {
  146. return;
  147. } else {
  148. return getPropByPath(model, namePath.value, true).v;
  149. }
  150. };
  151. var fieldValue = computed(function () {
  152. return getNewFieldValue();
  153. });
  154. var initialValue = ref(cloneDeep(fieldValue.value));
  155. var mergedValidateTrigger = computed(function () {
  156. var validateTrigger = props.validateTrigger !== undefined ? props.validateTrigger : formContext.validateTrigger.value;
  157. validateTrigger = validateTrigger === undefined ? 'change' : validateTrigger;
  158. return toArray(validateTrigger);
  159. });
  160. var rulesRef = computed(function () {
  161. var formRules = formContext.rules.value;
  162. var selfRules = props.rules;
  163. var requiredRule = props.required !== undefined ? {
  164. required: !!props.required,
  165. trigger: mergedValidateTrigger.value
  166. } : [];
  167. var prop = getPropByPath(formRules, namePath.value);
  168. formRules = formRules ? prop.o[prop.k] || prop.v : [];
  169. var rules = [].concat(selfRules || formRules || []);
  170. if (find(rules, function (rule) {
  171. return rule.required;
  172. })) {
  173. return rules;
  174. } else {
  175. return rules.concat(requiredRule);
  176. }
  177. });
  178. var isRequired = computed(function () {
  179. var rules = rulesRef.value;
  180. var isRequired = false;
  181. if (rules && rules.length) {
  182. rules.every(function (rule) {
  183. if (rule.required) {
  184. isRequired = true;
  185. return false;
  186. }
  187. return true;
  188. });
  189. }
  190. return isRequired || props.required;
  191. });
  192. var validateState = ref();
  193. watchEffect(function () {
  194. validateState.value = props.validateStatus;
  195. });
  196. var messageVariables = computed(function () {
  197. var variables = {};
  198. if (typeof props.label === 'string') {
  199. variables.label = props.label;
  200. } else if (props.name) {
  201. variables.label = String(name);
  202. }
  203. if (props.messageVariables) {
  204. variables = _objectSpread(_objectSpread({}, variables), props.messageVariables);
  205. }
  206. return variables;
  207. });
  208. var validateRules = function validateRules(options) {
  209. // no name, no value, so the validate result is incorrect
  210. if (namePath.value.length === 0) {
  211. return;
  212. }
  213. var _props$validateFirst = props.validateFirst,
  214. validateFirst = _props$validateFirst === void 0 ? false : _props$validateFirst;
  215. var _ref2 = options || {},
  216. triggerName = _ref2.triggerName;
  217. var filteredRules = rulesRef.value;
  218. if (triggerName) {
  219. filteredRules = filteredRules.filter(function (rule) {
  220. var trigger = rule.trigger;
  221. if (!trigger && !mergedValidateTrigger.value.length) {
  222. return true;
  223. }
  224. var triggerList = toArray(trigger || mergedValidateTrigger.value);
  225. return triggerList.includes(triggerName);
  226. });
  227. }
  228. if (!filteredRules.length) {
  229. return Promise.resolve();
  230. }
  231. var promise = validateRulesUtil(namePath.value, fieldValue.value, filteredRules, _objectSpread({
  232. validateMessages: formContext.validateMessages.value
  233. }, options), validateFirst, messageVariables.value);
  234. validateState.value = 'validating';
  235. errors.value = [];
  236. promise.catch(function (e) {
  237. return e;
  238. }).then(function () {
  239. var results = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  240. if (validateState.value === 'validating') {
  241. var res = results.filter(function (result) {
  242. return result && result.errors.length;
  243. });
  244. validateState.value = res.length ? 'error' : 'success';
  245. errors.value = res.map(function (r) {
  246. return r.errors;
  247. });
  248. formContext.onValidate(fieldName.value, !errors.value.length, errors.value.length ? toRaw(errors.value[0]) : null);
  249. }
  250. });
  251. return promise;
  252. };
  253. var _onFieldBlur = function onFieldBlur() {
  254. validateRules({
  255. triggerName: 'blur'
  256. });
  257. };
  258. var _onFieldChange = function onFieldChange() {
  259. if (validateDisabled.value) {
  260. validateDisabled.value = false;
  261. return;
  262. }
  263. validateRules({
  264. triggerName: 'change'
  265. });
  266. };
  267. var clearValidate = function clearValidate() {
  268. validateState.value = props.validateStatus;
  269. validateDisabled.value = false;
  270. errors.value = [];
  271. };
  272. var resetField = function resetField() {
  273. validateState.value = props.validateStatus;
  274. validateDisabled.value = true;
  275. errors.value = [];
  276. var model = formContext.model.value || {};
  277. var value = fieldValue.value;
  278. var prop = getPropByPath(model, namePath.value, true);
  279. if (Array.isArray(value)) {
  280. prop.o[prop.k] = [].concat(initialValue.value);
  281. } else {
  282. prop.o[prop.k] = initialValue.value;
  283. }
  284. // reset validateDisabled after onFieldChange triggered
  285. nextTick(function () {
  286. validateDisabled.value = false;
  287. });
  288. };
  289. var htmlFor = computed(function () {
  290. return props.htmlFor === undefined ? fieldId.value : props.htmlFor;
  291. });
  292. var onLabelClick = function onLabelClick() {
  293. var id = htmlFor.value;
  294. if (!id || !inputRef.value) {
  295. return;
  296. }
  297. var control = inputRef.value.$el.querySelector("[id=\"".concat(id, "\"]"));
  298. if (control && control.focus) {
  299. control.focus();
  300. }
  301. };
  302. expose({
  303. onFieldBlur: _onFieldBlur,
  304. onFieldChange: _onFieldChange,
  305. clearValidate: clearValidate,
  306. resetField: resetField
  307. });
  308. useProvideFormItemContext({
  309. id: fieldId,
  310. onFieldBlur: function onFieldBlur() {
  311. if (props.autoLink) {
  312. _onFieldBlur();
  313. }
  314. },
  315. onFieldChange: function onFieldChange() {
  316. if (props.autoLink) {
  317. _onFieldChange();
  318. }
  319. },
  320. clearValidate: clearValidate
  321. }, computed(function () {
  322. return !!(props.autoLink && formContext.model.value && fieldName.value);
  323. }));
  324. var registered = false;
  325. watch(fieldName, function (val) {
  326. if (val) {
  327. if (!registered) {
  328. registered = true;
  329. formContext.addField(eventKey, {
  330. fieldValue: fieldValue,
  331. fieldId: fieldId,
  332. fieldName: fieldName,
  333. resetField: resetField,
  334. clearValidate: clearValidate,
  335. namePath: namePath,
  336. validateRules: validateRules,
  337. rules: rulesRef
  338. });
  339. }
  340. } else {
  341. registered = false;
  342. formContext.removeField(eventKey);
  343. }
  344. }, {
  345. immediate: true
  346. });
  347. onBeforeUnmount(function () {
  348. formContext.removeField(eventKey);
  349. });
  350. var debounceErrors = useDebounce(errors);
  351. var mergedValidateStatus = computed(function () {
  352. if (props.validateStatus !== undefined) {
  353. return props.validateStatus;
  354. } else if (debounceErrors.value.length) {
  355. return 'error';
  356. }
  357. return validateState.value;
  358. });
  359. var itemClassName = computed(function () {
  360. var _ref3;
  361. return _ref3 = {}, _defineProperty(_ref3, "".concat(prefixCls.value, "-item"), true), _defineProperty(_ref3, "".concat(prefixCls.value, "-item-has-feedback"), mergedValidateStatus.value && props.hasFeedback), _defineProperty(_ref3, "".concat(prefixCls.value, "-item-has-success"), mergedValidateStatus.value === 'success'), _defineProperty(_ref3, "".concat(prefixCls.value, "-item-has-warning"), mergedValidateStatus.value === 'warning'), _defineProperty(_ref3, "".concat(prefixCls.value, "-item-has-error"), mergedValidateStatus.value === 'error'), _defineProperty(_ref3, "".concat(prefixCls.value, "-item-is-validating"), mergedValidateStatus.value === 'validating'), _defineProperty(_ref3, "".concat(prefixCls.value, "-item-hidden"), props.hidden), _ref3;
  362. });
  363. return function () {
  364. var _slots$default, _props$help;
  365. if (props.noStyle) return (_slots$default = slots.default) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots);
  366. var help = (_props$help = props.help) !== null && _props$help !== void 0 ? _props$help : slots.help ? filterEmpty(slots.help()) : null;
  367. return _createVNode(Row, _objectSpread(_objectSpread({}, attrs), {}, {
  368. "class": [itemClassName.value, help !== undefined && help !== null || debounceErrors.value.length ? "".concat(prefixCls.value, "-item-with-help") : '', attrs.class],
  369. "key": "row"
  370. }), {
  371. default: function _default() {
  372. var _props$label, _slots$label, _props$extra, _slots$extra;
  373. return _createVNode(_Fragment, null, [_createVNode(FormItemLabel, _objectSpread(_objectSpread({}, props), {}, {
  374. "htmlFor": htmlFor.value,
  375. "required": isRequired.value,
  376. "requiredMark": formContext.requiredMark.value,
  377. "prefixCls": prefixCls.value,
  378. "onClick": onLabelClick,
  379. "label": (_props$label = props.label) !== null && _props$label !== void 0 ? _props$label : (_slots$label = slots.label) === null || _slots$label === void 0 ? void 0 : _slots$label.call(slots)
  380. }), null), _createVNode(FormItemInput, _objectSpread(_objectSpread({}, props), {}, {
  381. "errors": help !== undefined && help !== null ? toArray(help) : debounceErrors.value,
  382. "prefixCls": prefixCls.value,
  383. "status": mergedValidateStatus.value,
  384. "ref": inputRef,
  385. "help": help,
  386. "extra": (_props$extra = props.extra) !== null && _props$extra !== void 0 ? _props$extra : (_slots$extra = slots.extra) === null || _slots$extra === void 0 ? void 0 : _slots$extra.call(slots)
  387. }), {
  388. default: slots.default
  389. })]);
  390. }
  391. });
  392. };
  393. }
  394. });