DEV Community

Petr
Petr

Posted on

Implementing Complex Form Logic Effortlessly with forms.js ๐Ÿš€

In the world of web development, forms are the key points of user interaction, collecting necessary information that drives business processes and system inputs. However, as straightforward as forms might appear, their underlying logic can scale in complexity very quickly. From conditional fields that appear based on previous answers to real-time validation that checks user input against database records. Managing form behavior can become a daunting task, especially in large-scale applications.

This is where forms.js comes into play. Created with the intent to ease web forms development and management, forms.js is a robust JavaScript library designed to streamline the creation and handling of dynamic forms. Whether you are building a simple contact form or a complex multi-stage application process, forms.js offers a suite of tools that make it easy to implement even the most complex form logic effortlessly.

As the lead developer and creator of forms.js I have seen many forms get from simple to hardly manageble very quickly as the projects grew. If you can relate to this it might be time that you try our library out. Letโ€™s dive into how forms.js can help you manage web forms more effectively, ensuring a smooth and accessable user experience.

Why forms.js?

If you are not convinced yet, we can go through couple of pros that forms.js can bring into your project.

Simplification of source code

Our library can significantly improve code readability. You can forget about cluttered couple thousand line forms where finding the hidden logic is about as enjoyable as your next dentist appointment.

Ease of use

You donโ€™t need to be an expert in javascript to work with this library, if you understand json input you are fine for the time being.

Extendability

The fact that the basics are very simple to use does not mean the experienced devs cannot wield the power of the library. There is a plugin system implemented that allows anybody to write and use custom plugins. The plugins can use features like validation, conditional logic and data management all handled by the library and you can just focus on what you need to implement.

High performance in lightweight size

Despite its powerful features, forms.js is lightweight and has a minimal impact on your projectโ€™s performance.

You can find the full documentation on our website where all features are listed and explained.

It is open source and continuously improving

As an open-source project hosted on github, forms.js is continuously improved by contributions from developers around the world. This collaborative effort ensures the library stays up-to-date with the latest web technologies and best practices.

Getting Started with forms.js

We expect you will be using NPM in your project if not check the documentation for more info about other means of including the library in your project.

First letโ€™s install the package in our project:

npm i @forms.js/core
Enter fullscreen mode Exit fullscreen mode

Then in our javascript files we will have the package import available.
The main class is called Form and it is responsible for initializing and handling form behaviour.

import { Form } from '@forms.js/core';
Enter fullscreen mode Exit fullscreen mode

if we want to use forms.js default styles we can import the css file too.

@import "@forms.js/core/css/index.css";
Enter fullscreen mode Exit fullscreen mode

Few minutes and we can already start creating forms. To showcase how simple for creation is below is a code for a login form.

import { Form } from "https://esm.sh/@forms.js/core";

new Form("login-form", {
  id:"login-form",
  schema: [
    {
      id: "username",
      type:"text",
      label: "Username",
      required: true,
    },
    {
      id: "password",
      type:"password",
      label: "Password",
      required: true,
    },
    {
      id: "rememberUser",
      type:"checkbox",
      label: "Remember me",
      toggle: true,
    },
    {
      id:"buttonGroup",
      type:"group",
      schema:[
         {
            id: "loginButton",
            type:"button",
            template: "Login",
        },   
      ]
    }
  ]
});
Enter fullscreen mode Exit fullscreen mode

Deep Dive: Implementing Complex Form Logic

While forms.js simplifies form handling, its true strength lies in effortlessly managing complex form logic that can be daunting with traditional approaches. We'll explore dynamic form fields based on user input, and real-time data validation, each accompanied by detailed code snippets.

As our example we will create a registration form for SaaS product. In this hypothetical SaaS as a super admin you will have ability to register users under your company and that is the form that we will make. The form will have following fields:

  • Role - select - a role that will the user have in the system, required
  • Personal Information
    • First Name - text, required
    • Last Name - text, required
    • Email - validated email
  • Contact Details - visible if a role is not guest
    • Phone Number - validated phone number - required if the role is not guest
    • Address - text for simplification
  • Admin Credentials - visible if a role is admin
    • Password - validated password
  • Terms and Conditions - checkbox - required before submission

This list of fields is essentially the schema of the form, we will just need to write it into the correct input.

We will start by defining the role select field:

const registrationFormSchema = [
    {
        id: "role",
        type: "select",
        default: "user",
        optionsList: [
            {
                value: "guest",
                label: "Guest",
            },
            {
                value: "user",
                label: "User",
            },
            {
                value: "admin",
                label: "Admin",
            },
        ]
    }
];
Enter fullscreen mode Exit fullscreen mode

