Foundation
Main View
Components
Charts
Dialogs
Framer Animations
Forms
Grids
Typography
Utils
Package: @uireact/ezforms
Create validated forms quickly and easily with a schema object
1. Make sure you install peer dependencies first:
2. Install package:
npm i -S @uireact/ezforms
The EzForm component builds a form automatically out of a schema
, the form will be automatically validated using the specifics on the schema. The schema is created using the @uireact/validator util:
The field()
fn exposes a function: ezMetadata()
which is a chainable function that can be used to pass metadata to each of the schema fields:
const validator = new UiValidator(); const schema = { name: validator.field('text').ezMetadata({ label: 'First Name', icon: 'User' }).present(), birthday: validator.field('date').ezMetadata({ label: 'When is your birthday?', icon: 'Party', dateFormat: 'yyyy/mm/dd' }).present(), age: validator.field('numeric').ezMetadata({ label: 'How old are you?', icon: 'Sun' }).present().greaterThan(18, "You have to be older than 18 years old"), email: validator.field('email').ezMetadata({ label: 'Enter your email', icon: 'Mail' }).present(), type: validator.field('choice').ezMetadata({ label: 'Account type' }).present("You have to select the type of account").oneOf(['user', 'admin', 'editor']), description: validator.field('text').ezMetadata({ label: 'Description', paragraph: true }).optional(), terms: validator.field('boolean').ezMetadata({ label: 'Accept terms and conditions' }).present("You have to accept our terms and conditions"), code: validator.field('numeric').ezMetadata({ label: 'Code sent to your phone', code: true }).present("The code should be present") };
From there you can continue to add any specific validation to each field.
Once the schema is complete we can pass it as a prop and this will be the outcome:
<EzFormExample />
The complete code for the EzFormExample
:
const validator = new UiValidator(); const schema = { name: validator .field('text') .ezMetadata({ label: 'First Name', icon: 'User' }) .present(), birthday: validator .field('date') .ezMetadata({ label: 'When is your birthday?', icon: 'Party', dateFormat: 'yyyy/mm/dd' }) .present(), age: validator .field('numeric') .ezMetadata({ label: 'How old are you?', icon: 'Sun' }) .present() .greaterThan(18, "You have to be older than 18 years old"), phone: validator .field('phone') .ezMetadata({ label: 'Your phone' }) .optional(), email: validator .field('email') .ezMetadata({ label: 'Your email', icon: 'Mail' }).present() .when('phone', validator.is().present()) .run(validator.is().optional()) .else(validator.is().present("The email is required if you don't provide your phone number.")), type: validator .field('choice') .ezMetadata({ label: 'Account type' }) .present("You have to select the type of account") .oneOf(['user', 'admin', 'editor']), description: validator .field('text') .ezMetadata({ label: 'Description', paragraph: true }) .optional(), terms: validator .field('boolean') .ezMetadata({ label: 'Accept terms and conditions' }) .present("You have to accept our terms and conditions"), code: validator .field('numeric') .ezMetadata({ label: 'Code sent to your phone', code: true }) .present("The code is required") .length(6, 6, "The code need to be 6 characters long") }; export const EzFormExample = () => { const onSubmit = useCallback((e: FormEvent<HTMLFormElement>, data: UiValidatorData) => { e.preventDefault(); console.log(data); }, []); const onCancel = useCallback(() => { console.log('Cancel triggered'); }, []); return ( <UiEzForm schema={schema} submitLabel='Save' cancelLabel='Cancel' onSubmit={onSubmit} onCancel={onCancel} initialData={{ birthday: '1992/12/03' }} /> ) };
The field()
function will set a initial type for that specific field in the schema, based on this we will render the input that is more suitable for it:
text -> <UiInput type="text" /> text with .ezMetadata({ paragraph: true }) -> <UiTextarea /> text with .ezMetadata({ protected: true }) -> <UiInput type="password" /> text with .ezMetadata({ hidden: true }) -> <input type="hidden" /> numeric -> <UiInput type="number" /> numeric with .ezMetadata({ code: true }).length(n, n) -> <UiDigitsInput /> // YOU HAVE TO PROVIDE THE LENGHT RULE email -> <UiInput type="email" /> phone -> <UiInput type="text" /> date -> <UiDatepicker /> boolean -> <UiSwitch /> choice -> <UiSelect />
In order to use a password field, we need to tell in the metadata that the field is protected, this ONLY works for text inputs.
<UiEzForm schema={{ username: new UiValidator().field("text").ezMetadata({ label: "Username:" }), password: new UiValidator().field("text").ezMetadata({ label: "Password:", protected: true }) }} buttonsAlignment='stacked' submitLabel='Log in' cancelLabel='Cancel' />
If you want to render a digits input, for instance for a numeric code then you need to tell the schema that the numeric field is a code
AND also you need to provide a length
rule because that will tell the inputs how many digits it has. If you don't provide one of those two properties then it WON'T render the digits input.
<UiEzForm schema={{ code: new UiValidator().field("numeric").ezMetadata({ label: "Your code:", code: true }).present("The code is required").length(6,6, "Code has to be 6 characters long"), }} submitLabel='Submit' />
In some forms we want to render hidden inputs to hold data that will be submitted alongside with the form, for this we can use the hidden
property:
<UiEzForm schema={{ newPassword: new UiValidator().field("text").ezMetadata({ label: "New password:", protected: true }).present("New password is needed"), token: new UiValidator().field("text").ezMetadata({ hidden: true }) }} initialData={{ token: 'some-super-secure-token', newPassword: '' }} submitLabel='Submit' loading onSubmit={(e, data) => {e.preventDefault(); console.log(data);}} />
In the previous example, there is only 1 input visible for the new password, although hidden, there is another input thinking that we might carry a token submitted alongside with the new password.
If you inspect the form you will see a hidden input with the value some-super-secure-token
as that is what we are setting from the initial data.
If you check the javascript console, once you submit the form you will see the data object carrying the "new password" and the token together.
IMPORTANT: Only text
fields are supported.
These are used to inject React elements around the form and customize what and how we need to render an input. The decorator name will tell the position where that React component will be rendered.
<UiEzForm schema={{ username: new UiValidator().field("text").ezMetadata({ label: "Username:" }), password: new UiValidator().field("text").ezMetadata({ label: "Password:", protected: true }) }} decorators={{ aboveActions: <UiText>Terms and conditions</UiText>, belowActions: <UiText align='center'>30 days free</UiText> }} buttonsAlignment='stacked' submitLabel='Sign up' />
The EzForm can be initialized with an initial data this is useful for use cases where you already have some data and the user need to modify or complete it.
The initialData
object MUST match a field in the schema, ONLY the properties in the object that matches a field in the schema will be used.
For instance, for this schema:
const schema = { name: validator.field('text').ezMetadata({ label: 'First Name', icon: 'User' }).present() };
The initial data should be something like:
const initialData = { name: 'Some user data' };
The EzForm will run validations when user clicks submits the form, all validations are run based on the schema passed.
Each of the validation functions can get their own error message, so if you haven't already you should check the @uireact/validator doc page to learn how to set up the schema.
The conditional validations are also supported, so if you use .when()
the validations in the form will be ran conditionally, to learn more about this go to Conditional validations;
The EzForm
exposes a onSubmit
callback that you can attach to it and will be triggered when the user submits the form and the validation passes. If you want to handle the submit with javascript then here you would do the preventDefault like this:
export const YourFormComponent = () => { const onSubmit = useCallback((e: FormEvent<HTMLFormElement>, data: UiValidatorData) => { e.preventDefault(); // Here you can prevent default to handle the submit with javascript console.log(data); }, []); return ( <UiEzForm schema={{}} submitLabel='Save' cancelLabel='Cancel' onSubmit={onSubmit} /> ) };
If you want to let the browser to continue with its normal submit then you don't need to pass any and the form will be triggered and submitted as usual. Important, make sure you pass the action and method prop so the submit goes as you plan to where you plan it to go.
By default the form buttons render inline:
<UiEzForm schema={{ name: new UiValidator().field("text").ezMetadata({ label: "Name:" }) }} submitLabel='Save' cancelLabel='Cancel' />
By default the form buttons render inline, although you can pass buttonsAlignment
prop as stacked
and the buttons are going to render stacked:
<UiEzForm schema={{ name: new UiValidator().field("text").ezMetadata({ label: "Name:" }) }} buttonsAlignment='stacked' submitLabel='Save' cancelLabel='Cancel' />
The EzForm component exposes a loading
prop that when passed it will render a loading spinner in the primary button. The primary button will also be disabled. You are responsible for setting its value as true / false to showcase loading behavior.
<UiEzForm schema={{ newPassword: new UiValidator().field("text").ezMetadata({ label: "New password:", protected: true }).present("New password is needed"), token: new UiValidator().field("text").ezMetadata({ hidden: true }) }} initialData={{ token: 'some-super-secure-token', newPassword: '' }} submitLabel='Submit' loading onSubmit={(e, data) => {e.preventDefault(); console.log(data);}} />