Skip to content

Instantly share code, notes, and snippets.

@Alex-Bond
Last active December 29, 2019 01:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alex-Bond/c45458983481ca5f092e18f08d7fc343 to your computer and use it in GitHub Desktop.
Save Alex-Bond/c45458983481ca5f092e18f08d7fc343 to your computer and use it in GitHub Desktop.
Smart validator - not finished
import * as React from 'react'
import validate from 'validate.js'
import { ValidationRules } from 'utils/validate'
import { findValueByPath } from 'utils/path'
type FieldConfiguration = {
key: string
validation?: ValidationRules
defaultValue?: any
nested?: FieldConfiguration[]
}
type Field = {
value?: any
errors?: string[]
}
type Fields = {
[key: string]: Field
}
type Errors = {
[key: string]: []
}
type FormState = {
fields: Fields
errors: Errors
}
export interface WithFormProps {
form: FormContainerInterface
formFields: Fields
}
export interface FormContainerInterface {
handleChange: (field: string) => (newVal: any) => void
getValue: (key: string) => any
getErrors: (key: string) => string[]
validate: () => void
}
export function Form(inputFieldsConfig: FieldConfiguration[]): <P extends WithFormProps>(WrappedComponent: React.ComponentClass<P>) => void {
return <P extends WithFormProps>(WrappedComponent: React.ComponentClass<P>) =>
class Form extends React.Component<P, FormState> implements FormContainerInterface {
state = {
fields: {},
errors: {}
} as FormState
constructor(props: Readonly<P>) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
componentDidMount(): void {
const defaultValues = {}
inputFieldsConfig.forEach(i => {
if (i.defaultValue !== null) {
defaultValues[i.key] = {
value: i.defaultValue
}
}
})
this.setState({ fields: defaultValues })
}
handleChange = (field: string) => (newVal: any) => {
this.setState((oldState: FormState) => {
if (!oldState.fields[field]) {
oldState.fields[field] = { value: newVal }
} else {
oldState.fields[field].value = newVal
}
return oldState
})
}
validate() {
const results = {}
inputFieldsConfig.forEach(i => {
if (i.validation) {
try {
const val = findValueByPath(this.state.fields, `${i.key}.value`)
const rotateValidation = (val, nums: number[] = []) => {
if (Array.isArray(val)) {
return val.map((i, k) => rotateValidation(i, [...nums, k]))
} else {
return {
result: validate.single(val, i.validation),
nums
}
}
}
const result = rotateValidation(val)
if (result) {
results[i.key] = result
}
} catch (e) {
console.log('Error while validating: ', e.toString())
}
}
})
this.setState({ errors: results })
}
getValue = (key: string): any => {
return findValueByPath(this.state.fields, `${key}.value`)
}
getErrors = (key: string): string[] => {
// if (Array.isArray(this.state.fields[key]?.errors) && typeof this.state.fields[key]?.errors[0] !== 'string') {
// return []
// }
return findValueByPath(this.state.fields, `${key}.errors`) || []
}
render() {
return (
<WrappedComponent {...this.props} form={this} formFields={Object.assign({}, this.state.fields)} />
)
}
}
}
import { flatten } from 'lodash'
import objectPath from 'object-path'
type PathItem = string | number
const preparePath = (path: string) => {
return flatten(
path
.split('.')
.map(i => {
if (/\[(\d)\]$/.test(i)) {
const arr = i.split(/\[(\d)\]/)
return [arr[0], parseInt(arr[1])]
}
return i
})
.map(i => {
// @ts-ignore
if (/\[\]$/.test(i)) {
// @ts-ignore
const arr = i.split(/\[\]/)
return [arr[0], true]
}
return i
})
)
}
const findValue = (obj, path: PathItem[]) => {
if (typeof obj === 'undefined') return obj
if (path.length === 1) {
return obj[path[0]]
}
if (typeof path[1] === 'number') {
const val = obj[path[0]] ? obj[path[0]][path[1]] : null
if (val === null || path.length === 2) {
return val
}
return findValue(val, path.splice(2))
}
if (typeof path[1] === 'boolean') {
const val = obj[path[0]]
if (!val) {
return val
}
if (Array.isArray(val)) {
if (path.length > 2 && val.length === 0) {
throw new Error('Object not found')
}
const nextPath = path.splice(2)
return val.map(i => findValue(i, nextPath))
} else {
throw new Error('Object is not array')
}
}
}
export const findValueByPath = (obj: {}, path: string) => {
return findValue(obj, preparePath(path))
}
export const setValueByPath = (obj: {}, path: string, value: any) => {
return objectPath.set(obj, path.replace('[', '').replace(']', ''), value)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment