npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

shadow-form-handler

v2.0.1

Published

v2.0.1 is a powerful and flexible form-handling and validation library for JavaScript and TypeScript. It simplifies the process of creating and managing complex forms by providing a rich set of features, an intuitive API, and an extensible architecture. W

Downloads

3

Readme

shadow-form-handler

Overview

shadow-form-handler is a powerful and flexible form-handling and validation library for JavaScript and TypeScript. It simplifies the process of creating and managing complex forms by providing a rich set of features, an intuitive API, and an extensible architecture. Whether you’re building simple forms or complex multi-step workflows, shadow-form-handler has you covered with robust validation, dynamic field management, and seamless integration capabilities.

🚀 Key Features

  1. Dynamic Field Management:

    • Easily add, remove, and modify form fields at runtime.
    • Specify the position where new fields should be inserted (top, bottom, or a specific index).
    • Disable buttons when adding or removing fields for a better user experience.
  2. Flexible Validation:

    • Leverage both schema-based and custom validation rules.
    • Utilize built-in validation rules like required, minLength, maxLength, pattern, email, range, and matches.
    • Create custom validation rules with asynchronous support.
    • Set up validations that depend on the values of other fields.
  3. Error Handling:

    • Display error messages for invalid fields with customizable styling.
    • Provide clear and specific error messages to enhance the user experience.
    • Automatically display errors when validation fails.
  4. Event Management:

    • Attach custom event listeners to form fields (e.g., input, change, blur).
    • Integrate with form events like onFormSubmitSuccess, onFormSubmitFail, and onFieldValidationError.
  5. Hooks System:

    • Extend the functionality of the form handler by adding custom hooks.
    • Trigger hooks at various stages of the form lifecycle, such as field registration, validation, and submission.
    • Use hooks to implement custom logic, logging, or side effects.
  6. Validation Modes:

    • Choose between default (on form submit) and runtime (real-time) validation modes.
    • Automatically add runtime validation to new fields when they are registered.
  7. Asynchronous Validation:

    • Perform asynchronous validations with ease, such as checking for uniqueness on the server.
    • Implement timeout handling to ensure a smooth user experience.
  8. Dependent Field Validation:

    • Set up validations that depend on the values of other form fields.
    • Automatically re-validate dependent fields when their dependencies change.
  9. Internationalization and Localization:

    • Provide translations for validation error messages.
    • Support multiple languages to reach a global audience.
    • Easily add new translations or override existing ones.

🎯 Benefits

  1. Reduced Development Time: Streamline form creation and validation with minimal boilerplate code.
  2. Enhanced User Experience: Provide instant feedback with real-time validation and clear error messages.
  3. Flexibility: Easily adapt to changing requirements with dynamic field management and extensible architecture.
  4. Powerful Validation: Combine schema-based and custom validators to handle complex validation scenarios.
  5. Asynchronous Support: Seamlessly integrate server-side validations without blocking the UI.
  6. Event-Driven Architecture: Extend functionality with hooks and custom event listeners.
  7. Customizable UI: Tailor error messages and styles to match your application's design.
  8. Internationalization: Support multiple languages for a global reach.

📦 Installation

Using npm

Install the package via npm:

npm install shadow-form-handler

Using HTML Script Tag

To use ShadowFormHandler directly in your HTML file, add the following script tag:

<script src="./node_modules/shadow-form-handler/dist/shadow-form-handler.js"></script>

Using CDN

Alternatively, you can use ShadowFormHandler via a CDN by adding this script tag:

<script src="https://cdn.jsdelivr.net/gh/Mostafashadow1/[email protected]/dist/shadow-form-handler.js"></script>

Using GitHub Releases

You can also download the latest release directly from GitHub. Follow these steps:

  1. Navigate to the Releases Page: Go to the Releases section of your GitHub repository.

  2. Download the Latest Release: Find the latest release and download the shadow-form-handler.js file from the assets section.

  3. Include the Script in Your HTML: Add the downloaded file to your project and include it in your HTML file like this:

<script src="path/to/shadow-form-handler.js"></script>

Simple Form Handling

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Simple Example with shadow-form-handler</title>
  <link rel="stylesheet" href="./styles.css">
  </head>
<body>
  <form id="myForm">
    <div>
      <label for="username">Username:</label>
      <input type="text" id="username" >
      <span id="username-error" class="error-message"></span>
    </div>
    <button type="submit">Submit</button>
  </form>

   <script src="https://cdn.jsdelivr.net/gh/Mostafashadow1/[email protected]/dist/shadow-form-handler.js"></script>
  <script src="./index.js"></script>

</body>
</html>
const { FormHandler } = shadowFormHandler;
const formHandler = new FormHandler();

// Register a field
formHandler.register({
  id: 'username',
  initialValue: "",
  schemaValidation: [
    formHandler.validation.required(), // if no message ? show default message
    formHandler.validation.minLength(3, 'Username must be at least 3 characters long'),
    formHandler.validation.maxLength(15, 'Username must be less than 15 characters'),
  ]
});

// Set up form submission
formHandler.submitHandler('myForm', (values) => {
  console.log('Form submitted with values:', values);
});

Core Components

Methods

Form Field Methods

  • register({id: string, initialValue?: string, schemaValidation?: [], customValidation?:[] }):

    • Purpose: Register a new form field.
    • Usage:
      formHandler.register({ id: 'username', initialValue: '', schemaValidation: [], customValidation: [] });
  • setMode(newMode: 'default' || 'runtime'):

    • Purpose: Set the validation mode (e.g., Default, Runtime).
    • Usage:
      formHandler.setMode('runtime');
  • submitHandler(formId: string, onSubmit?: Function):

    • Purpose: Handle form submission.
    • Usage:
      formHandler.submitHandler('myForm', (data) => console.log(data));
  • getValue(id: string):

    • Purpose: Get the value of a specific field.
    • Usage:
      const username = formHandler.getValue('username');
  • getValues():

    • Purpose: Get values of all fields.
    • Usage:
      const allValues = formHandler.getValues();
  • resetForm():

    • Purpose: Reset all form fields and errors.
    • Usage:
      formHandler.resetForm();
  • removeField({ fieldId: string, disabledId?: string }):

    • Purpose: Remove a field dynamically.
    • Usage:
      formHandler.removeField({ fieldId: 'descraption'});
  • addField({containerId: string, fieldId: string, labelText: string, register?: RegisterParams, position?: "top" | "bottom" | number, disabledId?: string }):

    • Purpose: Add a new field dynamically.
    • Usage:
      formHandler.addField({ containerId: 'formContainer', fieldId: 'password', labelText: 'Password', register: { id: 'password', validation: 'required|min:8' }, position: 'bottom' });
  • setErrorStyles(style: ErrorStyle):

    • Purpose: Set custom error styles.
    • Usage:
      formHandler.setErrorStyles({ color: 'red', fontSize: '12px' });

Sure! Here's the updated section for your README file:

Methods

addHooks(hooks: Hooks)

Purpose: Add custom hooks for form lifecycle events. Usage:

formHandler.addHooks({
  onFormSubmitSuccess: (values) => {
    console.log("Form submitted successfully with values:", values);
  },
  onFormSubmitFail: (errors) => {
    console.error("Form submission failed with errors:", errors);
  },
  onFieldValidationError: (fieldId, error) => {
    if (error) {
      console.warn(`Validation error on field ${fieldId}: ${error}`);
    }
  },
  onFormReset: () => {
    console.log("Form reset successfully");
  },
  beforeFieldRegister: (fieldId, params) => {
    console.log(`Registering field: ${fieldId}`, params);
  },
  afterFieldRegister: (fieldId, params) => {
    console.log(`Field registered: ${fieldId}`, params);
  },
  onValidationStart: (fields) => {
    console.log("Validation started", fields);
  },
  onValidationEnd: (fields) => {
    console.log("Validation ended", fields);
  },
  onValueChange: (fieldId, value) => {
    console.log(fieldId, value);
  },
  onFieldAdd: (fieldId) => {
    console.log(fieldId);
  },
  onFieldRemove: (fieldId) => {
    console.log(fieldId);
  },
  onFocus: (field) => {
    console.log(field);
  },
  onBlur: (field) => {
    console.log(field);
  },
});
  • addCustomEventListener(id: string, event: string, listener: EventListener):
    • Purpose: Add a custom event listener to a field.
    • Usage:
      formHandler.addCustomEventListener('username', 'focus', () => console.log('Username field focused'));

