AForm
Build statically-typed forms using React and TypeScript based on form schemas.
How to install
yarn install a-form
How to use
For creating a form with a-form
follow steps described below.
Define form schema
import { Input, InputArray, InputGroup } from 'a-form';
const aSchema = new InputGroup({
name: new Input<string>(), // <- string input
age: new Input<number>(), // <- number input
address: new InputGroup({ // <- nested inputs
city: new Input<string>(),
zip: new Input<number>()
}),
phoneNumbers: new InputArray(new Input<string>()), // <- array of string inputs
ids: new InputArray(new Input<number>()), // <- array of number inputs
guests: new InputArray( // <- array of nested inputs
new InputGroup({
name: new Input<string>(),
age: new Input<number>()
})
)
});
You can also use helper functions for denining form schemas:
import { input, inputArray, inputGroup } from 'a-form';
const aSchema = inputGroup({
name: input<string>(), // <- string input
age: input<number>(), // <- number input
address: inputGroup({ // <- nested inputs
city: input<string>(),
zip: input<number>()
}),
phoneNumbers: inputArray(input<string>()), // <- array of string inputs
ids: inputArray(input<number>()), // <- array of number inputs
guests: inputArray( // <- array of nested inputs
inputGroup({
name: input<string>(),
age: input<number>()
})
)
});
Using useForm React hook
import { useForm } from 'a-form';
const AForm: React.SFC<{}> = (props) => {
const validate = (values: typeof initialValues) => ({}); // form always valid
const submit = (values: typeof initialValues) => console.log(values);
const form = useForm({ aSchema, initialValues, validate, submit });
// use form to access properties for rendering form
return (
<form onSubmit={form.handleSubmit}>
{ // ... }
</form>
)
};
Using Form component to render your form with render props
const AForm: React.SFC<{}> = (props) => {
const validate = (values: typeof initialValues) => ({}); // form always valid
const submit = (values: typeof initialValues) => console.log(values);
return (
<Form schema={aSchema} validate={validate} submit={submit} initialValues={}>
{
(formProps) =>
(
<form onSubmit={formProps.handleSubmit}>
<div>
<label htmlFor="input-name">Name</label>
<input id="input-name" {...formProps.name} />
{
formProps.name.touched && formProps.name.invalid &&
<span className="error">{formProps.name.error}</span>
}
</div>
<div>
<label htmlFor="input-age">Age</label>
<input id="input-age" {...formProps.age} />
{
formProps.age.touched && formProps.age.invalid &&
<span className="error">{formProps.age.error}</span>
}
</div>
<div>
<label htmlFor="input-address-city">City</label>
<input id="input-address-city" {...formProps.address.city} />
</div>
<div>
<label htmlFor="input-address-zip">Postal code</label>
<input id="input-address-zip" {...formProps.address.zip} />
</div>
{
formProps.phoneNumbers.items.map((phoneNumberProps, index) => {
<div>
<label htmlFor={`input-phone-number-${index}`}>Phone {index}</label>
<input id={`input-phone-number-${index}`} {...phoneNumberProps} />
<button type="button" onClick={() => formProps.phoneNumbers.remove(index)}>×</button>
</div>
})
}
<button type="button" onClick={() => formProps.phoneNumbers.push('')}>Add phone number</button>
<button type="submit">Submit form</button>
</form>
)
}
</Form>
);
}
API
Form props / useForm hook config
-
schema: InputGroup
- form schema, which defines available form inputs, for which later field-level props will be available -
validate(values)
- validation function which returns an object with validation errors for each field. -
submit(values)
- function which handles data submission -
initialValues
- values of the form, with which the form will be initialized
Form state
-
values: Values<T>
- form values -
errors: Errors<T>
- per-input validation errors -
active: Flags<T>
- per-input active flags, indicating whether input is focused or not -
touched: Flags<T>
- per-input touched flags, indicating whether input is touched or not -
invalid: boolean
- indicates that form has validation errors -
validating: boolean
- indicates that form is currently running validations -
submitting: boolean
- indicates that form is currently submitting -
submitted: boolean
- indicates that the form was submitted -
submitResult: boolean | undefined
- indicates whether the form submission was successfull or failed -
error: string | undefined
- global form-level error -
dirty: boolean
- global dirty flag, indicates that current form values are not deeply equal to initialValues
Form handlers
-
handleSubmit(event: React.FormEvent): void;
- form submit handler, which internally marks all fields as touched, validates and submits form values -
reset(): void;
- reset form values to form's initialValues -
setValues(values: Values<T>): void;
- set form values -
setErrors(errors: Errors<T>): void;
- set form errors
Input props
-
value: T
- field value -
name: string
- field name -
error: string | undefined
- validation error -
active: boolean
- active flag (indicates that field is currently focused) -
touched: boolean
- touched flag (indicates that field is touched) -
dirty: boolean
- field value was changed and is different from value, specified ininitialValues
prop of the form component -
invalid: boolean
- field has validation errors -
onBlur(event: React.FocusEvent<HTMLElement>): void
- blur event handler -
onChange(eventOrValue: React.ChangeEvent<HTMLInputElement> | T): void
- change event handler -
onFocus(event: React.FocusEvent<HTMLElement>): void
- focus event handler
InputArray props
-
items
: array of field props for each value in the list -
touched
: boolean -
pop(): void
- remove last value from the list -
push(value: T): void
- add value specified as agrument to the end of the list -
shift(): void
- remove first value from the list -
unshift(value: T): void
- add value specified as agrument to the beginning of the list -
insert(index: number, value: T): void
- insert value specified as agrument at a given index into the list -
replace(index: number, value: T): void
- replace value by existing index with a new value -
remove(index: number): void
- remove value from the list at index specified as an argument -
move(from: number, to: number): void
- move value from one index to another -
swap(indexA: number, indexB: number): void
- swap positions of two values in the list, specified by arguments
InputGroup props
-
touched
: boolean -
change(values: T)
- change form group values
For each of the input defined in particular input group, it's props are available by the name of the input.
Example:
const groupSchema = new InputGroup({
name: new Input<string>(),
tags: new InputArray(Input<string>)
});
// ...
// usage later in form component
formProps.name // <= Input props for name input
formProps.tags // <= InputArray props for tags input
Licence
GPLv3
Copyright hck, 2019