UNPKG

zent

Version:

一套前端设计语言和基于React的实现

647 lines (526 loc) 22.1 kB
'use strict'; 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;