Language Methods

  • setCurrentLanguage(langCode: string):

    • Purpose: Set the current language.
    • Usage:
      formHandler.lang.setCurrentLanguage('ar');
  • getCurrentLanguage():

    • Purpose: Get the current language.
    • Usage:
      const currentLang = formHandler.lang.getCurrentLanguage();
  • addTranslations(lang: string, translations: { [key: string]: string; }):

    • Purpose: Add translations for a specific language.
    • Usage:
      formHandler.lang.addTranslations('en', {
         'password': 'Password must contain at least one uppercase letter, one lowercase letter, and one number.',
        'confirmPassword' : "Passwords do not match"
      });
      
      formHandler.lang.addTranslations('ar', {
       'password': 'يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل، وحرف صغير واحد، ورقم واحد.',
       'confirmPassword' : "كلمات المرور غير متطابقة."
      });
      
      formHandler.lang.addTranslations('es', {
        'password': 'Le mot de passe doit contenir au moins une lettre majuscule, une lettre minuscule et un chiffre.',
        'confirmPassword' : "Les mots de passe ne correspondent pas."
      });
const currentLang = formHandler.lang.getCurrentLanguage();

Built-in Validation Rules

The package provides the following validation rules:

  1. required(message?: string):

    • Ensures the field is not empty.
    • If no message is provided, it will use the translation for the 'required' key, or the default message "This field is required.".
  2. minLength(min: number, message?: string):

    • Checks if the value has at least the minimum length.
    • If no message is provided, it will use the translation for the 'minLength' key, or the default message "This field must be at least {min} characters long.".
  3. maxLength(max: number, message?: string):

    • Checks if the value doesn't exceed the maximum length.
    • If no message is provided, it will use the translation for the 'maxLength' key, or the default message "This field must be no more than {max} characters long.".
  4. pattern(regex: RegExp, message?: string):

    • Validates the value against a regular expression pattern.
    • If no message is provided, it will use the translation for the 'pattern' key, or the default message "This field does not match the required pattern.".
  5. email(message?: string):

    • Checks if the value is a valid email format.
    • If no message is provided, it will use the translation for the 'email' key, or the default message "This field must be a valid email address.".
  6. range(min: number, max: number, message?: string):

    • Ensures the value is within a specified range.
    • If no message is provided, it will use the translation for the 'range' key, or the default message "The value must be between {min} and {max}.".
  7. matches(fieldToMatch: string, message?: string):

    • Ensures the value matches another field's value.
    • If no message is provided, it will use the translation for the 'matches' key, or the default message "This field must match the {fieldToMatch} field.".

The validation rules use the lang.getTranslation() function to retrieve the appropriate error message. If the translation key is not found, the default message will be used.

Dynamic Field Management

// Add a new field
formHandler.addField({
  containerId: 'formContainer',
  fieldId: 'newField',
  labelText: 'New Field',
  register: {
    initialValue: '',
    schemaValidation: [required('This field is required')]
  },
  position: 'top' // position of element added: 'top' | 'bottom' | number of element filed like 3
});

// Remove a field
formHandler.removeField({
  fieldId: 'fieldToRemove',
  disabledId: 'addButton'
});

Asynchronous Validation

formHandler.register({
  id: 'username',
  customValidation: [{
    validate: async (value) => {
      // Simulating an API call to check username uniqueness
      await new Promise(resolve => setTimeout(resolve, 1000));
      return !value.includes('shadow' && 'mostafa mohamed' && 'mostafa');
    },
    message: 'This username is already taken.',
  }]
});

Internationalization and Localization


// Set the current language
formHandler.lang.setCurrentLanguage('ar');

// Add translations for different languages
formHandler.lang.addTranslations('en', {
  'required': 'This field is required.',
  'minLength': 'This field must be at least {min} characters long.',
  'maxLength': 'This field must not exceed {max} characters.',
  'email': 'Please enter a valid email address.',
  'range': 'Value must be between {min} and {max}.',
  'matches': 'This field must match {fieldToMatch}.',
});

formHandler.lang.addTranslations('ar', {
  'required': 'هذا الحقل مطلوب.',
  'minLength': 'يجب أن يحتوي هذا الحقل على {min} أحرف على الأقل.',
  'maxLength': 'يجب ألا يتجاوز هذا الحقل {max} حرفًا.',
  'email': 'يرجى إدخال عنوان بريد إلكتروني صالح.',
  'range': 'يجب أن تكون القيمة بين {min} و {max}.',
  'matches': 'يجب أن يتطابق هذا الحقل مع {fieldToMatch}.',
});

Dependent Field Validation With built in 'matches' method


formHandler.lang.addTranslations('en', {
  'password': 'Password must contain at least one uppercase letter, one lowercase letter, and one number.',
  'confirmPassword' : "Passwords do not match"

});

formHandler.lang.addTranslations('ar', {
  'password': 'يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل، وحرف صغير واحد، ورقم واحد.',
  'confirmPassword' : "كلمات المرور غير متطابقة."

});

formHandler.lang.addTranslations('es', {
  'password': 'Le mot de passe doit contenir au moins une lettre majuscule, une lettre minuscule et un chiffre.',
  'confirmPassword' : "Les mots de passe ne correspondent pas."
});

formHandler.register({
  id: 'password',
  schemaValidation: [
    formHandler.validation.required(),
    formHandler.validation.minLength(8),
    formHandler.validation.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, formHandler.lang.getTranslation('password'))
  ]
});

formHandler.register({
  id: 'confirm-password',
  schemaValidation: [
    formHandler.validation.required(),
    formHandler.validation.matches('password' , formHandler.lang.getTranslation('confirmPassword'))
  ]
});

in this approach we prefer uses dependencies

formHandler.register({
  id: 'confirm-password',
  dependencies: ['password'], 
  customValidation: [{
    validate: (value, formValues) => value === formValues.password,
    message: formHandler.lang.getTranslation('confirmPassword')
  }]
});

