JustValidate

repository·master·Indexed 20 days ago

https://github.com/horprogs/just-validate

A modern, lightweight (~5kb gzip), zero-dependency form validation library written in TypeScript. It supports predefined rules for text, numbers, passwords, files, and dates, as well as async validation, custom rules, plugins, and localization. It provides extensive configuration for error/success styling, custom CSS classes, tooltips, and automatic form submission.

Tokens
23.5K
Snippets
104
Records
108
Agent score
65%

What's inside just-validate

  1. Configure a Rule object in addField

    master

    When using the addField method to validate a field, you pass a Rule object (or an array of Rule objects) to define the validation logic. A Rule object specifies which rule to apply, the value to compare against, the error message to display, and optionally a custom validator function.

    Key properties of a Rule object:

    • rule: A string representing the predefined rule name (e.g., 'required', 'minLength').
    • value: The threshold or pattern to validate against. Depending on the rule, this can be a number, string, RegExp, or a file configuration object { files: object }.
    • errorMessage: The message shown when validation fails. This can be a string or a function with the signature (value, context) => string.
    • validator: A custom validation function used to implement logic not covered by predefined rules.
    // Example of an array of rule objects passed to addField
    [
      {
        rule: 'minLength',
        value: 3,
      },
      {
        rule: 'maxLength',
        value: 15,
      },
    ];
  2. Validate date ranges with isBefore and isAfter

    master

    You can validate that a date falls within a specific range using isBefore and isAfter.

    Important: When using text inputs, you must define the same format used in your comparison strings so the library can parse them correctly. If you are using an HTML <input type="date">, you do not need to define a format because the browser always uses the yyyy-mm-dd format.

    // Example for text input with specific format
    import JustValidatePluginDate from 'just-validate-plugin-date';
    
    validation.addField('#date', [
      {
        plugin: JustValidatePluginDate(() => ({
          format: 'dd/MM/yyyy',
          isBefore: '15/12/2021',
          isAfter: '10/12/2021',
        })),
        errorMessage: 'Date should be between 10/12/2021 and 15/12/2021',
      },
    ]);
    
    // Example for HTML date input (no format needed)
    validation.addField('#date-between', [
      {
        plugin: JustValidatePluginDate((fields) => ({
          isAfter: document.querySelector('#date-start').value,
          isBefore: document.querySelector('#date-end').value,
        })),
        errorMessage: 'Date should be between start and end dates',
      },
    ]);
  3. Perform cross-field validation with addField()

    master

    You can implement validation logic that depends on the values of other fields by using a custom validator function within the rules array. The validator function receives two arguments:

    1. value: The current value of the field being validated.
    2. fields: An object containing the state of all registered fields. You can access other fields using their selector as a key (e.g., fields['#password']).

    Each field object in the fields map contains an elem property, which is the actual DOM element.

    Note: The validator function must return a boolean or a Promise that resolves to a boolean.

    validation.addField('#repeat-password', [
      {
        validator: (value, fields) => {
          if (fields['#password'] && fields['#password'].elem) {
            const repeatPasswordValue = fields['#password'].elem.value;
            return value === repeatPasswordValue;
          }
          return true;
        },
        errorMessage: 'Passwords should be the same',
      },
    ]);
  4. Advanced validation: Custom validators and required groups

    master

    JustValidate supports advanced scenarios such as comparing two fields, using custom logic, and validating groups of inputs.

    Custom Validators

    Instead of a predefined rule, you can provide a validator function. This function receives the current value and an object containing all fields. It should return true if valid or false if invalid. You can also specify a custom errorMessage.

    Error Containers

    You can specify a custom element to hold errors for a specific field using the errorsContainer option in .addField().

    Required Groups

    To ensure at least one element in a group (like checkboxes or radio buttons) is selected, use .addRequiredGroup(selector, errorMessage) or .addRequiredGroup(selector). The selector should target the container of the group.

    const validator = new JustValidate('#advanced-usage_form');
    
    validator
      // 1. Custom validator to compare fields (e.g., password confirmation)
      .addField('#advanced-usage_repeat-password', [
        {
          rule: 'required',
        },
        {
          validator: (value, fields) => {
            if (fields['#advanced-usage_password'] && fields['#advanced-usage_password'].elem) {
              const repeatPasswordValue = fields['#advanced-usage_password'].elem.value;
              return value === repeatPasswordValue;
            }
            return true;
          },
          errorMessage: 'Passwords should be the same',
        },
      ])
      // 2. Custom validator for logic (e.g., minimum length)
      .addField('#advanced-usage_message', [
        {
          validator: (value) => {
            return value !== undefined && (value as string).length > 3;
          },
          errorMessage: 'Message should be more than 3 letters.',
        },
      ])
      // 3. Custom error container
      .addField('#advanced-usage_consent_checkbox', [
        { rule: 'required' },
      ], {
        errorsContainer: '#advanced-usage_consent_checkbox-errors-container',
      })
      // 4. Required group for checkboxes
      .addRequiredGroup(
        '#advanced-usage_communication_checkbox_group',
        'You should select at least one communication channel'
      )
      // 5. Required group for radio buttons
      .addRequiredGroup('#advanced-usage_communication_radio_group')
      // 6. Integer validation
      .addField('#advanced-usage_input_integer_number', [
        { rule: 'required' },
        { rule: 'integer' },
      ]);
  5. Validate form before submitting

    master

    By default, validation might occur on submit. To ensure validation is performed and prevents the default form submission behavior until the form is valid, set the validateBeforeSubmitting option to true in the JustValidate constructor.

    const validator = new JustValidate('#before-submit_form', {
      validateBeforeSubmitting: true,
    });
    
    validator.addField('#before-submit_email', [
      {
        rule: 'required',
      },
      {
        rule: 'email',
      },
    ]);
  6. Implement advanced form validation with JustValidate

    master

    For complex forms, you can use JustValidate to chain multiple validation rules, handle field dependencies, manage error containers, and validate groups of inputs (like checkboxes or radio buttons).

    Key Capabilities:

    • Field Dependencies: Use a custom validator function to compare a field's value against another field in the form.
    • Custom Error Containers: Specify a specific DOM element to display errors for a field using the errorsContainer option in addField.
    • Required Groups: Use addRequiredGroup to ensure at least one element within a group (identified by a selector) is selected. This is useful for checkbox groups or radio button groups.
    • Built-in Rules: Apply rules like required, number, integer, minNumber, and maxNumber directly to fields.

    Example Implementation

    const validator = new JustValidate('#form-id');
    
    validator
      // Basic required field
      .addField('#password', [
        { rule: 'required' },
      ])
      // Field with dependency (matching password)
      .addField('#repeat-password', [
        { rule: 'required' },
        {
          validator: (value, fields) => {
            if (fields['#password'] && fields['#password'].elem) {
              const repeatPasswordValue = fields['#password'].elem.value;
              return value === repeatPasswordValue;
            }
            return true;
          },
          errorMessage: 'Passwords should be the same',
        },
      ])
      // Field with custom logic and specific error container
      .addField('#consent-checkbox', [
        { rule: 'required' },
      ], {
        errorsContainer: '#consent-errors-container',
      })
      // Validating a checkbox group (at least one must be checked)
      .addRequiredGroup(
        '#communication-checkbox-group',
        'You should select at least one communication channel'
      )
      // Validating a radio group
      .addRequiredGroup('#communication-radio-group')
      // Numeric constraints
      .addField('#age', [
        { rule: 'required' },
        { rule: 'number' },
        { rule: 'minNumber', value: 10 },
        { rule: 'maxNumber', value: 20 },
      ]);
    const validator = new JustValidate('#advanced-usage_form');
    
    validator
      .addField('#advanced-usage_password', [
        {
          rule: 'required',
        },
      ])
      .addField('#advanced-usage_repeat-password', [
        {
          rule: 'required',
        },
        {
          validator: (value, fields) => {
            if (
              fields['#advanced-usage_password'] &&
              fields['#advanced-usage_password'].elem
            ) {
              const repeatPasswordValue =
                fields['#advanced-usage_password'].elem.value;
    
              return value === repeatPasswordValue;
            }
    
            return true;
          },
          errorMessage: 'Passwords should be the same',
        },
      ])
      .addField('#advanced-usage_message', [
        {
          validator: (value) => {
            return value !== undefined && (value as string).length > 3;
          },
          errorMessage: 'Message should be more than 3 letters.',
        },
      ])
      .addField(
        '#advanced-usage_consent_checkbox',
        [
          {
            rule: 'required',
          },
        ],
        {
          errorsContainer: '#advanced-usage_consent_checkbox-errors-container',
        }
      )
      .addField('#advanced-usage_favorite_animal_select', [
        {
          rule: 'required',
        },
      ])
      .addRequiredGroup(
        '#advanced-usage_communication_checkbox_group',
        'You should select at least one communication channel'
      )
      .addRequiredGroup('#advanced-usage_communication_radio_group')
      .addField('#advanced-usage_input_number', [
        {
          rule: 'required',
        },
        {
          rule: 'number',
        },
      ])
      .addField('#advanced-usage_input_integer_number', [
        {
          rule: 'required',
        },
        {
          rule: 'integer',
        },
      ])
      .addField('#advanced-usage_input_number_between', [
        {
          rule: 'required',
        },
        {
          rule: 'minNumber',
          value: 10,
        },
        {
          rule: 'maxNumber',
          value: 20,
        },
      ]);
  7. Submit form automatically

    master

    To automatically trigger the form submission once all fields have passed validation, set the submitFormAutomatically option to true in the JustValidate constructor. This is useful for seamless user experiences where a manual submit button might not be required.

    const validator = new JustValidate('#submit-automatically_form_form', {
      submitFormAutomatically: true,
    });
    
    validator.addField('#submit-automatically_form_email', [
      {
        rule: 'required',
      },
      {
        rule: 'email',
      },
    ]);
  8. Configure tooltips and error containers

    master

    JustValidate allows you to customize the appearance and placement of error tooltips. You can set a global tooltip position when initializing JustValidate, or override it for specific fields. Additionally, you can specify a custom errorsContainer for a field to control where error messages are rendered.

    const validator = new JustValidate('#tooltips_form', {
      tooltip: {
        position: 'top',
      },
    });
    
    validator
      .addField('#tooltips_name', [
        {
          rule: 'required',
        },
      ])
      .addField(
        '#tooltips_consent_checkbox',
        [
          {
            rule: 'required',
          },
        ],
        {
          errorsContainer: '#tooltips_consent_checkbox-errors-container',
        }
      )
      .addField(
        '#tooltips_favorite_animal_select',
        [
          {
            rule: 'required',
          },
        ],
        {
          tooltip: {
            position: 'right',
          },
        }
      )
      .addRequiredGroup(
        '#tooltips_communication_checkbox_group',
        'You should select at least one communication channel',
        {
          tooltip: {
            position: 'bottom',
          },
        }
      );
  9. Install and use JustValidatePluginDate

    master

    Date validation is not part of the core just-validate library to keep the bundle size small. It is provided via a separate plugin called JustValidatePluginDate. This plugin uses date-fns internally to handle date operations and supports validation for specific formats, as well as relative comparisons like isBefore and isAfter. It works with both standard text inputs and HTML date inputs.

    import JustValidatePluginDate from 'just-validate-plugin-date';
  10. Deploy the just-validate website

    master

    The website can be deployed using different methods depending on your hosting preference:

    Using SSH: Set the USE_SSH environment variable to true before running the deploy command.

    Using GitHub Pages: If you want to deploy to GitHub Pages, provide your GitHub username via the GIT_USER environment variable. This command builds the website and pushes the content to the gh-pages branch.

    # Deploy via SSH
    $ USE_SSH=true yarn deploy
    
    # Deploy to GitHub Pages
    $ GIT_USER=<Your GitHub username> yarn deploy