zent
Version:
一套前端设计语言和基于React的实现
647 lines (526 loc) • 22.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _react = require('react');
var _omit = require('lodash/omit');
var _omit2 = _interopRequireDefault(_omit);
var _find = require('lodash/find');
var _find2 = _interopRequireDefault(_find);
var _noop = require('lodash/noop');
var _noop2 = _interopRequireDefault(_noop);
var _assign = require('lodash/assign');
var _assign2 = _interopRequireDefault(_assign);
var _isEqual = require('lodash/isEqual');
var _isEqual2 = _interopRequireDefault(_isEqual);
var _some = require('lodash/some');
var _some2 = _interopRequireDefault(_some);
var _get = require('lodash/get');
var _get2 = _interopRequireDefault(_get);
var _isPromise = require('../utils/isPromise');
var _isPromise2 = _interopRequireDefault(_isPromise);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _utils = require('./utils');
var _validationRules = require('./validationRules');
var _validationRules2 = _interopRequireDefault(_validationRules);
var _handleSubmit = require('./handleSubmit');
var _handleSubmit2 = _interopRequireDefault(_handleSubmit);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var emptyArray = []; /* eslint-disable no-underscore-dangle */
var checkSubmit = function checkSubmit(submit) {
if (!submit || typeof submit !== 'function') {
throw new Error('You must either pass handleSubmit() an onSubmit function or pass onSubmit as a prop');
}
return submit;
};
var createForm = function createForm() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var formValidations = config.formValidations;
var validationRules = (0, _assign2['default'])({}, _validationRules2['default'], formValidations);
return function (WrappedForm) {
var _class, _temp;
return _temp = _class = function (_ref) {
(0, _inherits3['default'])(Form, _ref);
function Form(props) {
(0, _classCallCheck3['default'])(this, Form);
var _this = (0, _possibleConstructorReturn3['default'])(this, (Form.__proto__ || Object.getPrototypeOf(Form)).call(this, props));
_this.submitCompleted = function (result) {
delete _this.submitPromise;
return result;
};
_this.submitFailed = function (error) {
delete _this.submitPromise;
throw error;
};
_this.listenToSubmit = function (promise) {
if (!(0, _isPromise2['default'])(promise)) {
return promise;
}
// 当submit是一个promise时,需要一个标识表明正在提交,提交结束后删除标识
_this.submitPromise = promise;
return promise.then(_this.submitCompleted, _this.submitFailed);
};
_this.submit = function (submitOrEvent) {
var onSubmit = _this.props.onSubmit;
// 在表单中手动调用handleSubmit或者把handleSubmit赋值给表单的onSubmit回调
// handleSubmit赋值给表单的onSubmit时,submitOrEvent是一个event对象
// handleSubmit的参数必须是function
if (!submitOrEvent || (0, _utils.silenceEvent)(submitOrEvent)) {
if (!_this.submitPromise) {
// 调用props传入的onSubmit方法
return _this.listenToSubmit((0, _handleSubmit2['default'])(checkSubmit(onSubmit), _this));
}
} else {
// submitOrEvent是一个自定义submit函数,返回一个promise对象
return (0, _utils.silenceEvents)(function () {
return !_this.submitPromise && _this.listenToSubmit((0, _handleSubmit2['default'])(checkSubmit(submitOrEvent), _this));
});
}
};
_this.isSubmitting = function () {
return _this.state.isSubmitting;
};
_this.isValid = function () {
return _this.state.isFormValid;
};
_this.setFieldValidationErrors = function (errors) {
var updateDirty = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
_this.fields.forEach(function (field) {
var name = field.getName();
var data = {
_isValid: !(name in errors),
_validationError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]
};
if (updateDirty) {
data._isDirty = true;
}
field.setState(data);
});
};
_this.setFieldExternalErrors = function (errors) {
var updateDirty = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
_this.fields.forEach(function (field) {
var name = field.getName();
var error = (0, _get2['default'])(errors, name);
var data = {
_isValid: false,
_externalError: typeof error === 'string' ? [error] : error
};
if (updateDirty) {
data._isDirty = true;
}
field.setState(data);
});
};
_this.setFormDirty = function (isDirty) {
_this.fields.forEach(function (field) {
field.setState({
_isDirty: isDirty
});
});
};
_this.setFormPristine = function (isPristine) {
_this.fields.forEach(function (field) {
field.setState({
_isDirty: !isPristine
});
});
};
_this.initialize = function (data) {
_this.fields.forEach(function (field) {
var name = field.getName();
var value = (0, _get2['default'])(data, name);
if (value) {
field.setInitialValue(value);
} else {
field.setInitialValue();
}
});
};
_this.resetFieldsValue = function (data) {
_this.fields.forEach(function (field) {
var name = field.getName();
var value = (0, _get2['default'])(data, name);
if (value !== undefined) {
field.setValue(value);
} else {
field.resetValue();
}
});
};
_this.setFieldsValue = function (data) {
_this.fields.forEach(function (field) {
var name = field.getName();
var value = (0, _get2['default'])(data, name);
if (value) {
field.setValue(value);
}
});
};
_this.reset = function (data) {
_this.setFormDirty(false);
_this.resetFieldsValue(data);
};
_this.isFieldDirty = function (name) {
var field = (0, _find2['default'])(_this.fields, function (component) {
return component.getName() === name;
});
if (!field) return false;
return field.isDirty();
};
_this.isFieldValidating = function (name) {
var field = (0, _find2['default'])(_this.fields, function (component) {
return component.getName() === name;
});
if (!field) return false;
return field.isValidating();
};
_this.getFieldError = function (name) {
var field = (0, _find2['default'])(_this.fields, function (component) {
return component.getName() === name;
});
if (!field) return '';
return field.getErrorMessage();
};
_this.getFormValues = function () {
var assignValue = function assignValue(values, keyPath, newValue) {
if (keyPath.length === 0) {
return;
}
var currentKey = keyPath[0];
if (/\[\d+\]/.test(currentKey)) {
// array
var index = currentKey.match(/\d+(?=\])/)[0];
currentKey = currentKey.replace(/\[\d+\]/, '');
if (!values[currentKey]) {
values[currentKey] = [];
}
if (keyPath.length > 1) {
index > values[currentKey].length - 1 ? values[currentKey].push({}) : null;
assignValue(values[currentKey][index], keyPath.slice(1), newValue);
} else {
values[currentKey][index] = newValue;
}
} else {
// object
if (!values[currentKey]) {
values[currentKey] = {};
}
if (keyPath.length > 1) {
assignValue(values[currentKey], keyPath.slice(1), newValue);
} else {
values[currentKey] = newValue;
}
}
};
return _this.fields.reduce(function (values, field) {
var name = field.getName();
var fieldValue = field.getValue();
var fieldNamePath = name.split('.');
assignValue(values, fieldNamePath, fieldValue);
return values;
}, {});
};
_this.getValidationErrors = function () {
return _this.fields.reduce(function (errors, field) {
var name = field.getName();
errors[name] = field.getErrorMessage();
return errors;
}, {});
};
_this.getInitialValues = function () {
return _this.fields.reduce(function (values, field) {
var name = field.getName();
values[name] = field.getInitialValue();
return values;
}, {});
};
_this.isChanged = function () {
return !(0, _isEqual2['default'])(_this.getInitialValues(), _this.getFormValues());
};
_this.isValidating = function () {
return (0, _some2['default'])(_this.fields, function (field) {
return field.isValidating();
});
};
_this.isValidValue = function (field, value) {
return _this.runValidation(field, value).isValid;
};
_this.runValidation = function (field) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : field.getValue();
var formValidationErrors = _this.props.validationErrors;
var _field$props = field.props,
name = _field$props.name,
validationError = _field$props.validationError,
validationErrors = _field$props.validationErrors;
var currentValues = _this.getFormValues();
var validationResults = _this.runRules(value, currentValues, field._validations);
var isValid = !validationResults.failed.length && !(formValidationErrors && formValidationErrors[field.getName()]);
return {
isValid: isValid,
error: function () {
if (isValid) {
return emptyArray;
}
if (validationResults.errors.length) {
return validationResults.errors;
}
if (formValidationErrors && formValidationErrors[name]) {
return typeof formValidationErrors[name] === 'string' ? [formValidationErrors[name]] : formValidationErrors[name];
}
if (validationResults.failed.length) {
return validationResults.failed.map(function (failed) {
return validationErrors[failed] ? validationErrors[failed] : validationError;
}).filter(function (x, pos, arr) {
// Remove duplicates
return arr.indexOf(x) === pos;
});
}
}()
};
};
_this.runRules = function (value, currentValues) {
var validations = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var results = {
errors: [],
failed: []
};
function updateResults(validation, validationMethod) {
// validation方法可以直接返回错误信息,否则需要返回布尔值表明校验是否成功
if (typeof validation === 'string') {
results.errors.push(validation);
results.failed.push(validationMethod);
} else if (!validation) {
results.failed.push(validationMethod);
}
}
Object.keys(validations).forEach(function (validationMethod) {
// validations中不指定function则必须是内置的rule
if (!validationRules[validationMethod] && typeof validations[validationMethod] !== 'function') {
throw new Error('does not have the validation rule: ' + validationMethod);
}
// 使用自定义校验方法或内置校验方法(可以按需添加)
if (typeof validations[validationMethod] === 'function') {
var validation = validations[validationMethod](currentValues, value);
updateResults(validation, validationMethod);
} else {
var _validation = validationRules[validationMethod](currentValues, value, validations[validationMethod]);
updateResults(_validation, validationMethod);
}
});
return results;
};
_this.onValidationComplete = function () {
var allIsValid = _this.fields.every(function (field) {
return field.isValid();
});
_this.setState({
isFormValid: allIsValid
});
if (allIsValid) {
_this.props.onValid();
} else {
_this.props.onInvalid();
}
};
_this.validate = function (field) {
// 初始化时调用validate不触发onChange
if (_this._isMounted) {
_this.props.onChange(_this.getFormValues(), _this.isChanged());
}
var validation = _this.runValidation(field);
field.setState({
_isValid: validation.isValid,
_validationError: validation.error,
_externalError: null
}, _this.validateForm);
};
_this.asyncValidate = function (field, value) {
var asyncValidation = field.props.asyncValidation;
var values = _this.getFormValues();
if (!asyncValidation && field.state._validationError.length) return;
field.setState({
_isValidating: true
});
var promise = asyncValidation(values, value);
if (!(0, _isPromise2['default'])(promise)) {
throw new Error('asyncValidation function must return a promise');
}
var handleResult = function handleResult(rejected) {
return function (error) {
field.setState({
_isValidating: false,
_isValid: !rejected && field.state._validationError.length === 0,
_externalError: error ? [error] : null,
_asyncValidated: true
});
if (rejected) {
_this.setState({
isFormValid: false
});
throw new Error(error);
}
};
};
return promise.then(handleResult(false), handleResult(true));
};
_this.isFormAsyncValidated = function () {
var allIsAsyncValid = _this.fields.every(function (field) {
return field.isAsyncValidated() || !field.props.asyncValidation;
});
return allIsAsyncValid;
};
_this.validateForm = function () {
_this.fields.forEach(function (field, index) {
var _externalError = field.state._externalError;
var validation = _this.runValidation(field);
if (validation.isValid && _externalError) {
validation.isValid = false;
}
field.setState({
_isValid: validation.isValid,
_validationError: validation.error,
_externalError: !validation.isValid && _externalError ? _externalError : null
}, index === _this.fields.length - 1 ? _this.onValidationComplete : null);
});
};
_this.attachToForm = function (field) {
if (_this.fields.indexOf(field) < 0) {
_this.fields.push(field);
}
// form初始化时不校验,后续动态添加的元素再校验
_this._isMounted && _this.validate(field);
};
_this.detachFromForm = function (field) {
var fieldPos = _this.fields.indexOf(field);
if (fieldPos >= 0) {
_this.fields.splice(fieldPos, 1);
}
_this.validateForm();
};
_this.getWrappedForm = function () {
return _this.wrappedForm;
};
_this.state = {
isFormValid: true,
isSubmitting: false
};
_this.fields = [];
_this._isMounted = false;
return _this;
}
(0, _createClass3['default'])(Form, [{
key: 'getChildContext',
value: function getChildContext() {
return {
zentForm: {
attachToForm: this.attachToForm,
detachFromForm: this.detachFromForm,
validate: this.validate,
asyncValidate: this.asyncValidate,
getFormValues: this.getFormValues,
getFieldError: this.getFieldError,
isValidValue: this.isValidValue,
setFieldExternalErrors: this.setFieldExternalErrors,
resetFieldsValue: this.resetFieldsValue,
setFormDirty: this.setFormDirty,
setFormPristine: this.setFormDirty,
isValid: this.isValid,
isSubmitting: this.isSubmitting
}
};
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
this.validateForm();
this._isMounted = true;
}
}, {
key: 'componentWillUpdate',
value: function componentWillUpdate() {
this.prevFieldNames = this.fields.map(function (field) {
return field.getName();
});
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
var validationErrors = this.props.validationErrors;
if (validationErrors && (typeof validationErrors === 'undefined' ? 'undefined' : (0, _typeof3['default'])(validationErrors)) === 'object' && Object.keys(validationErrors).length > 0) {
this.setFieldValidationErrors(validationErrors);
}
var newFieldNames = this.fields.map(function (field) {
return field.getName();
});
if (!(0, _isEqual2['default'])(this.prevFieldNames, newFieldNames)) {
this.validateForm();
}
}
// 设置服务端返回的错误信息
}, {
key: 'render',
value: function render() {
var _this2 = this;
var passableProps = (0, _omit2['default'])(this.props, ['validationErrors', 'handleSubmit', 'onChange']);
return (0, _react.createElement)(WrappedForm, (0, _extends3['default'])({}, passableProps, {
ref: function ref(_ref2) {
_this2.wrappedForm = _ref2;
},
handleSubmit: this.submit,
zentForm: {
getFormValues: this.getFormValues,
getFieldError: this.getFieldError,
setFieldExternalErrors: this.setFieldExternalErrors,
resetFieldsValue: this.resetFieldsValue,
setFieldsValue: this.setFieldsValue,
setFormDirty: this.setFormDirty,
setFormPristine: this.setFormPristine,
initialize: this.initialize,
isFieldDirty: this.isFieldDirty,
isFieldTouched: this.isFieldDirty,
isFieldValidating: this.isFieldValidating,
isValid: this.isValid,
isValidating: this.isValidating,
isSubmitting: this.isSubmitting,
isFormAsyncValidated: this.isFormAsyncValidated
}
}));
}
}]);
return Form;
}(_react.PureComponent || _react.Component), _class.displayName = 'Form(' + (0, _utils.getDisplayName)(WrappedForm) + ')', _class.WrappedForm = WrappedForm, _class.propTypes = {
onSubmit: _propTypes2['default'].func,
onSubmitSuccess: _propTypes2['default'].func,
onSubmitFail: _propTypes2['default'].func,
onValid: _propTypes2['default'].func,
onInvalid: _propTypes2['default'].func,
onChange: _propTypes2['default'].func,
validationErrors: _propTypes2['default'].object
}, _class.defaultProps = {
onSubmit: _noop2['default'],
onSubmitSuccess: _noop2['default'],
onSubmitFail: _noop2['default'],
onValid: _noop2['default'],
onInvalid: _noop2['default'],
onChange: _noop2['default'],
validationErrors: null
}, _class.childContextTypes = {
zentForm: _propTypes2['default'].object
}, _temp;
};
};
exports['default'] = createForm;