Hooks System

  • onFormSubmitSuccess(values): Triggered when the form is successfully submitted.

    • Parameters:
      • values: The values submitted in the form.
    • Example:
      onFormSubmitSuccess: (values) => {
        console.log("Form submitted successfully with values:", values);
      }
  • onFormSubmitFail(errors): Triggered when the form submission fails.

    • Parameters:
      • errors: The errors encountered during submission.
    • Example:
      onFormSubmitFail: (errors) => {
        console.error("Form submission failed with errors:", errors);
      }
  • onFieldValidationError(fieldId, error): Triggered when a field validation error occurs.

    • Parameters:
      • fieldId: The ID of the field with the error.
      • error: The validation error message.
    • Example:
      onFieldValidationError: (fieldId, error) => {
        if (error) {
          console.warn(`Validation error on field ${fieldId}: ${error}`);
        }
      }
  • onFormReset(): Triggered when the form is reset.

    • Example:
      onFormReset: () => {
        console.log("Form reset successfully");
      }
  • beforeFieldRegister(fieldId, params): Triggered before a field is registered.

    • Parameters:
      • fieldId: The ID of the field being registered.
      • params: The parameters for the field registration.
    • Example:
      beforeFieldRegister: (fieldId, params) => {
        console.log(`Registering field: ${fieldId}`, params);
      }
  • afterFieldRegister(fieldId, params): Triggered after a field is registered.

    • Parameters:
      • fieldId: The ID of the field being registered.
      • params: The parameters for the field registration.
    • Example:
      afterFieldRegister: (fieldId, params) => {
        console.log(`Field registered: ${fieldId}`, params);
      }
  • onValidationStart(fields): Triggered when validation starts.

    • Parameters:
      • fields: The fields being validated.
    • Example:
      onValidationStart: (fields) => {
        console.log("Validation started", fields);
      }
  • onValidationEnd(fields): Triggered when validation ends.

    • Parameters:
      • fields: The fields that were validated.
    • Example:
      onValidationEnd: (fields) => {
        console.log("Validation ended", fields);
      }
  • onValueChange(fieldId, value): Triggered when a field value changes.

    • Parameters:
      • fieldId: The ID of the field.
      • value: The new value of the field.
    • Example:
      onValueChange: (fieldId, value) => {
        console.log(fieldId, value);
      }
  • onFieldAdd(fieldId): Triggered when a field is added.

    • Parameters:
      • fieldId: The ID of the field being added.
    • Example:
      onFieldAdd: (fieldId) => {
        console.log(fieldId);
      }
  • onFieldRemove(fieldId): Triggered when a field is removed.

    • Parameters:
      • fieldId: The ID of the field being removed.
    • Example:
      onFieldRemove: (fieldId) => {
        console.log(fieldId);
      }
  • onFocus(field): Triggered when a field gains focus.

    • Parameters:
      • field: The field that gained focus.
    • Example:
      onFocus: (field) => {
        console.log(field);
      }
  • onBlur(field): Triggered when a field loses focus.

    • Parameters:
      • field: The field that lost focus.
    • Example:
      onBlur: (field) => {
        console.log(field);
      }

💡 Usage Example

Here's a comprehensive example demonstrating the power of shadow-form-handler:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Enhanced Form Validation Example</title>
  <link rel="stylesheet" href="./styles.css">
</head>
<body>
 
  <form id="myForm">

    <div>
      <label for="firstname">Firstname:</label>
      <input type="text" id="firstname" >
      <span id="firstname-error" class="error-message"></span>
    </div>
    <div>
      <label for="lastname">Lastname:</label>
      <input type="text" id="lastname" >
      <span id="lastname-error" class="error-message"></span>
    </div>
    <div>
      <label for="age">Age:</label>
      <input type="number" id="age" >
      <span id="age-error" class="error-message"></span>
    </div>
    <div>
      <label for="username">Username:</label>
      <input type="text" id="username" >
      <span id="username-error" class="error-message"></span>
    
    </div>
    <div>
      <label for="email">Email:</label>
      <input type="text" id="email" >
      <span id="email-error" class="error-message"></span>
    </div>
    <div>
      <label for="password">Password:</label>
      <input type="password" id="password" >
      <span id="password-error" class="error-message"></span>
    </div>
    <div>
      <label for="confirm-password">Confirm Password:</label>
      <input type="password" id="confirm-password" >
      <span id="confirm-password-error" class="error-message"></span>
    </div>
    <button type="submit">Submit</button>
    <button type="button" id="removeUsername">Remove Username Field</button>
    <button type="button" id="addDescription">Add Description Field</button>
    <button type="button" id="resetform">Reset Form</button>
  </form>
 
<script src="https://cdn.jsdelivr.net/gh/Mostafashadow1/[email protected]/dist/shadow-form-handler.js"></script>
  <script src="./index.js"></script>
</body>
</html>
const { FormHandler} = shadowFormHandler;
const formHandler = new FormHandler();
formHandler.setMode('runtime'); // runtime or default 

