UNPKG

@alifd/field

Version:

Fields can be used to manage data when it comes to form data manipulation and validation. After being associated with a component, the form data can be automatically written back, read, and verified.

1,235 lines (1,234 loc) 50.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var validate_1 = tslib_1.__importDefault(require("@alifd/validate")); var utils_1 = require("./utils"); var initMeta = { state: '', valueName: 'value', trigger: 'onChange', inputValues: [], }; var Field = /** @class */ (function () { function Field(com, options) { if (options === void 0) { options = {}; } var _this_1 = this; if (!com) { (0, utils_1.warning)('`this` is missing in `Field`, you should use like `new Field(this)`'); } this.com = com; this.fieldsMeta = {}; this.cachedBind = {}; this.instance = {}; this.instanceCount = {}; this.reRenders = {}; this.listeners = {}; // holds constructor values. Used for setting field defaults on init if no other value or initValue is passed. // Also used caching values when using `parseName: true` before a field is initialized this.values = Object.assign({}, options.values); this.processErrorMessage = options.processErrorMessage; this.afterValidateRerender = options.afterValidateRerender; this.options = Object.assign({ parseName: false, forceUpdate: false, first: false, onChange: function () { }, autoUnmount: true, autoValidate: true, }, options); [ 'init', 'getValue', 'getValues', 'setValue', 'setValues', 'getError', 'getErrors', 'setError', 'setErrors', 'validateCallback', 'validatePromise', 'getState', 'reset', 'resetToDefault', 'remove', 'spliceArray', 'addArrayValue', 'deleteArrayValue', 'getNames', ].forEach(function (m) { _this_1[m] = _this_1[m].bind(_this_1); }); } Field.create = function (com, options) { if (options === void 0) { options = {}; } return new this(com, options); }; Field.getUseField = function (_a) { var _this_1 = this; var useState = _a.useState, useMemo = _a.useMemo; return function (options) { if (options === void 0) { options = {}; } var _a = tslib_1.__read(useState(), 2), setState = _a[1]; var field = useMemo(function () { return _this_1.create({ setState: setState }, options); }, [setState]); return field; }; }; /** * 设置配置信息 * @param options - 配置 */ Field.prototype.setOptions = function (options) { Object.assign(this.options, options); }; /** * 初始化一个字段项 * @param name - 字段 key * @param option - 字段配置 * @param rprops - 其它参数 */ Field.prototype.init = function (name, option, rprops) { var _a; var _this_1 = this; if (option === void 0) { option = {}; } var id = option.id, initValue = option.initValue, _b = option.valueName, valueName = _b === void 0 ? 'value' : _b, _c = option.trigger, trigger = _c === void 0 ? 'onChange' : _c, _d = option.rules, rules = _d === void 0 ? [] : _d, _e = option.props, props = _e === void 0 ? {} : _e, _f = option.getValueFromEvent, getValueFromEvent = _f === void 0 ? null : _f, _g = option.getValueFormatter, getValueFormatter = _g === void 0 ? getValueFromEvent : _g, setValueFormatter = option.setValueFormatter, _h = option.autoValidate, autoValidate = _h === void 0 ? true : _h, reRender = option.reRender; var parseName = this.options.parseName; if (getValueFromEvent) { (0, utils_1.warning)('`getValueFromEvent` has been deprecated in `Field`, use `getValueFormatter` instead of it'); } var originalProps = Object.assign({}, props, rprops); var defaultValueName = "default".concat(valueName[0].toUpperCase()).concat(valueName.slice(1)); var defaultValue; if (typeof initValue !== 'undefined') { defaultValue = initValue; } else if (typeof originalProps[defaultValueName] !== 'undefined') { // here use typeof, in case of defaultValue={0} defaultValue = originalProps[defaultValueName]; } // get field from this.fieldsMeta or new one var field = this._getInitMeta(name); Object.assign(field, { valueName: valueName, initValue: defaultValue, disabled: 'disabled' in originalProps ? originalProps.disabled : false, getValueFormatter: getValueFormatter, setValueFormatter: setValueFormatter, rules: (0, utils_1.cloneToRuleArr)(rules), ref: originalProps.ref, }); var oldValue = field.value; // Controlled Component, should always equal props.value if (valueName in originalProps) { var originalValue = originalProps[valueName]; // When rerendering set the values from props.value if (parseName) { // when parseName is true, field should not store value locally. To prevent sync issues if (!('value' in field)) { this._proxyFieldValue(field); } } else { this.values[name] = originalValue; } field.value = originalValue; } /** * first init field (value not in field) * should get field.value from this.values or defaultValue */ if (!('value' in field)) { if (parseName) { var cachedValue = (0, utils_1.getIn)(this.values, name); if (typeof cachedValue !== 'undefined') { oldValue = cachedValue; } var initValue_1 = typeof cachedValue !== 'undefined' ? cachedValue : defaultValue; // when parseName is true, field should not store value locally. To prevent sync issues this._proxyFieldValue(field); field.value = initValue_1; } else { var cachedValue = this.values[name]; if (typeof cachedValue !== 'undefined') { field.value = cachedValue; oldValue = cachedValue; } else if (typeof defaultValue !== 'undefined') { // should be same with parseName, but compatible with old versions field.value = defaultValue; this.values[name] = field.value; } } } // field value init end var newValue = field.value; this._triggerFieldChange(name, newValue, oldValue, 'init'); // Component props var inputProps = (_a = { 'data-meta': 'Field', id: id || name, ref: this._getCacheBind(name, "".concat(name, "__ref"), this._saveRef) }, _a[valueName] = setValueFormatter ? setValueFormatter(field.value, field.inputValues) : field.value, _a); var rulesMap = {}; if (this.options.autoValidate && autoValidate !== false) { // trigger map in rules, rulesMap = (0, utils_1.mapValidateRules)(field.rules, trigger); var _loop_1 = function (action) { // skip default trigger, which will trigger in step2 if (action === trigger) { return "continue"; } var actionRule = rulesMap[action]; inputProps[action] = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } _this_1._callNativePropsEvent.apply(_this_1, tslib_1.__spreadArray([action, originalProps], tslib_1.__read(args), false)); _this_1._validate(name, actionRule, action); }; }; // step1 : validate hooks for (var action in rulesMap) { _loop_1(action); } } // step2: onChange(trigger=onChange by default) hack inputProps[trigger] = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var oldValue = _this_1.getValue(name); _this_1._updateFieldValue.apply(_this_1, tslib_1.__spreadArray([name], tslib_1.__read(args), false)); var newValue = _this_1.getValue(name); _this_1._triggerFieldChange(name, newValue, oldValue, 'change'); // clear validate error _this_1._resetError(name); _this_1._callNativePropsEvent.apply(_this_1, tslib_1.__spreadArray([trigger, originalProps], tslib_1.__read(args), false)); // call global onChange _this_1.options.onChange(name, field.value); // validate while onChange var rule = rulesMap[trigger]; rule && _this_1._validate(name, rule, trigger); _this_1._reRender(name, trigger); }; // step3: save reRender function if (reRender && typeof reRender === 'function') { this.reRenders[name] = reRender; } delete originalProps[defaultValueName]; return Object.assign({}, originalProps, inputProps); }; /** * 获取单个输入控件的值 * @param name - 字段名 * @returns 字段值 */ Field.prototype.getValue = function (name) { if (this.options.parseName) { return (0, utils_1.getIn)(this.values, name); } return this.values[name]; }; /** * 获取一组输入控件的值 * @param names - 字段名数组 * @returns 不传入`names`参数,则获取全部字段的值 */ Field.prototype.getValues = function (names) { var _this_1 = this; var allValues = {}; if (names && names.length) { names.forEach(function (name) { allValues[name] = _this_1.getValue(name); }); } else { Object.assign(allValues, this.values); } return allValues; }; /** * 设置单个输入控件的值(默认会触发 render,请遵循 react 时机使用) * @param name - 字段名 * @param value - 字段值 * @param reRender - 设置完成后是否重新渲染,默认为 true * @param triggerChange - 是否触发 watch change,默认为 true */ Field.prototype.setValue = function (name, value, reRender, triggerChange) { if (reRender === void 0) { reRender = true; } if (triggerChange === void 0) { triggerChange = true; } var oldValue = this.getValue(name); if (name in this.fieldsMeta) { this.fieldsMeta[name].value = value; } if (this.options.parseName) { this.values = (0, utils_1.setIn)(this.values, name, value); } else { this.values[name] = value; } var newValue = this.getValue(name); if (triggerChange) { this._triggerFieldChange(name, newValue, oldValue, 'setValue'); } reRender && this._reRender(name, 'setValue'); }; /** * 设置一组输入控件的值(默认会触发 render,请遵循 react 时机使用) * @param fieldsValue - 一组输入控件值对象 * @param reRender - 设置完成后是否重新渲染,默认为 true */ Field.prototype.setValues = function (fieldsValue, reRender) { var e_1, _a; var _this_1 = this; if (fieldsValue === void 0) { fieldsValue = {}; } if (reRender === void 0) { reRender = true; } if (!this.options.parseName) { Object.keys(fieldsValue).forEach(function (name) { _this_1.setValue(name, fieldsValue[name], false, true); }); } else { // NOTE: this is a shallow merge // Ex. we have two values a.b.c=1 ; a.b.d=2, and use setValues({a:{b:{c:3}}}) , then because of shallow merge a.b.d will be lost, we will get only {a:{b:{c:3}}} // fieldsMeta[name].value is proxy from this.values[name] when parseName is true, so there is no need to assign value to fieldMeta // shallow merge var newValues_1 = Object.assign({}, this.values, fieldsValue); var fields = this.getNames(); var allOldFieldValues = this.getValues(fields); // record all old field values, exclude items overwritten by fieldsValue var oldFieldValues = fields .filter(function (name) { return !(0, utils_1.isOverwritten)(fieldsValue, name); }) .map(function (name) { return ({ name: name, value: _this_1.fieldsMeta[name].value }); }); // assign lost field value to newValues oldFieldValues.forEach(function (_a) { var name = _a.name, value = _a.value; if (!(0, utils_1.hasIn)(newValues_1, name)) { newValues_1 = (0, utils_1.setIn)(newValues_1, name, value); } }); // store the new values this.values = newValues_1; try { // trigger changes after update for (var fields_1 = tslib_1.__values(fields), fields_1_1 = fields_1.next(); !fields_1_1.done; fields_1_1 = fields_1.next()) { var name_1 = fields_1_1.value; this._triggerFieldChange(name_1, this.getValue(name_1), allOldFieldValues[name_1], 'setValue'); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (fields_1_1 && !fields_1_1.done && (_a = fields_1.return)) _a.call(fields_1); } finally { if (e_1) throw e_1.error; } } } reRender && this._reRender(); }; /** * 获取单个输入控件的 Error * @param name - 字段名 * @returns 该字段的 Error */ Field.prototype.getError = function (name) { var field = this._get(name); if (field && field.errors && field.errors.length) { return field.errors; } return null; }; /** * 获取一组输入控件的 Error * @param names - 字段名列表 * @returns 不传入`names`参数,则获取全部字段的 Error */ Field.prototype.getErrors = function (names) { var _this_1 = this; var fields = names || this.getNames(); var allErrors = {}; fields.forEach(function (f) { allErrors[f] = _this_1.getError(f); }); return allErrors; }; /** * 设置单个输入控件的 Error * @param name - 字段名 * @param errors - 错误信息 */ Field.prototype.setError = function (name, errors) { var err = Array.isArray(errors) ? errors : errors ? [errors] : []; if (name in this.fieldsMeta) { this.fieldsMeta[name].errors = err; } else { this.fieldsMeta[name] = { errors: err, name: name, }; } if (this.fieldsMeta[name].errors && this.fieldsMeta[name].errors.length > 0) { this.fieldsMeta[name].state = 'error'; } else { this.fieldsMeta[name].state = ''; } this._reRender(name, 'setError'); }; /** * 设置一组输入控件的 Error */ Field.prototype.setErrors = function (fieldsErrors) { var _this_1 = this; if (fieldsErrors === void 0) { fieldsErrors = {}; } Object.keys(fieldsErrors).forEach(function (name) { _this_1.setError(name, fieldsErrors[name]); }); }; /** * 获取单个字段的校验状态 * @param name - 字段名 */ Field.prototype.getState = function (name) { var field = this._get(name); if (field && field.state) { return field.state; } return ''; }; /** * 校验 - Callback version */ Field.prototype.validateCallback = function (ns, cb) { var _this_1 = this; var _a = (0, utils_1.getParams)(ns, cb), names = _a.names, callback = _a.callback; var fieldNames = names || this.getNames(); var descriptor = {}; var values = {}; var hasRule = false; for (var i = 0; i < fieldNames.length; i++) { var name_2 = fieldNames[i]; var field = this._get(name_2); if (!field) { continue; } if (field.rules && field.rules.length) { descriptor[name_2] = field.rules; values[name_2] = this.getValue(name_2); hasRule = true; // clear error field.errors = []; field.state = ''; } } if (!hasRule) { var errors = this.formatGetErrors(fieldNames); callback && callback(errors, this.getValues(names ? fieldNames : [])); return; } var validate = new validate_1.default(descriptor, { first: this.options.first, messages: this.options.messages, }); validate.validate(values, function (errors) { var errorsGroup = null; if (errors && errors.length) { errorsGroup = {}; errors.forEach(function (e) { var fieldName = e.field; if (!errorsGroup[fieldName]) { errorsGroup[fieldName] = { errors: [], }; } var fieldErrors = errorsGroup[fieldName].errors; fieldErrors.push(e.message); }); } if (errorsGroup) { // update error in every Field Object.keys(errorsGroup).forEach(function (i) { var field = _this_1._get(i); if (field) { field.errors = (0, utils_1.getErrorStrs)(errorsGroup[i].errors, _this_1.processErrorMessage); field.state = 'error'; } }); } var formattedGetErrors = _this_1.formatGetErrors(fieldNames); if (formattedGetErrors) { errorsGroup = Object.assign({}, formattedGetErrors, errorsGroup); } // update to success which has no error for (var i = 0; i < fieldNames.length; i++) { var name_3 = fieldNames[i]; var field = _this_1._get(name_3); if (field && field.rules && !(errorsGroup && name_3 in errorsGroup)) { field.state = 'success'; } } callback && callback(errorsGroup, _this_1.getValues(names ? fieldNames : [])); _this_1._reRender(names, 'validate'); _this_1._triggerAfterValidateRerender(errorsGroup); }); }; /** * 校验 - Promise version */ Field.prototype.validatePromise = function (ns, formatter) { return tslib_1.__awaiter(this, void 0, void 0, function () { var _a, names, callback, fieldNames, descriptor, values, hasRule, i, name_4, field, errors_1, validate, results, errors, errorsGroup, callbackResults, error_1; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: _a = (0, utils_1.getParams)(ns, formatter), names = _a.names, callback = _a.callback; fieldNames = names || this.getNames(); descriptor = {}; values = {}; hasRule = false; for (i = 0; i < fieldNames.length; i++) { name_4 = fieldNames[i]; field = this._get(name_4); if (!field) { continue; } if (field.rules && field.rules.length) { descriptor[name_4] = field.rules; values[name_4] = this.getValue(name_4); hasRule = true; // clear error field.errors = []; field.state = ''; } } if (!hasRule) { errors_1 = this.formatGetErrors(fieldNames); if (callback) { return [2 /*return*/, callback({ errors: errors_1, values: this.getValues(names ? fieldNames : []), })]; } else { return [2 /*return*/, { errors: errors_1, values: this.getValues(names ? fieldNames : []), }]; } } validate = new validate_1.default(descriptor, { first: this.options.first, messages: this.options.messages, }); return [4 /*yield*/, validate.validatePromise(values)]; case 1: results = _b.sent(); errors = (results && results.errors) || []; errorsGroup = this._getErrorsGroup({ errors: errors, fieldNames: fieldNames }); callbackResults = { errors: errorsGroup, values: this.getValues(names ? fieldNames : []), }; _b.label = 2; case 2: _b.trys.push([2, 5, , 6]); if (!callback) return [3 /*break*/, 4]; return [4 /*yield*/, callback(callbackResults)]; case 3: callbackResults = _b.sent(); _b.label = 4; case 4: return [3 /*break*/, 6]; case 5: error_1 = _b.sent(); return [2 /*return*/, error_1]; case 6: this._reRender(names, 'validate'); // afterValidateRerender 作为通用属性,在 callback 和 promise 两个版本的 validate 中保持相同行为 this._triggerAfterValidateRerender(errorsGroup); return [2 /*return*/, callbackResults]; } }); }); }; /** * 重置一组输入控件的值,并清空校验信息 * @param names - 要重置的字段名,不传递则重置全部字段 */ Field.prototype.reset = function (ns) { this._reset(ns, false); }; /** * 重置一组输入控件的值为默认值,并清空校验信息 * @param names - 要重置的字段名,不传递则重置全部字段 */ Field.prototype.resetToDefault = function (ns) { this._reset(ns, true); }; /** * 获取所有字段名列表 */ Field.prototype.getNames = function () { var fieldsMeta = this.fieldsMeta; return Object.keys(fieldsMeta).filter(function () { return true; }); }; /** * 删除某一个或者一组控件的数据,删除后与之相关的 validate/value 都会被清空 * @param name - 要删除的字段名,不传递则删除全部字段 */ Field.prototype.remove = function (ns) { var _this_1 = this; if (typeof ns === 'string') { ns = [ns]; } if (!ns) { this.values = {}; } var names = ns || Object.keys(this.fieldsMeta); names.forEach(function (name) { if (name in _this_1.fieldsMeta) { delete _this_1.fieldsMeta[name]; } if (_this_1.options.parseName) { _this_1.values = (0, utils_1.deleteIn)(_this_1.values, name); } else { delete _this_1.values[name]; } }); }; /** * 向指定数组字段内添加数据 * @param name - 字段名 * @param index - 开始添加的索引 * @param argv - 新增的数据 */ Field.prototype.addArrayValue = function (name, index) { var argv = []; for (var _i = 2; _i < arguments.length; _i++) { argv[_i - 2] = arguments[_i]; } return this._spliceArrayValue.apply(this, tslib_1.__spreadArray([name, index, 0], tslib_1.__read(argv), false)); }; /** * 删除指定字段数组内的数据 * @param name - 变量名 * @param index - 开始删除的索引 * @param howmany - 删除几个数据,默认为 1 */ Field.prototype.deleteArrayValue = function (name, index, howmany) { if (howmany === void 0) { howmany = 1; } return this._spliceArrayValue(name, index, howmany); }; /** * splice in a Array [deprecated] * @deprecated Use `addArrayValue` or `deleteArrayValue` instead * @param keyMatch - like name.\{index\} * @param startIndex - index */ Field.prototype.spliceArray = function (keyMatch, startIndex) { var e_2, _a; var _this_1 = this; // @ts-expect-error FIXME 无效的 if 逻辑,恒定为 false if (keyMatch.match(/{index}$/) === -1) { (0, utils_1.warning)('key should match /{index}$/'); return; } // regex to match field names in the same target array var reg = keyMatch.replace('{index}', '(\\d+)'); var keyReg = new RegExp("^".concat(reg)); var listMap = {}; /** * keyMatch='key.\{index\}' * case 1: names=['key.0', 'key.1'], should delete 'key.1' * case 2: names=['key.0.name', 'key.0.email', 'key.1.name', 'key.1.email'], should delete 'key.1.name', 'key.1.email' */ var names = this.getNames(); var willChangeNames = []; names.forEach(function (n) { // is name in the target array? var ret = keyReg.exec(n); if (ret) { var index = parseInt(ret[1]); if (index > startIndex) { var l = listMap[index]; var item = { from: n, to: "".concat(keyMatch.replace('{index}', (index - 1).toString())).concat(n.replace(ret[0], '')), }; willChangeNames.push(item.from); if (names.includes(item.to)) { willChangeNames.push(item.to); } if (!l) { listMap[index] = [item]; } else { l.push(item); } } } }); var oldValues = this.getValues(willChangeNames); var idxList = Object.keys(listMap) .map(function (i) { return { index: Number(i), list: listMap[i], }; }) // @ts-expect-error FIXME 返回 boolean 值并不能正确排序 .sort(function (a, b) { return a.index < b.index; }); // should be continuous array if (idxList.length > 0 && idxList[0].index === startIndex + 1) { idxList.forEach(function (l) { var list = l.list; list.forEach(function (i) { var v = _this_1.getValue(i.from); // get index value _this_1.setValue(i.to, v, false, false); // set value to index - 1 }); }); var lastIdxList = idxList[idxList.length - 1]; lastIdxList.list.forEach(function (i) { _this_1.remove(i.from); }); var parentName = keyMatch.replace('.{index}', ''); parentName = parentName.replace('[{index}]', ''); var parent_1 = this.getValue(parentName); if (parent_1) { // if parseName=true then parent is an Array object but does not know an element was removed // this manually decrements the array length parent_1.length--; } } try { for (var willChangeNames_1 = tslib_1.__values(willChangeNames), willChangeNames_1_1 = willChangeNames_1.next(); !willChangeNames_1_1.done; willChangeNames_1_1 = willChangeNames_1.next()) { var name_5 = willChangeNames_1_1.value; this._triggerFieldChange(name_5, this.getValue(name_5), oldValues[name_5], 'setValue'); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (willChangeNames_1_1 && !willChangeNames_1_1.done && (_a = willChangeNames_1.return)) _a.call(willChangeNames_1); } finally { if (e_2) throw e_2.error; } } }; Field.prototype.get = function (name) { if (name) { return this._get(name); } else { return this.fieldsMeta; } }; /** * 监听字段值变化 * @param names - 监听的 name 列表 * @param callback - 变化回调 * @returns 解除监听回调 */ Field.prototype.watch = function (names, callback) { var e_3, _a; var _this_1 = this; try { for (var names_1 = tslib_1.__values(names), names_1_1 = names_1.next(); !names_1_1.done; names_1_1 = names_1.next()) { var name_6 = names_1_1.value; if (!this.listeners[name_6]) { this.listeners[name_6] = new Set(); } var set = this.listeners[name_6]; set.add(callback); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (names_1_1 && !names_1_1.done && (_a = names_1.return)) _a.call(names_1); } finally { if (e_3) throw e_3.error; } } return function () { var e_4, _a; try { for (var names_2 = tslib_1.__values(names), names_2_1 = names_2.next(); !names_2_1.done; names_2_1 = names_2.next()) { var name_7 = names_2_1.value; if (_this_1.listeners[name_7]) { _this_1.listeners[name_7].delete(callback); } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (names_2_1 && !names_2_1.done && (_a = names_2.return)) _a.call(names_2); } finally { if (e_4) throw e_4.error; } } }; }; Field.prototype._get = function (name) { return name in this.fieldsMeta ? this.fieldsMeta[name] : null; }; Field.prototype._getInitMeta = function (name) { if (!(name in this.fieldsMeta)) { this.fieldsMeta[name] = Object.assign({ name: name }, initMeta); } return this.fieldsMeta[name]; }; Field.prototype._getErrorsGroup = function (_a) { var _this_1 = this; var errors = _a.errors, fieldNames = _a.fieldNames; var errorsGroup = null; if (errors && errors.length) { errorsGroup = {}; errors.forEach(function (e) { var fieldName = e.field; if (!errorsGroup[fieldName]) { errorsGroup[fieldName] = { errors: [], }; } var fieldErrors = errorsGroup[fieldName].errors; fieldErrors.push(e.message); }); } if (errorsGroup) { // update error in every Field Object.keys(errorsGroup).forEach(function (i) { var field = _this_1._get(i); if (field) { field.errors = (0, utils_1.getErrorStrs)(errorsGroup[i].errors, _this_1.processErrorMessage); field.state = 'error'; } }); } var formattedGetErrors = this.formatGetErrors(fieldNames); if (formattedGetErrors) { errorsGroup = Object.assign({}, formattedGetErrors, errorsGroup); } // update to success which has no error for (var i = 0; i < fieldNames.length; i++) { var name_8 = fieldNames[i]; var field = this._get(name_8); if (field && field.rules && !(errorsGroup && name_8 in errorsGroup)) { field.state = 'success'; } } return errorsGroup; }; Field.prototype._reset = function (ns, backToDefault) { var _this_1 = this; if (typeof ns === 'string') { ns = [ns]; } var changed = false; var names = ns || Object.keys(this.fieldsMeta); var oldValues = this.getValues(names); if (!ns) { this.values = {}; } names.forEach(function (name) { var field = _this_1._get(name); if (field) { changed = true; field.value = backToDefault ? field.initValue : undefined; field.state = ''; delete field.errors; delete field.rules; delete field.rulesMap; if (_this_1.options.parseName) { _this_1.values = (0, utils_1.setIn)(_this_1.values, name, field.value); } else { _this_1.values[name] = field.value; } } _this_1._triggerFieldChange(name, _this_1.getValue(name), oldValues[name], 'reset'); }); if (changed) { this._reRender(names, 'reset'); } }; Field.prototype._resetError = function (name) { var field = this._get(name); if (field) { delete field.errors; //清空错误 field.state = ''; } }; Field.prototype._reRender = function (name, action) { var _this_1 = this; // 指定了字段列表且字段存在对应的自定义渲染函数 if (name) { var names = Array.isArray(name) ? name : [name]; if (names.length && names.every(function (n) { return _this_1.reRenders[n]; })) { names.forEach(function (n) { var reRender = _this_1.reRenders[n]; reRender(action); }); return; } } if (this.com) { if (!this.options.forceUpdate && this.com.setState) { this.com.setState({}); } else if (this.com.forceUpdate) { this.com.forceUpdate(); //forceUpdate 对性能有较大的影响,成指数上升 } } }; /** * Get errors using `getErrors` and format to match the structure of errors returned in field.validate */ Field.prototype.formatGetErrors = function (names) { var errors = this.getErrors(names); var formattedErrors = null; for (var field in errors) { if (errors.hasOwnProperty(field) && errors[field]) { var errorsObj = errors[field]; if (!formattedErrors) { formattedErrors = {}; } formattedErrors[field] = { errors: errorsObj }; } } return formattedErrors; }; /** * call native event from props.onXx * eg: props.onChange props.onBlur props.onFocus */ Field.prototype._callNativePropsEvent = function (action, props) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } action in props && typeof props[action] === 'function' && props[action].apply(props, tslib_1.__spreadArray([], tslib_1.__read(args), false)); }; Field.prototype._proxyFieldValue = function (field) { var _this = this; Object.defineProperty(field, 'value', { configurable: true, enumerable: true, get: function () { return (0, utils_1.getIn)(_this.values, this.name); }, set: function (v) { // 此处 this 解释同上 _this.values = (0, utils_1.setIn)(_this.values, this.name, v); return true; }, }); }; /** * update field.value and validate */ Field.prototype._updateFieldValue = function (name) { var others = []; for (var _i = 1; _i < arguments.length; _i++) { others[_i - 1] = arguments[_i]; } var e = others[0]; var field = this._get(name); if (!field) { return; } field.value = field.getValueFormatter ? field.getValueFormatter.apply(this, others) : (0, utils_1.getValueFromEvent)(e); field.inputValues = others; if (this.options.parseName) { this.values = (0, utils_1.setIn)(this.values, name, field.value); } else { this.values[name] = field.value; } }; /** * ref must always be the same function, or if not it will be triggerd every time. */ Field.prototype._getCacheBind = function (name, action, fn) { var cache = (this.cachedBind[name] = this.cachedBind[name] || {}); if (!cache[action]) { cache[action] = fn.bind(this, name); } return cache[action]; }; Field.prototype._setCache = function (name, action, hander) { var cache = (this.cachedBind[name] = this.cachedBind[name] || {}); cache[action] = hander; }; Field.prototype._getCache = function (name, action) { var cache = this.cachedBind[name] || {}; return cache[action]; }; Field.prototype._saveRef = function (name, component) { var key = "".concat(name, "_field"); var autoUnmount = this.options.autoUnmount; if (!component && autoUnmount) { // more than one component, do nothing this.instanceCount[name] && this.instanceCount[name]--; if (this.instanceCount[name] > 0) { return; } // component with same name (eg: type ? <A name="n"/>:<B name="n"/>) // while type changed, B will render before A unmount. so we should cached value for B // step: render -> B mount -> 1. _saveRef(A, null) -> 2. _saveRef(B, ref) -> render // 1. _saveRef(A, null) var cache = this.fieldsMeta[name]; if (cache) { if (this.options.parseName) { // 若 parseName 模式下,因为 value 为 getter、setter,所以将当前值记录到_value 内 cache._value = cache.value; } this._setCache(name, key, cache); } // after destroy, delete data delete this.instance[name]; delete this.reRenders[name]; var oldValue = this.getValue(name); this.remove(name); var newValue = this.getValue(name); this._triggerFieldChange(name, newValue, oldValue, 'unmount'); return; } // 2. _saveRef(B, ref) (eg: same name but different compoent may be here) if (autoUnmount && !this.fieldsMeta[name] && this._getCache(name, key)) { var cache = this._getCache(name, key); this.fieldsMeta[name] = cache; // 若 parseName 模式,则使用_value 作为值设置到 values 内 this.setValue(name, this.options.parseName ? cache._value : cache.value, false, false); this.options.parseName && '_value' in cache && delete cache._value; } // only one time here var field = this._get(name); if (field) { //When the autoUnmount is false, the component uninstallation needs to clear the verification information to avoid blocking the validation. if (!component && !autoUnmount) { field.state = ''; delete field.errors; delete field.rules; delete field.rulesMap; } var ref = field.ref; if (ref) { if (typeof ref === 'string') { throw new Error("can not set string ref for ".concat(name)); } else if (typeof ref === 'function') { ref(component); } else if (typeof ref === 'object' && 'current' in ref) { // while ref = React.createRef() ref={ current: null} ref.current = component; } } // mount if (autoUnmount && component) { var cnt = this.instanceCount[name]; if (!cnt) { cnt = 0; } this.instanceCount[name] = cnt + 1; } this.instance[name] = component; } }; Field.prototype._validate = function (name, rule, trigger) { var _a, _b; var _this_1 = this; var field = this._get(name); if (!field) { return; } var value = field.value; field.state = 'loading'; var validate = this._getCache(name, trigger); if (validate && typeof validate.abort === 'function') { validate.abort(); } validate = new validate_1.default((_a = {}, _a[name] = rule, _a), { messages: this.options.messages }); this._setCache(name, trigger, validate); validate.validate((_b = {}, _b[name] = value, _b), function (errors) { var newErrors, newState; if (errors && errors.length) { newErrors = (0, utils_1.getErrorStrs)(errors, _this_1.processErrorMessage); newState = 'error'; } else { newErrors = []; newState = 'success'; } var reRender = false; // only status or errors changed, Rerender if (newState !== field.state || !field.errors || newErrors.length !== field.errors.length || newErrors.find(function (e, idx) { return e !== field.errors[idx]; })) { reRender = true; } field.errors = newErrors; field.state = newState; reRender && _this_1._reRender(name, 'validate'); }); }; /** * splice array */ Field.prototype._spliceArrayValue = function (key, index, howmany) { var e_5, _a; var _this_1 = this; var argv = []; for (var _i = 3; _i < arguments.length; _i++) { argv[_i - 3] = arguments[_i]; } var argc = argv.length; var offset = howmany - argc; // how the reset fieldMeta move var startIndex = index + howmany; // 计算起点 /** * eg: call _spliceArrayValue('key', 1) to delete 'key.1': * case 1: names=['key.0', 'key.1']; delete 'key.1'; * case 2: names=['key.0', 'key.1', 'key.2']; key.1= key.2; delete key.2; * case 3: names=['key.0.name', 'key.0.email', 'key.1.name', 'key.1.email'], should delete 'key.1.name', 'key.1.email' * eg: call _spliceArrayValue('key', 1, item) to add 'key.1': * case 1: names=['key.0']; add 'key.1' = item; * case 2: names=['key.0', 'key.1']; key.2= key.1; delete key.1; add key.1 = item; */ var listMap = {}; // eg: {1:[{from: 'key.2.name', to: 'key.1.name'}, {from: 'key.2.email', to: 'key.1.email'}]} var replacedReg = /\$/g; // 替换特殊字符$ var replacedKey = key.replace(replacedReg, '\\$&'); var keyReg = new RegExp("^(".concat(replacedKey, ".)(\\d+)")); var replaceArgv = []; var names = this.getNames(); var willChangeNames = []; // logic of offset fix begin names.forEach(function (n) { var ret = keyReg.exec(n); if (ret) { var idx_1 = parseInt(ret[2]); // get index of 'key.0.name' if (idx_1 >= startIndex) { var l = listMap[idx_1]; var item = { from: n, to: n.replace(keyReg, function (match, p1) { return "".concat(p1).concat(idx_1 - offset); }), }; willChangeNames.push(item.from); if (names.includes(item.to)) { willChangeNames.push(item.to); } if (!l) { listMap[idx_1] = [item]; } else { l.push(item); } } // in case of offsetList.length = 0, eg: delete last element if (offset > 0 && idx_1 >= index && idx_1 < index + howmany) { replaceArgv.push(n); } } }); var oldValues = this.getValues(willChangeNames); // sort with index eg: [{index:1, list: [{from: 'key.2.name', to: 'key.1.name'}]}, {index:2, list: [...]}] var offsetList = Object.keys(listMap) .map(function (i) { return { index: Number(i), list: listMap[i], }; }) .sort(function (a, b) { return (offset > 0 ? a.index - b.index : b.index - a.index); }); offsetList.forEach(function (l) { var list = l.list; list.forEach(function (i) { _this_1.fieldsMeta[i.to] = _this_1.fieldsMeta[i.from]; // 移位后,同步调整 name _this_1.fieldsMeta[i.to].name = i.to; }); }); // delete copy data if (offsetList.length > 0) { var removeList = offsetList.slice(offsetList.length - (offset < 0 ? -offset : offset), offsetList.length); removeList.forEach(function (item) { item.list.forEach(function (i) { delete _this_1.fieldsMeta[i.from]; }); }); } else { // will get from this.values while rerender replaceArgv.forEach(function (i) { delete _this_1.fieldsMeta[i]; }); } var p = this.getValue(key); if (p) { p.splice.apply(p, tslib_1.__spreadArray([index, howmany], tslib_1.__read(argv), false)); } try { for (var willChangeNames_2 = tslib_1.__values(willChangeNames), willChangeNames_2_1 = willChangeNames_2.next(); !willChangeNames_2_1.done; willChangeNames_2_1 = willChangeNames_2.next()) { var name_9 = willChangeNames_2_1.value; this._triggerFieldChange(name_9, this.getValue(name_9), oldValues[name_9], 'setValue'); } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (willChangeNames_2_1 && !willChangeNames_2_1.done && (_a = willChangeNames_2.return)) _a.call(willChangeNames_2); } finally { if (e_5) throw e_5.error; } } this._reRender(); }; Field.prototype._triggerFieldChange = function (name, value, oldValue, triggerType) { var e_6, _a; // same value should not trigger change if (Object.is(value, oldValue)) { return; } var listenerSet = this.listeners[name]; if (!(listenerSet === null || listenerSet === void 0 ? void 0 : listenerSet.size)) { return; } try { for (var listenerSet_1 = tslib_1.__values(listenerSet), listenerSet_1_1 = listenerSet_1.next(); !listenerSet_1_1.done; listenerSet_1_1 = listenerSet_1.next()) { var callback = listenerSet_1_1.value; callback(name, value, oldValue, triggerType); } } catch (e_6_1) { e_6 = { error: e_6_1 }; } finally { try { if (listenerSet_1_1 && !listenerSet_1_1.done && (_a = listenerSet_1.return)) _a.call(listenerSet_1); } finally { if (e_6) throw e_6.error; } } }; Field.prototype._triggerAfterValidateRerender = function (errorsGroup) { if (typeof this.afterValidateRerender === 'function') { this.afterValidateRerender({ errorsGroup: errorsGroup, options: this.options, instance: this.instance, }); } }; return Field; }()); tslib_1.__exportStar(require("./types"), exports); exports.default = Field;