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,undefinedandnullmeans 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
setErroras 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())