Foundation

Main View


Components

Charts

Dialogs

Framer Animations

Forms

Grids

Typography


Utils


EzForm


Package: @uireact/ezforms

v0.6.1
‼️ Beta

Create validated forms quickly and easily with a schema object

1. Make sure you install peer dependencies first:

Expand peer dependencies

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 />

Passwords

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' 
  />

Digits input

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' 
  />

Hidden inputs

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;

Client side

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} 
    />
  )
};

Browser apis

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.

Inline (Default)

By default the form buttons render inline:

  <UiEzForm
    schema={{
      name: new UiValidator().field("text").ezMetadata({ label: "Name:" })
    }}
    submitLabel='Save'
    cancelLabel='Cancel'
  />

Stacked

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);}}
  />