After that we can start implementing the rest of the logic, a lot of conditions will be based on this the role select field. We can group the fields in groups to allow better field separation. The conditional logic is documented here.

const registrationFormSchema = [
    // previous field implementation,
    {
        id: "personalInformation",
        type: "group",
        schema: [
            {
                id: "firstName",
                type: "text",
                label: "First Name",
                required: true,
            },
            {
                id: "lastName",
                type: "text",
                label: "Last Name",
                required: true,
            },
            {
                id: "email",
                type: "email",
                label: "Email",
                required: true,
            },
        ]
    },
    {
        id: "contactInformation",
        type: "group",
        conditions: (data)=>{
            return data.role !== 'guest';
        },
        schema: [
            {
                id: "phoneNumber",
                type: "tel",
                label: "Phone Number"
            },
            {
                id: "address",
                type: "text",
                label: "Address"
            },
        ]
    }
];
Enter fullscreen mode Exit fullscreen mode

We can see that we have defined conditions parameter on the contact group as function that returns boolean value. When the function returns true the field is visible, if it returns false the field gets hidden. Now we can finish the rest of the fields, we will implement a custom validation for the password and the terms fields.

const registrationFormSchema = [
    // previous field implementation,
    {
        id: "adminCredentials",
        type: "group",
        conditions: (data)=>{
            return data.role === 'admin';
        },
        schema: [
            {
                id: "password",
                type: "password",
                label: "Admin Password",
                required: (value, data)=>{
                    return data.role === "admin";
                },
                validation: (value, data, required) => {
                    if(required && !value){
                        return "This field is required";
                    }
                    if(value && !value.match(/^(?=.*[0-9])$/)){
                        return "Password must include at least one number";
                    }
                    return true;
                }               
            },
        ]
    },
    {
        id: "terms",
        type: "checkbox",
        label: "I agree to the terms and conditions",
        required: true,
        validation: (value, data, required) => {
            if(required && !value){
                return "You need to agree with terms and conditions to submit the form";
            }
            return true;
        }
    },
];
Enter fullscreen mode Exit fullscreen mode

For our password field we have defined a custom required function that says that the field is required only when the role is admin. We have also implemented a validation that checks if the password includes any characters. Validation function returns true if the field is valid otherwise it returns a string with the error. We have applied similar logic to the terms field and defined custom validation error there too.

Now we will just add a submit button and initialize the form. Below you can find codepen with this solution.

import { Form } from "https://esm.sh/@forms.js/core";

const registrationFormSchema = [
    // previous field implementation,
    {
      id:"buttonGroup",
      type:"group",
      schema:[
         {
            id: "submitButton",
            type:"button",
            template: "Create",
        },   
      ]
    }
];

const form = new Form("form", {
  id:"registrationForm",
  schema: registrationFormSchema,
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

The integration of forms.js into your project management tools not only simplifies the handling of complex forms but also elevates the user experience by making it more interactive and responsive. As we've explored in this article, forms.js provides a robust framework for dynamically managing form inputs, enforcing real-time validations, and adapting form behavior based on user interactions. These capabilities are crucial for modern day platforms and applications, especially in areas requiring precise data collection and processing like project management.

By leveraging forms.js, developers can significantly reduce the time and effort spent on form-related coding while improving the accuracy and user-friendliness of their applications. Next time we will explore the application of forms.js in large-scale projects and create multipage form using the premium features.

If you have any questions or just want to say hi, please create discussion on our github project. We highly appreciate all of the contributions and continuously working on improving the library. You can also find more information about the project on forms.js website

Finally, if you've implemented forms.js in your projects, share your experiences and outcomes on social media or tech forums. Your insights and feedback are invaluable in helping others understand the potential benefits of using forms.js and in driving forward the evolution of this powerful tool.

Top comments (2)

Collapse
 
duncan_true profile image
Dun

Great read! I'm curious, how does forms.js handle accessibility for screen readers and other assistive technologies?

Collapse
 
trimatic profile image
Petr

Hi, Thank you! I am preparing to make a separate post about this as it is a fascinating topic. The accessibility is partially supported through rendering correctly structured inputs and groups under the hood which in itself is supported by screen readers, additionally, the correct aria labeling with aria-invalid and aria-describedby is taking that even further and making sure even the validation and any deeper logic is reflected on readers.