Foundation

Main View


Components

Charts

Dialogs

Framer Animations

Forms

Grids

Typography


Utils


EzForm


Package: @uireact/ezforms

v0.11.0
‼️ 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. You need to create a schema and pass it to the < EzForm /> component:

  <EzFormExample />

Example code:

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(),
  money: validator
    .field('numeric')
    .present("Please select a value")
    .ezMetadata({ label: 'How much would you like to invest?', prefix: '$' })
    .range(0, 100),
  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');
  }, []);
  const onChange = useCallback((data: UiValidatorData) => {
    console.log('Change triggered');
    console.log(data);
  }, []);

  return (
    <UiEzForm 
      schema={schema} 
      submitLabel='Save' 
      cancelLabel='Cancel' 
      onSubmit={onSubmit} 
      onCancel={onCancel} 
      initialData={{ birthday: '1992/12/03' }} 
    />
  )
};

This is a function exposed as part of the validator class that help you to set up properties used in the EzForms

Options:

/** Metadata information used from EzForms */
export type UiValidatorFieldMetadata = {
  /** The input label to use */
  label?: string;
  /** The icon name to use in the text input */
  icon?: string;
  /** Tells UiDatepickerInput the date format to use */
  dateFormat?: 'yyyy/mm/dd' | 'yyyy-mm-dd' | 'mm/dd/yyyy' | 'dd/mm/yyyy';
  /** Tells EzForms to display a textarea rather than a text input */
  paragraph?: boolean;
  /** Tells EzForms to display field as password */
  protected?: boolean;
  /** Tells EzForms to display as a UiDigitsInputs */
  code?: boolean;
  /** Tells EzForms to display input as hidden */
  hidden?: boolean;
  /** Tells EzForms to display a prefix on a range input */
  prefix?: string;
}

Example of usage:

const 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 })
}

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 }).length(6, 12).secure('strong')
    }}
    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.

By default the form renders with all its elements stacked, but if you need an inline form then you can pass a prop to make it render inline.

  <UiEzForm
    schema={{
      firstName: new UiValidator().field("text").ezMetadata({ label: "First name:" }),
      lastName: new UiValidator().field("text").ezMetadata({ label: "Last name:" }),
    }}
    direction='inline'
    submitLabel='Save'
    cancelLabel='Cancel'
  />

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