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:

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 field

  • settingName (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 field

  • settingName (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.

FormController.submit()

Submits the form, returning a promise from the submission. It is recommended to catch any errors from this submission with the x function.

Returns

Promise<K>

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.