cerebral-forms
Signals, actions and state factories to create forms
Install
This project is still in alpha. To test alpha version check instructions in monorepo.
API
Cerebral forms is basically a function that creates state needed to handle validation and an action factory for validating fields. It is simple in nature, but handles all the complexity that comes with forms.
form
A state factory for creating form state. Used when defining initial state or dynamically with an action. You can put forms inside forms.
You can add any properties to a form where properties containing an object with a "value" property are identified as fields.
import {form} from 'cerebral-forms'
export default {
state: {
form: form({
firstName: {
value: '',
validationRules: ['minLength:3'],
validationMessages: ['Must be at least 3 characters long'],
isRequired: false,
requiredMessage: null,
dependsOn: 'app.myForm.repeatPassword'
isValueRules: ['isValue'],
defaultValue: '',
hasValue: false,
isPristine: true,
isValid: true,
errorMessage: null,
},
lastName: {
value: '',
validationRules: [{
minLength: 3,
isAlpha: true
}]
}
})
}
}
Sometimes you want more control over your form. isPristine
for example changes from false to true whenever you do a validation on the field. Sometimes you might want blur events instead to show error messages to the user. You are completely free to add whatever state you need when you create your fields. An isTouched property might be used to make blur events but you can call it whatever you like, it's just state.
import {form} from 'cerebral-forms'
export default function MyAction({state}) {
state.set('some.new.form', form({
name: {
value: '',
isTouched: false
},
age: {
value: 18
}
}))
}
Custom global props
You can add custom props to the root to the form state.
For example if you want to show validation errors only
when submitting the form you can add a showErrors
prop
which you set true when validation fails during form submit.
import {form} from 'cerebral-forms'
export default function MyAction({state}) {
state.set('some.new.form', form({
name: {
value: '',
},
showErrors: false
}))
}
Set a default value for the whole form
You can set a default value for a property using a factory:
import {form, getFormFields} from 'cerebral-forms'
const MyFormFactory = (formObject) => {
const myForm = form(formObject)
const fields = getFormFields(myForm)
myForm.showErrors = false
fields.forEach((field) => {
field.requiredMessage = field.requiredMessage || 'This field is required'
field.someProp = field.someProp || 'Some default'
})
return myForm
}
field
To add a new field you simply merge a new form into the existing one.
import {form} from 'cerebral-forms'
export default function MyAction({state}) {
state.merge('path.to.form', form({
address2: {
value: ''
}
}))
}
changeField
A chain you can use to easily change a field in some form. It will automatically validate the form. Can also be composed into any other chain.
import {changeField} from 'cerebral-forms'
export default {
state: {
form: form({
firstName: {
value: ''
}
})
},
signals: {
fieldChanged: changeField
}
}
In your view code you call the signal with path to the field and the updated value:
import React from 'react'
import {connect} from 'cerebral/react'
export default connect({
form: 'someModule.form'
}, {
fieldChanged: 'someModule.fieldChanged'
},
function MyForm({form, fieldChanged}) {
return (
<div>
<h4>First name</h4>
<input
value={form.firstName.value}
onChange={(event) => fieldChanged({
field: 'someModule.form.firstName',
value: event.target.value
})}
/>
</div>
)
}
)
validateField
An action factory you can use to validate any field in any chain.
import {props} from 'cerebral/tags'
import {validateField} from 'cerebral-forms'
export default [
doThis,
validateField('path.to.form.field'),
validateField(props`fieldPath`),
doThat
]
validateForm
An action factory you can use to validate a whole form.
import {props} from 'cerebral/tags'
import {validateForm} from 'cerebral-forms'
export default [
validateForm('path.to.form'),
validateForm(props`formPath`),
isFormValid, {
true: [
passInForm
],
false: [
setSomeErrorMessages
]
}
]
resetForm
An action factory you can use to reset any form from any chain. It will replace current value with the initial or default value defined. And revalidate.
import {props} from 'cerebral/tags'
import {resetForm} from 'cerebral-forms'
export default [
doThis,
resetForm('path.to.form'),
resetForm(props`formPath`),
doThat
]
formToJSON
A function that takes a form and returns the same structure with only values. Can be used wherever, though typically in actions. Often used to pass a form to server.
import {formToJSON} from 'cerebral-forms'
export default function MyAction({state, axios}) {
const formData = formToJSON(state.get('some.form'))
return axios.post('/data', formData)
}
getFormFields
A function that takes a form and returns an object where the key is the path to the field and the value is the field itself. Typically used to calculate the state of a form.
This examples is a function that takes a form and returns true if all fields has a value.
import {getFormFields} from 'cerebral-forms'
export default function allHasValue(form) {
const formFields = getFormFields(form)
return Object.keys(formFields).reduce((allHasValue, key) => {
if (!allHasValue || !formFields[key].hasValue) {
return false
}
return true
}, true)
}
getInvalidFormFields
A function that takes a form and returns only the fields that are invalid. The object keys are the path to the field and the value is the field itself.
import React from 'react'
import {connect} from 'cerebral/react'
import {getInvalidFormFields} from 'cerebral-forms'
export default connect({
form: 'someModule.form'
},
function MyForm({form}) {
const invalidFields = getInvalidFormFields(form)
return (
<ul>
{Object.keys(invalidFields).map((key) => (
<li key={key}>
{invalidFields[key].errorMessage}
</li>
))}
</ul>
)
}
)
isValidForm
A function that takes a form and returns true if all the fields in the form is valid. Used in both actions and components.
import React from 'react'
import {connect} from 'cerebral/react'
import {isValidForm} from 'cerebral-forms'
export default connect({
form: 'someModule.form'
},
function MyForm({form}) {
const isValid = isValidForm(form)
return (
<div>
<button disabled={!isValid}>Send form</button>
</div>
)
}
)
You can also use this function inside a chain:
import {props} from 'cerebral/tags'
import {isValidForm} from 'cerebral-forms'
export default [
isValidForm('path.to.form')
isValidForm(props`formPath`), {
true: [],
false: []
}
]
Rules
- isValue - Checks if there is a truthy value, including array length
- isExisty - Checks for truthy value
- matchRegexp - Only in object form: [{matchRegexp: /\s/g}]
- isUndefined - Checks if undefined
- isEmpty - Checks if empty string
- isEmail - Checks if valid email format
- isUrl - Checks if valid url format
- isTrue - Checks if actual true value
- isFalse - Checks if actual false value
- isNumeric - Checks value is only numeric
- isAlpha - Checks if value is only alpha characters (text)
- isAlphanumeric - Checks if either numeric or alpha characters
- isInt - Checks if value is number and no decimals
- isFloat - Checks if value is number with decimals
- isWords - Checks if multiple words in value
- isSpecialWords - Checks for special characters in words
- isLength:Number - Checks length of value with number passed
- equals:Value - Does strict equality check
- equalsField:Field - Checks equality of field in same form
- maxLength:Number - Checks value length does not pass passed number
- minLength:Number - Checks value length does pass passed number
Custom rules
You can attach new rules to the rules object.
import {rules} from 'cerebral-forms';
rules.isFirstUpperCase = (value, form, arg) => {
return typeof value === 'string' && value[0] === value[0].toUpperCase()
}