useForm.js 9.7 KB

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