Validate
Validation itself is up to you, stapp-validate
module provides a way to connect your favorite validation library to the application and store validation results in the applications state. stapp-validate
module is intended to be used with stapp-formbase
module.
Debouncing, throttling and other time-controlling mechanisms are up to you.
Installation
npm install stapp-`validate` stapp stapp-formbase rxjs reselect
# OR using stapp-cli-tools
stapp install stapp-validate
Peer dependencies
- stapp: >= 2.6
- stapp-formbase: >= 2.6
- reselect: >= 4
- rxjs: >= 6
Definition
type validate = (config: {
rules: { [K: string]: ValidationRule },
validateOnInit: boolean // defaults to `true`
setTouchedOnSubmit?: boolean // defaults to `true`
}) => Module<{}, { validating: { [K: string]: boolean } }>
type ValidationRule<State extends FormBaseState> = (
value: string | void,
fieldName: string,
state: State,
flags: {
onChange?: boolean,
onInit?: boolean,
onRevalidate?: boolean
}
) => any
Usage
Import stapp-validate
module and provide some rules. Don't forget stapp-formbase
module.
import { createApp } from 'stapp'
import { formBase } from 'stapp-formbase'
import { validate } from 'stapp-validate'
const app = createApp({
modules: [formBase(), validate({
rules: [...rules]
})]
})
Validation rules are called each on its field change, right after initialization (can be disabled) and on revalidate
event (exported, but not mixed into application api). Each rule receives these arguments:
value
: new value of the fieldfieldName
: name of the changed fieldstate
: the whole applications stateflags
: flags indicate the reason why the rule was called
Validation rule can return anything. Here are some rules:
false
,undefined
andnull
means that everything is okconst app = createApp({ modules: [ validate({ rules: { age: value => value < 18 ? 'Too young!' : null } }) ] }) app.subscribe(state => console.log(state.errors)) app.dispatch(setValue({ age: 10 })) // { age: 'Too young!' } app.dispatch(setValue({ age: 50 })) // {}
objects will be passed to
setError
as isconst app = createApp({ modules: [ validate({ rules: { name (name, fieldName, state) { if (name !== state.values.username) { return } return { name: 'Username and name cannot be equal!', username: 'Username and name cannot be equal!' } } } }) ] }) app.subscribe(state => console.log(state.errors)) app.dispatch(setValue({ name: 'john', username: 'john' })) // { // name: 'Username and name cannot be equal!' // username: 'Username and name cannot be equal!' // }
promises will be awaited, and these rules will be applied to their result recursively
const app = createApp({ modules: [ validate({ rules: { async age (value, fieldName, state) { await wait(500) return value < 18 ? 'Too young!' : null } } }) ] }) app.subscribe(console.log) app.dispatch(setValue({ age: 10 })) // { // values: { age: 10 }, // ready: { age: false }, // validating: { age: true } // errors: {} // } // some time later // { // values: { age: 10 }, // ready: { age: true }, // validating: { age: false }, // errors: { age: 'Too young!' } // }
any other value returned from a validation function will be regarded as an error value (Error instances, arrays, strings, numbers, etc.)
isValidatingSelector()
stapp-validate
module comes with isValidatingSelector
selector creator. Use it to create memoized selector, that will return true
or false
depending on validation state.
revalidate
revalidate
event can be used to cause validation. Accepts an optional array of field names to revalidate.
import { revalidate } from 'stapp-validate'
// Dispatch an array of field names to revalidate selectively...
app.dispatch(revalidate(['fieldA', 'fieldB']))
// or without arguments to revalidate all fields
app.dispatch(revalidate())