1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 1x 9x 9x 9x 19x 19x 19x 19x 19x 19x 9x 9x 9x 9x 9x 9x 10x 9x 9x 9x 10x 5x 5x 5x 10x 47x 28x 1x 1x | import React, { Component, PropTypes } from "react"; import merge from "lodash/merge"; import union from "lodash/union"; import validationRules from "./validationRules"; import createValidationRulesFromInput from "./createValidationRulesFromInput"; const _ = { merge, union }; class Validate extends Component { static checkErrorCount(errorObject) { return Object.keys(errorObject).length && Object.keys(errorObject).reduce((acc, curr) => { const total = acc += Object.keys(errorObject[curr]).length; return total; }, 0); } constructor(props) { super(props); this.state = { errorMessages: {}, argumentSeperator: ":", allValid: false, errorCount: 0, }; this.handleValidate = this.handleValidate.bind(this); this.testForValidation = this.testForValidation.bind(this); } componentWillMount() { const validations = _.merge( {}, createValidationRulesFromInput(this.renderChildren()), this.props.validations, ); this.setState({ validations, }); } handleValidate(e) { const fieldName = e.target.name; const fieldValue = e.target.value; const fieldErrorMessages = this.testForValidation(fieldName, fieldValue); const allErrors = Object.assign( {}, this.state.errorMessages, { [fieldName]: fieldErrorMessages }, ); const errorCount = Validate.checkErrorCount(allErrors); this.setState({ errorMessages: allErrors, errorCount, allValid: errorCount === 0, }); } ruleHasArgument(rule) { return rule.indexOf(this.state.argumentSeperator) >= 0; } testForValidation(field, value) { const fieldRequirements = this.state.validations[field]; // combine both the built in rules and custom rules const combinedValidationRules = _.merge({}, validationRules, this.props.rules); return fieldRequirements && fieldRequirements.map(rule => { if (this.ruleHasArgument(rule)) { const [funcName, arg] = rule.split(this.state.argumentSeperator); return ( combinedValidationRules[funcName] && !combinedValidationRules[funcName].test(arg)(value) && combinedValidationRules[funcName].message(arg)(field, value) ); } return ( combinedValidationRules[rule] && !combinedValidationRules[rule].test(value) && combinedValidationRules[rule].message(field, value) ); }).filter(val => val); } renderChildren() { return this.props.children({ validate: this.handleValidate, errorMessages: this.state.errorMessages, allValid: this.state.allValid, errorCount: this.state.errorCount, }); } render() { return this.renderChildren(); } } Validate.propTypes = { children: PropTypes.func.isRequired, validations: PropTypes.objectOf(PropTypes.array), rules: PropTypes.shape({ test: PropTypes.func, message: PropTypes.func, }), }; Validate.defaultProps = { validations: {}, rules: {}, }; export default Validate; |