Forms
Forms provide a simple, unified state infrastructure for handling web forms, including validation, error handling, and submission.
Form Controllers
Form controllers are instantiated much like other controllers, registering the relevant data elements in your state manager. Their syntax is a bit more verbose than other modules since the frontend handles much more of the data validation.
To get a new form controller:
TypeScript
import {useForm} from '@opencraft/providence/react/hooks'
// Declare a type in TypeScript to get type checking for your form's fields.
// Note that if you do not explicitly create a type, TypeScript will attempt to infer the type
// via the default values you place on the fields when instantiating the form.
declare interface SurveyType {
age: number,
email: string,
name: string,
}
const MyComponent = () => {
controller = useForm<SurveyType>('survey', {
endpoint: 'https://example.com/api/survey/',
fields: {
age: {value: 20},
email: {value: '', validators: [{name: 'email'}]},
name: {value: ''},
}
})
// ... The rest of your component code goes here!
}
JavaScript
import {useForm} from '@opencraft/providence/react/hooks'
const MyComponent = () => {
controller = useForm('survey', {
endpoint: 'https://example.com/api/survey/',
fields: {
age: {value: 20},
email: {value: '', validators: [{name: 'email'}]},
name: {value: ''},
}
})
// ... The rest of your component code goes here!
}
TypeScript
// Coming once the Vuex module is completed.
JavaScript
// Coming once the Vuex module is completed.
Form Module Options
Forms have a handful of parameters which can be set upon instantiation.
- class FormModuleOptions()
The construction parameters are mostly state that the FormModule will be initialized with. This contains both the values of the fields themselves and meta information about the form’s status and how it is to be managed.
interface
- FormModuleOptions.disabled?
type: boolean
Whether the form should be considered disabled. This does not affect providence’s behavior but may be useful when rendering in your favorite frontend template/component framework.
- FormModuleOptions.endpoint?
type: string
The endpoint this form is to be submitted to. Defaults to ‘#’.
- FormModuleOptions.errors?
type: default
Errors on the form itself, as opposed to the individual fields. Also contains the status of the last submission attempt, normalized to a string, such as ‘500’, ‘UKNOWN’ or ‘ECONNABORTED’.
- FormModuleOptions.fields
type: FieldSetOptions<T>
Key-value sets of fields of this form. See
FieldOptions()
for the values.
- FormModuleOptions.persistent?
type: boolean
If true, this form will not be removed from the data store even when all listeners have been removed from it.
- FormModuleOptions.step?
type: number
Forms may have ‘steps’ for wizard-like functionality. This keeps track of the current step of the form. When an error is returned by the server, providence will roll the step back to the first step which contains a field with an error. See
FieldOptions()
for setting the step of a particular field.
Field Options
- class FieldOptions()
interface
- FieldOptions.debounce?
type: number
To prevent the annoying artifact of checking a value before the user is finished typing, the debounce value for the field may be set. This throttles how often the validators are run. Validators run after input has changed and the number of milliseconds specified by this attribute have passed.
- FieldOptions.disabled?
type: boolean
Indicates whether this field should be marked disabled. This does not affect providence’s behavior, but is useful when rendering fields in your desired component/template framework.
- FieldOptions.errors?
type: string[]
A list of errors this field has. These may come from the validators, or from a failed form submission.
- FieldOptions.initialValue?
type: ValType
The field’s ‘initial’ value. In most cases this is taken from the value property, but it can be specified manually if needed. Its function is to replace the current value when the form is reset.
- FieldOptions.omitIf?
type: any
If the value equals this attribute, omit the field from the resulting data when constructing it for submission. This can be used in cases where the presence of the key itself has significance, or when it could cause visual artifacts. An example might be when constructing a query string for a search page– if a searchable attribute isn’t specified, you probably don’t want it in the URL!
- FieldOptions.step?
type: number
The ‘step’ this field is on. Forms can be constructed in wizard-like fashion, with several fields on one step, and several steps in a form before submission. See the information on
the form module's step attribute
for more information.
- FieldOptions.validators?
type: ValidatorSpec<object>[]
Validators used to check the data in this field. See the documentation on Validators for more information.
- FieldOptions.value
type: ValType
The field’s current value.
Validators
Fields may have validators assigned to them. Validators are asynchronous functions that take some context information about a field’s current value and return an array of strings with any errors found when validating. This flexibility allows for both simple data validation, such as verifying if a number is within a certain range, as well as remote validation, such as contacting a server to see if a username has been taken.
Validators are added to Providence in the global configuration in the validators setting
. Providence comes with an example email validator. The validator gives useful feedback about what’s wrong with a user’s email entry while avoiding most regex validation pitfalls.
- class ValidatorArgs()
Validator functions are given a single context object argument with the following structure:
interface
- ValidatorArgs.args
type: ArgsStruct
The args value will be whatever args are specified to the field upon initialization.
For example, if the fields are specified in this way:
{ password: { value: '', validators: [{name: 'length', args: {min: 5, max: 50}}] } password2: { value: '', validators: [{name: 'matches', args: {fieldName: 'password'}}], }, }
…Then the length validator args for password will be {min: 5, max: 50} and the matches validator args for password2 will be {fieldName: ‘password’}.
- ValidatorArgs.fieldName
type: keyof T
The name of the field to be validated.
- ValidatorArgs.formState
type: FormState<T>
The current raw state of the form module in its entirety.
- ValidatorArgs.signal
type: AbortSignal
An AbortSignal connected to the current validation attempt. This might be used if the field is updated and a new attempt will replace the current one, or if the user submits the form before validation completes.
Hand this signal to any network requests you perform to ensure that the request is appropriately aborted if the validation attempt is aborted.
- ValidatorArgs.value
type: FieldValueType
The value of the field to be validated.
Controller Reference
The form controller is your interface for managing your form. Check here for a primer on controllers as a concept.
- class FormController()
interface
- Extends:
BaseController()
- FormController.data
type: T
Derives the data from the form for submission. Creates an instance of T, omitting fields that match their OmitIf value.
- FormController.endpoint
type: string
The endpoint property on the form module. This is a getter/setter, so you can update the endpoint.
- FormController.errors
type: string[]
Form-level errors. These are errors returned by the server which do not apply to any specific field. To get field-level errors, use the Fielders.
- FormController.f
type: BoundFielders<T>
Fielders.
- FormController.method
type: Method
The HTTP method used when submitting this form. Usually this is ‘post’ or ‘put’. This is a getter/setter, in case you need to update it for some reason.
- FormController.moduleType
type: <TODO: other type>
Returns ‘form’, the type of module this controller handles.
- FormController.sending
type: boolean
Returns whether the form is currently being submitted.
- FormController.status
type: string
Gets/sets The status code of the last failed submit request.
- FormController.step
type: number
Gets/sets which ‘Step’ the form is on. This is used to break up long forms into multi-step wizards.
- FormController.addFields(fieldSpec)
Adds fields to the form
- Arguments
fieldSpec (
Partial
) – FieldSetOptions with defined fields to add.
- FormController.clearErrors()
Clears all error-related settings on the form and its fields.
- FormController.delFields(names)
Deletes fields from the form.
- Arguments
names (
keyof T[]()
) – Field names to remove.
- FormController.getFieldSetting(name, settingName)
Gets a particular setting on a specific field from the form module.
- Arguments
name (
FieldName()
) – The name of the fieldsettingName (
SettingName()
) – The name of the setting on the field.
- Returns
<TODO: other type> –
- FormController.handleError(error)
A provided helper function which handles most of the cleanup when there’s an error from submitting a form. Derives the errors, and adjusts the state to show them and move the form step to the first error in the set. Use this as the error handler for the
x
function, like so:controller.submit().then(yourSuccessFunction, controller.handleError)
- Arguments
error (
AxiosError
) – An AxiosError raised when submitting the form.
- FormController.hasField(fieldName)
Returns true if the form has a field by a particular name.
- Arguments
fieldName (
string()
) –
- Returns
boolean –
- FormController.reset()
Resets the form to its default state– including all fields at their initial values and clearing all errors.
- FormController.setErrors(errorSet)
Sets the form’s error state from a FormErrorSet.
- Arguments
errorSet (
FormErrorSet
) – A FormErrorSet containing errors for this form.
- FormController.setFieldSetting(name, settingName, value)
Sets a particular setting on a specific field from the form module.
- Arguments
name (
FieldName()
) – The name of the fieldsettingName (
SettingName()
) – The name of the setting on the field.value (
<TODO: other type>()
) – The value to set.
- FormController.stopValidators()
Stops all validation currently in progress for form fields. Useful when you’re about to set errors manually but they might be overwritten by in-progress validation.
Fielders
Fielders are a special, controller-like wrapper around the fields of forms, much like patchers for singles. They handle updates to the field’s value and triggering validation. This is especially useful for cases like Vue’s v-model, but also allows for more terse functions in the case of React’s event listeners.
Say you had a form for a Survey:
declare interface Product {
age: number,
email: string,
name: string,
}
const controller = useForm<SurveyType>('survey', {
endpoint: 'https://example.com/api/survey/',
fields: {
age: {value: 20},
// We use the `email` validator for the email field.
email: {value: '', validators: [{name: 'email'}]},
name: {value: ''},
}
})
You could update the value of email by setting its value on the autogenerated fielder:
// Output: ''
console.log(controller.f.email)
controller.f.email.model = 'test'
// Output: [], since validation is debounced.
console.log(controller.f.email.errors)
// Force validators to run now instead of in a few hundred milliseconds.
controller.f.email.validate.flush().then(() => {
// Output: ['Emails must contain an @ in the middle.']
console.log(controller.f.email.errors)
})
The fielder automatically takes care of validating the value for you, and also manages the errors for that particular field, making it easy to create input controls that validate and display timely errors in your frontend component/template framework of choice.
- class Fielder()
interface
- Fielder.controller
type: FormController<T>
Returns the FormController.
- Fielder.debounce
type: <TODO: other type>
Returns the debounce rate set on the field.
- Fielder.disabled
type: boolean
Returns a flag which indicates if the field should be considered disabled.
- Fielder.errors
type: <TODO: other type>
Gets/sets the errors on a field.
- Fielder.fieldName
type: FieldName
Returns the name of the field
- Fielder.initialValue
type: <TODO: other type>
Gets/sets the initial Value for this field. This is what is used to populate the field when the form is reset.
- Fielder.model
type: <TODO: other type>
Gets/sets the raw value of the field, triggering validation checks.
- Fielder.moduleType
type: <TODO: other type>
Returns ‘fielder’, the type of ‘module’ this wrapper handles. Technically fielders aren’t modules, since their state is shared with singles, however this makes it consistent with similarly behaving controllers, which should have a moduleType property for easy identification.
- Fielder.rawValue
type: <TODO: other type>
Gets/sets the raw value of the field. Unlike model, this does not trigger validation checks.
- Fielder.step
type: <TODO: other type>
Gets/sets the form step this field is on.
- Fielder.validate
type: DebouncedFunc<<TODO: reflection>>
Debounced call to runValidators. Especially helpful to avoid annoying users that are still typing.
- Fielder.attr(attrName)
Grabs a specified attribute from a field’s settings, such as its ‘value’ or its ‘validators’ array.
- Arguments
attrName (
AttrName()
) –
- Returns
<TODO: other type> –
- Fielder.cancelValidation()
Force cancel current validation run. We use this when submitting a form so our validators don’t clobber whatever the server sends back.
- Fielder.reset()
Resets this field to its initial value and clears errors.
- Fielder.runValidators()
Runs the validators on the form. Usually you don’t want to call this directly– instead, try the debounced validate() function.