formHandler.lang.getCurrentLanguage();
// Add translations
formHandler.lang.addTranslations('en', {
'required': 'This field is required.',
'minLength': 'This field must be at least {min} characters long.',
'maxLength': 'This field must not exceed {max} characters.',
'email': 'Please enter a valid email address.',
'range': 'Value must be between {min} and {max}.',
'matches': 'This field must match {fieldToMatch}.',
});

formHandler.lang.addTranslations('ar', {
'required': 'هذا الحقل مطلوب.',
'minLength': 'يجب أن يحتوي هذا الحقل على {min} أحرف على الأقل.',
'maxLength': 'يجب ألا يتجاوز هذا الحقل {max} حرفًا.',
'email': 'يرجى إدخال عنوان بريد إلكتروني صالح.',
'range': 'يجب أن تكون القيمة بين {min} و {max}.',
'matches': 'يجب أن يتطابق هذا الحقل مع {fieldToMatch}.',
});

formHandler.lang.addTranslations('es', {
'required': 'Este campo es obligatorio.',
'minLength': 'Este campo debe tener al menos {min} caracteres.',
'maxLength': 'Este campo no debe exceder los {max} caracteres.',
'email': 'Por favor, introduce una dirección de correo electrónico válida.',
'range': 'El valor debe estar entre {min} y {max}.',
'matches': 'Este campo debe coincidir con {fieldToMatch}.',
});


formHandler.lang.setCurrentLanguage('ar')

formHandler.setErrorStyles({
color: "red",
marginTop:2,
})
formHandler.register({
id: "firstname",
initialValue: "",
schemaValidation: [
  formHandler.validation.required(),
  formHandler.validation.maxLength(10),
  formHandler.validation.minLength(4)
]
});
formHandler.register({
id: "lastname",
initialValue: "",
schemaValidation: [
  formHandler.validation.required(),
  formHandler.validation.maxLength(10),
  formHandler.validation.minLength(4)
]
});

formHandler.lang.addTranslations('en', {
'usernameTaken': 'This username is already taken.'
});

formHandler.lang.addTranslations('ar', {
'usernameTaken': 'اسم المستخدم هذا مستخدم بالفعل.'
});

formHandler.lang.addTranslations('es', {
'usernameTaken': 'Este nombre de usuario ya está en uso.'
});

formHandler.register({
id: "username",
dependencies: ['email'],
schemaValidation: [formHandler.validation.required() , formHandler.validation.minLength(3),
  formHandler.validation.maxLength(20)],
 customValidation: [
  {
    validate: async (value) => {
      // Simulate an API call to check if the email is already registered
      await new Promise(resolve => setTimeout(resolve, 1000));
      return !value.includes('shadow' && 'mostafa mohamed' && 'mostafa');
    },
    message: formHandler.lang.getTranslation('usernameTaken'),
  }
 ]
});



formHandler.register({
id: 'email',
schemaValidation: [
  formHandler.validation.required(),
  formHandler.validation.email()
]
});

formHandler.lang.addTranslations('en', {
'password': 'Password must contain at least one uppercase letter, one lowercase letter, and one number.',
'confirmPassword' : "Passwords do not match"

});

formHandler.lang.addTranslations('ar', {
'password': 'يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل، وحرف صغير واحد، ورقم واحد.',
'confirmPassword' : "كلمات المرور غير متطابقة."

});

formHandler.lang.addTranslations('es', {
'password': 'Le mot de passe doit contenir au moins une lettre majuscule, une lettre minuscule et un chiffre.',
'confirmPassword' : "Les mots de passe ne correspondent pas."
});

formHandler.register({
id: 'password',
schemaValidation: [
  formHandler.validation.required(),
  formHandler.validation.minLength(8),
  formHandler.validation.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, formHandler.lang.getTranslation('password'))
]
});


formHandler.register({
id: 'confirm-password',
schemaValidation: [
  formHandler.validation.required(),
  formHandler.validation.matches('password' , formHandler.lang.getTranslation('confirmPassword'))
]
});

