useForm.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import _extends from "@babel/runtime/helpers/esm/extends";
  4. import { reactive, watch, nextTick, unref, shallowRef, toRaw, ref } from 'vue';
  5. import cloneDeep from 'lodash-es/cloneDeep';
  6. import intersection from 'lodash-es/intersection';
  7. import isEqual from 'lodash-es/isEqual';
  8. import debounce from 'lodash-es/debounce';
  9. import omit from 'lodash-es/omit';
  10. import { validateRules } from './utils/validateUtil';
  11. import { defaultValidateMessages } from './utils/messages';
  12. import { allPromiseFinish } from './utils/asyncUtil';
  13. function isRequired(rules) {
  14. var isRequired = false;
  15. if (rules && rules.length) {
  16. rules.every(function (rule) {
  17. if (rule.required) {
  18. isRequired = true;
  19. return false;
  20. }
  21. return true;
  22. });
  23. }
  24. return isRequired;
  25. }
  26. function toArray(value) {
  27. if (value === undefined || value === null) {
  28. return [];
  29. }
  30. return Array.isArray(value) ? value : [value];
  31. }
  32. function getPropByPath(obj, path, strict) {
  33. var tempObj = obj;
  34. path = path.replace(/\[(\w+)\]/g, '.$1');
  35. path = path.replace(/^\./, '');
  36. var keyArr = path.split('.');
  37. var i = 0;
  38. for (var len = keyArr.length; i < len - 1; ++i) {
  39. if (!tempObj && !strict) break;
  40. var key = keyArr[i];
  41. if (key in tempObj) {
  42. tempObj = tempObj[key];
  43. } else {
  44. if (strict) {
  45. throw new Error('please transfer a valid name path to validate!');
  46. }
  47. break;
  48. }
  49. }
  50. return {
  51. o: tempObj,
  52. k: keyArr[i],
  53. v: tempObj ? tempObj[keyArr[i]] : null,
  54. isValid: tempObj && keyArr[i] in tempObj
  55. };
  56. }
  57. function useForm(modelRef) {
  58. var rulesRef = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ref({});
  59. var options = arguments.length > 2 ? arguments[2] : undefined;
  60. var initialModel = cloneDeep(unref(modelRef));
  61. var validateInfos = reactive({});
  62. var rulesKeys = shallowRef([]);
  63. var resetFields = function resetFields(newValues) {
  64. _extends(unref(modelRef), _objectSpread(_objectSpread({}, cloneDeep(initialModel)), newValues));
  65. nextTick(function () {
  66. Object.keys(validateInfos).forEach(function (key) {
  67. validateInfos[key] = {
  68. autoLink: false,
  69. required: isRequired(unref(rulesRef)[key])
  70. };
  71. });
  72. });
  73. };
  74. var filterRules = function filterRules() {
  75. var rules = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  76. var trigger = arguments.length > 1 ? arguments[1] : undefined;
  77. if (!trigger.length) {
  78. return rules;
  79. } else {
  80. return rules.filter(function (rule) {
  81. var triggerList = toArray(rule.trigger || 'change');
  82. return intersection(triggerList, trigger).length;
  83. });
  84. }
  85. };
  86. var lastValidatePromise = null;
  87. var validateFields = function validateFields(names) {
  88. var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  89. var strict = arguments.length > 2 ? arguments[2] : undefined;
  90. // Collect result in promise list
  91. var promiseList = [];
  92. var values = {};
  93. var _loop = function _loop() {
  94. var name = names[i];
  95. var prop = getPropByPath(unref(modelRef), name, strict);
  96. if (!prop.isValid) return "continue";
  97. values[name] = prop.v;
  98. var rules = filterRules(unref(rulesRef)[name], toArray(option && option.trigger));
  99. if (rules.length) {
  100. promiseList.push(validateField(name, prop.v, rules, option || {}).then(function () {
  101. return {
  102. name: name,
  103. errors: [],
  104. warnings: []
  105. };
  106. }).catch(function (ruleErrors) {
  107. var mergedErrors = [];
  108. var mergedWarnings = [];
  109. ruleErrors.forEach(function (_ref) {
  110. var warningOnly = _ref.rule.warningOnly,
  111. errors = _ref.errors;
  112. if (warningOnly) {
  113. mergedWarnings.push.apply(mergedWarnings, _toConsumableArray(errors));
  114. } else {
  115. mergedErrors.push.apply(mergedErrors, _toConsumableArray(errors));
  116. }
  117. });
  118. if (mergedErrors.length) {
  119. return Promise.reject({
  120. name: name,
  121. errors: mergedErrors,
  122. warnings: mergedWarnings
  123. });
  124. }
  125. return {
  126. name: name,
  127. errors: mergedErrors,
  128. warnings: mergedWarnings
  129. };
  130. }));
  131. }
  132. };
  133. for (var i = 0; i < names.length; i++) {
  134. var _ret = _loop();
  135. if (_ret === "continue") continue;
  136. }
  137. var summaryPromise = allPromiseFinish(promiseList);
  138. lastValidatePromise = summaryPromise;
  139. var returnPromise = summaryPromise.then(function () {
  140. if (lastValidatePromise === summaryPromise) {
  141. return Promise.resolve(values);
  142. }
  143. return Promise.reject([]);
  144. }).catch(function (results) {
  145. var errorList = results.filter(function (result) {
  146. return result && result.errors.length;
  147. });
  148. return Promise.reject({
  149. values: values,
  150. errorFields: errorList,
  151. outOfDate: lastValidatePromise !== summaryPromise
  152. });
  153. });
  154. // Do not throw in console
  155. returnPromise.catch(function (e) {
  156. return e;
  157. });
  158. return returnPromise;
  159. };
  160. var validateField = function validateField(name, value, rules) {
  161. var option = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  162. var promise = validateRules([name], value, rules, _objectSpread({
  163. validateMessages: defaultValidateMessages
  164. }, option), !!option.validateFirst);
  165. if (!validateInfos[name]) {
  166. return promise.catch(function (e) {
  167. return e;
  168. });
  169. }
  170. validateInfos[name].validateStatus = 'validating';
  171. promise.catch(function (e) {
  172. return e;
  173. }).then(function () {
  174. var results = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  175. if (validateInfos[name].validateStatus === 'validating') {
  176. var _options$onValidate;
  177. var res = results.filter(function (result) {
  178. return result && result.errors.length;
  179. });
  180. validateInfos[name].validateStatus = res.length ? 'error' : 'success';
  181. validateInfos[name].help = res.length ? res.map(function (r) {
  182. return r.errors;
  183. }) : null;
  184. options === null || options === void 0 ? void 0 : (_options$onValidate = options.onValidate) === null || _options$onValidate === void 0 ? void 0 : _options$onValidate.call(options, name, !res.length, res.length ? toRaw(validateInfos[name].help[0]) : null);
  185. }
  186. });
  187. return promise;
  188. };
  189. var validate = function validate(names, option) {
  190. var keys = [];
  191. var strict = true;
  192. if (!names) {
  193. strict = false;
  194. keys = rulesKeys.value;
  195. } else if (Array.isArray(names)) {
  196. keys = names;
  197. } else {
  198. keys = [names];
  199. }
  200. var promises = validateFields(keys, option || {}, strict);
  201. // Do not throw in console
  202. promises.catch(function (e) {
  203. return e;
  204. });
  205. return promises;
  206. };
  207. var clearValidate = function clearValidate(names) {
  208. var keys = [];
  209. if (!names) {
  210. keys = rulesKeys.value;
  211. } else if (Array.isArray(names)) {
  212. keys = names;
  213. } else {
  214. keys = [names];
  215. }
  216. keys.forEach(function (key) {
  217. validateInfos[key] && _extends(validateInfos[key], {
  218. validateStatus: '',
  219. help: null
  220. });
  221. });
  222. };
  223. var mergeValidateInfo = function mergeValidateInfo(items) {
  224. var info = {
  225. autoLink: false
  226. };
  227. var help = [];
  228. var infos = Array.isArray(items) ? items : [items];
  229. for (var i = 0; i < infos.length; i++) {
  230. var arg = infos[i];
  231. if ((arg === null || arg === void 0 ? void 0 : arg.validateStatus) === 'error') {
  232. info.validateStatus = 'error';
  233. arg.help && help.push(arg.help);
  234. }
  235. info.required = info.required || (arg === null || arg === void 0 ? void 0 : arg.required);
  236. }
  237. info.help = help;
  238. return info;
  239. };
  240. var oldModel = initialModel;
  241. var isFirstTime = true;
  242. var modelFn = function modelFn(model) {
  243. var names = [];
  244. rulesKeys.value.forEach(function (key) {
  245. var prop = getPropByPath(model, key, false);
  246. var oldProp = getPropByPath(oldModel, key, false);
  247. var isFirstValidation = isFirstTime && (options === null || options === void 0 ? void 0 : options.immediate) && prop.isValid;
  248. if (isFirstValidation || !isEqual(prop.v, oldProp.v)) {
  249. names.push(key);
  250. }
  251. });
  252. validate(names, {
  253. trigger: 'change'
  254. });
  255. isFirstTime = false;
  256. oldModel = cloneDeep(toRaw(model));
  257. };
  258. var debounceOptions = options === null || options === void 0 ? void 0 : options.debounce;
  259. var first = true;
  260. watch(rulesRef, function () {
  261. rulesKeys.value = rulesRef ? Object.keys(unref(rulesRef)) : [];
  262. if (!first && options && options.validateOnRuleChange) {
  263. validate();
  264. }
  265. first = false;
  266. }, {
  267. deep: true,
  268. immediate: true
  269. });
  270. watch(rulesKeys, function () {
  271. var newValidateInfos = {};
  272. rulesKeys.value.forEach(function (key) {
  273. newValidateInfos[key] = _extends({}, validateInfos[key], {
  274. autoLink: false,
  275. required: isRequired(unref(rulesRef)[key])
  276. });
  277. delete validateInfos[key];
  278. });
  279. for (var key in validateInfos) {
  280. if (Object.prototype.hasOwnProperty.call(validateInfos, key)) {
  281. delete validateInfos[key];
  282. }
  283. }
  284. _extends(validateInfos, newValidateInfos);
  285. }, {
  286. immediate: true
  287. });
  288. watch(modelRef, debounceOptions && debounceOptions.wait ? debounce(modelFn, debounceOptions.wait, omit(debounceOptions, ['wait'])) : modelFn, {
  289. immediate: options && !!options.immediate,
  290. deep: true
  291. });
  292. return {
  293. modelRef: modelRef,
  294. rulesRef: rulesRef,
  295. initialModel: initialModel,
  296. validateInfos: validateInfos,
  297. resetFields: resetFields,
  298. validate: validate,
  299. validateField: validateField,
  300. mergeValidateInfo: mergeValidateInfo,
  301. clearValidate: clearValidate
  302. };
  303. }
  304. export default useForm;