formHandler.register({
id: 'confirm-password',
dependencies: ['password'],
customValidation: [{
  validate: (value, formValues) => value === formValues.password,
  message: formHandler.lang.getTranslation('confirmPassword')
}]
});

formHandler.register({
id: 'age',
schemaValidation: [
  formHandler.validation.required(),
  formHandler.validation.range(18, 100)
]
});

// Adding hooks for validation events
formHandler.addHooks({
onFormSubmitSuccess: (values) => {
  console.log("Form submitted successfully with values:", values);
},
onFormSubmitFail: (errors) => {
  console.error("Form submission failed with errors:", errors);
},
onFieldValidationError: (fieldId, error) => {
  if(error) {
    console.warn(`Validation error on field ${fieldId}: ${error}`);
  }
},
onFormReset: () => {
  console.log(`form reseted susccfully`);
},
beforeFieldRegister: (fieldId, params) => {
  console.log(`Registering field: ${fieldId}`, params);
},
afterFieldRegister: (fieldId, params) => {
  console.log(`Field registered: ${fieldId}`, params);
},
onValidationStart:  (fields) => {
  console.log( "beforeValidate", fields),
},
onValidationEnd:(fields) => {
  console.log("after validated" , fields)
},
onValueChange: (fieldId, value) => {
  console.log(fieldId , value)
},
onFieldAdd:(fieldId) => {
  console.log(fieldId)
},
onFieldRemove:(fieldId) => {
  console.log(fieldId)
},
onFocus:(field) => {
  console.log(field)
},
onBlur:(field) => {
  console.log(field)
},
})


// Adding custom event listener
formHandler.addCustomEventListener("email", "blur", (event) => {
console.log("Email input lost focus", event);
});

// Handling form submission
formHandler.submitHandler('myForm', (values) => {
console.log('Form submitted with values:', values);
});

// Adding a field dynamically
document.getElementById('addDescription').addEventListener('click', () => {
formHandler.addField({
  containerId:'myForm',
  fieldId:'description',
  labelText:'Description',
  register:{
  initialValue:'',
  schemaValidation:[formHandler.validation.required()],
  },
  position:3,
  disabledId:"addDescription",
  register:{
    initialValue: "",
    schemaValidation: [
      formHandler.validation.required(),
      formHandler.validation.maxLength(10),
      formHandler.validation.minLength(4)
    ],
    customValidation:[]
}
})
});

// Removing a field dynamically
document.getElementById('removeUsername').addEventListener('click', () => {
formHandler.removeField( {fieldId : "username" , disabledId:"removeUsername"} );
});


// Removing a field dynamically
document.getElementById('resetform').addEventListener('click', () => {
formHandler.resetForm()
});

🛠️ Best Practices

  1. Clear Error Messages: Provide clear and specific error messages for each validation rule to enhance the user's understanding.
  2. Asynchronous Validations: Use asynchronous validations sparingly to avoid performance issues. Ensure they are necessary and optimized.
  3. Error Handling: Implement proper error handling for asynchronous operations to manage potential issues gracefully.
  4. Utilize Hooks: Leverage hooks to extend functionality without modifying the core logic, ensuring maintainability and flexibility.
  5. Regular Resets: Regularly reset the form to clear old data and errors, maintaining a fresh state for new submissions.

🔧 Troubleshooting

  • Validation Issues: If fields are not being validated, ensure they are properly registered with the correct validation rules.
  • Runtime Validation: For runtime validation issues, verify that the correct mode (default or runtime) is set.
  • Event Listeners: If custom event listeners are not firing, check the element IDs and event types to ensure they are correctly specified.

🤝 Contributing

Contributions to the shadow-form-handler package are welcome! Please refer to the contributing guidelines in the repository for more information on how to submit pull requests, report issues, or request features.

📄 License

This package is released under the MIT License. See the LICENSE file in the package repository for more details.