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

form-gear

v1.1.1

Published

FormGear is a framework engine for dynamic form creation and complex form processing and validation for data collection.

Downloads

50

Readme

About

FormGear is a framework engine for form creation, processing, and validation. FormGear is designed to support official statistics data collection in BPS - Statistics Indonesia, Indonesia's National Statistics Office. It was done by a team under the direction of BPS. This requirement calls for dynamic form creation, complex processing and validation, and ease of use in the data collection process.

FormGear uses a defined JSON object template, thus is easy to build, use, and efficiently handle nested inquiries to capture everything down to the last detail. Unlike other similar framework, validation is handled in a FALSE condition in which each field is validated against a test equation. This leads to a more efficient and effective way of validating each component.

Features

  • Easily create complex form with various form controls.
  • Divide form into several sections for ease during data collection.
  • Create nested form inquiry to accommodate recurring fields.
  • Add conditions to enable form control.
  • Validate answer given during data collection with your own test function.
  • Add remark to record additional information.
  • Add preset to provide information acquired prior to data collection.

Usage

Table of Content

Online examples

Develop on JS Framework examples:

  • SolidJS
    • Demo : https://solid-form-gear.vercel.app/
    • Code Example : https://codesandbox.io/s/solid-form-gear-vvj0wt
    • Repo : https://github.com/AdityaSetyadi/solid-form-gear
  • Angular
    • Demo : https://angular-form-gear.vercel.app/
    • Code Example : https://codesandbox.io/s/angular-form-gear-diy9p0
    • Repo : https://github.com/AdityaSetyadi/angular-form-gear
  • Vue.js
    • Demo : https://vue-form-gear.vercel.app/
    • Code Example : https://codesandbox.io/s/vue-form-gear-sfilvp
    • Repo : https://github.com/AdityaSetyadi/vue-form-gear

Installation

FormGear can be installed via package manager like npm or yarn, or it can be used directly via CDN.

npm install form-gear
import { FormGear } from 'form-gear'
import {  } from './node_modules/form-gear/dist/style.css'

const data = Promise.all([
                fetch("./data/template.json").then((res) => res.json()),
                fetch("./data/preset.json").then((res) => res.json()),
                fetch("./data/response.json").then((res) => res.json()),
                fetch("./data/validation.json").then((res) => res.json())
                fetch("./data/remark.json").then((res) => res.json())
            ]);

data.then(([template, preset, response, validation, remark]) => initForm(template, preset, response, validation, remark));

function initForm(template, preset, response, validation, remark){
        
   let config = {
      clientMode: 1, // CAWI = 1, CAPI = 2
      token: ``,
      baseUrl: ``,
      lookupKey: `key%5B%5D`,
      lookupValue: `value%5B%5D`,
      username: 'AdityaSetyadi',
      formMode: 1 // 1 => OPEN ; 2 => REJECTED ; 3 => SUBMITTED ; 4 => APPROVED ;
   }
   
   let uploadHandler = function (setter) {
      console.log('camera handler', setter);
      cameraFunction = setter;
      openCamera();
   }

   let GpsHandler = function (setter, isPhoto) {
      console.log('camera handler', setter);
      isPhoto = true,
         cameraGPSFunction = setter;
      openCameraGPS(isPhoto);
   }

   let onlineSearch = async (url) =>
      (await fetch(url, setBearer())
         .catch((error: any) => {
            return {
               success: false,
               data: {},
               message: '500'
            }
         }).then((res: any) => {
         if (res.status === 200) {
            let temp = res.json();
            return temp;
         } else {
            return {
               success: false,
               data: {},
               message: res.status
            }
         }
         }).then((res: any) => {
            return res;
         }
      ));

   let setResponseMobile = function (res, rem) {
      respons = res
      remarks = rem

      console.log('respons', respons)
      console.log('remarks', remarks)
   }

   let setSubmitMobile = function (res, rem) {
      respons = res
      remarks = rem

      console.log('respons submit', respons)
      console.log('remarks submit', remarks)
   }

   let openMap = function (koordinat) {
      koordinat = koordinat

      console.log('coordinat ', koordinat)
   }

   let form = FormGear(template, preset, response, validation, remark, config, uploadHandler, GpsHandler, onlineSearch, setResponseMobile, setSubmitMobile, openMap);
   
   return form;
   
}

Template

FormGear use defined template which is based on JSON Object. This allows for dynamic form creation, letting you to easily build your form with various form controls simply by expanding the JSON object. This also allows FormGear to create nest other form control in other form control, making it easy for you to create recurring questions based on the nested form control. Below is the structure and example of the JSON object that FormGear uses as its form template.

template.json
│   description
│   dataKey
│   title
│   acronym
│   ...
└───components
│   │   section
│   └───components
│       │   textInput
│       │   checkboxInput
│       │   selectInput
│       │   ...
└───
│   │   section
│   └───components
│       │   textInput
│       │   checkboxInput
│       │   nestedInput
│       └───
│           │   sourceQuestion
│           └───components
│               │   textInput
│               │   checkboxInput
│               │   nestedInput
│               └───
│                   │   sourceQuestion
│                   └───components
│                       │   textInput
│                       │   dateInput
│                       │   ...
│               │   ...
│       │
│       │   dateInput
│       │   ...
└───
│   │   section
│   └───components
│       │   textInput
│       │   checkboxInput
│       │   selectInput
│       │   ...
│
[
    {
        "label":"Hobbies",
        "dataKey":"hobbies",
        "type":29,
        "cols":3,
        "options":[
            {
                "label":"Sleeping",
                "value":"1"
            },
            {
                "label":"Play Games",
                "value":"2"
            },
            {
                "label":"Watching Movie",
                "value":"3"
            },
            {
                "label":"Cooking",
                "value":"4"
            },
            {
                "label":"Working",
                "value":"5"
            },
            {
                "label":"Travelling",
                "value":"6"
            },
            {
                "label":"Other",
                "value":"13",
                "open":true
            }
        ]
    },
    {
        "label": "Province",
        "dataKey": "l2_r322_prov_1",
        "typeOption" : 2,
        "type": 27,
        "enableCondition":"Number(getValue('l2_land_location@$ROW$')) > 0",
        "componentEnable":["l2_land_location@$ROW$"],
        "sourceSelect":[{
            "id": "f796a32a-36df-4554-bb79-bf8065e28c52",
            "tableName": "kode_kab_ppkk",
            "value": "kode_prov",
            "desc": "nama_prov",
            "parentCondition": [
            
            ]  
        }]
    },
    {
        "label":"Healthy neighborhood rating",
        "dataKey":"rating",
        "type":26,
        "cols":5,
        "options":[
            {
                "label":"",
                "value":"1"
            },
            {
                "label":"",
                "value":"2"
            },
            {
                "label":"",
                "value":"3"
            },
            {
                "label":"",
                "value":"4"
            },
            {
                "label":"",
                "value":"5"
            }
        ]
    },
    {
        "label":"Happiness Index",
        "dataKey":"happy",
        "type":18,
        "range":[
        {
            "min":0,
            "max":100,
            "step":5
        }
        ]
    },
    {
        "label":"Chld nested",
        "dataKey":"childnested",
        "description":"ChildNested",
        "type":2,
        "sourceQuestion":"hobbies",
        "components":[
            [
                {
                    "label":"name--- $NAME$",
                    "dataKey":"name_",
                    "type":4,
                    "expression":"let nm = ''; let list = getValue('hobbies'); if(list !== undefined && list.length > 0) { let rowIndex = getRowIndex(0); let filter = list.filter(obj => obj.value == rowIndex); nm=filter[0].label }; nm;",
                    "componentVar":["hobbies"],
                    "render":true,
                    "renderType":1
                }
            ]
        ]
    }
]

Control Type


FormGear allows you to work with a lot of possible HTML input types. To add a control type, you can simply add the code as a value of the type component as a JSON object and add other corresponding components:

{
   "description":"Interviewing family characteristics individually",
   "dataKey":"family-characteristics-2022",
   "title":"Family Characteristics",
   "acronym":"FC-22.Individu",
   "version":"0.0.1",
   "components":[
		    {
                        "label":"Full Name",
                        "dataKey":"full_name",
                        "hint":"Full name including his degree, position, etc",
                        "answer":"Ignatius",
                        "enableRemark":true,
                        "type":25
                    }
		]
}

FormGear uses numbers as code to define each control type. Below is a table of control types and their corresponding code and description.

| Control Type | Code | Description | | --- | --- | --- | | Section | 1 | Adds section to form. | | NestedInput | 2 | Adds nested input field to existing field. | | InnerHTML | 3 | Adds text written in HTML format. | | VariableInput | 4 | Variable input. | | DateInput | 11 | Date input. | | DateTimeLocalInput | 12 | Date and time input field with no time zone. | | TimeInput | 13 | Time input field. | | MonthInput | 14 | Month input field. | | WeekInput | 15 | Week input field. | | SingleCheckInput | 16 | Checkbox input field, lets user choose only one option out of limited choices. | | ToggleInput | 17 | Toggle input. | | RangeSliderInput | 18 | Range slider input, lets user to select a value or range of value from a specified min and max. | | UrlInput | 19 | URL input field. | | CurrencyInput | 20 | Currency input field, limited to IDR and USD. | | ListTextInputRepeat | 21 | Text input field, creating a list by letting user add more written input as needed. | | ListSelectInputRepeat | 22 | Dropdown input field, creating a list by letting user add more choices as needed. | | MultipleSelectInput | 23 | Drop-down input, lets user to choose one or more options of limited choices. | | MaskingInput | 24 | Number input with certain formatting, such as phone number and taxpayer identification number. | | TextInput | 25 | Single-line input field. | | RadioInput | 26 | Radio button, lets user choose one options of limited choices. | | SelectInput | 27 | Drop-down input. | | NumberInput| 28 | Numeric input field. | | CheckboxInput | 29 | Checkbox input field, lets user choose one or more options of limited choices. | | TextAreaInput | 30 | Adjustable text area input field. | | EmailInput | 31 | Email address input field. | | PhotoInput | 32 | Photo input, lets user add picture with .jpg, .jpeg, .png, and .gif format. | | GpsInput | 33 | GPS input. | | CsvInput | 34 | CSV input, lets user upload .csv file to be stored as .json format in the Response. The .csv file can later be downloaded again in the same format. |

Component Type


To customize each form control further, you can use components . Below are the components, the corresponding ControlType in which the component can be used, the input or value type of each component, and the description.

| Component Type | Corresponding ControlType | Input Type | Description | Notes | | --- | --- | --- | --- | --- | | dataKey | All | string | Component identifier. | | | label | All | string | Component label that will show up in the form. | | | hint | All | string | Provide hint on how to fill the corresponding field. | | | type | All | ControlType | any | Define the ControlType of a field. | | | components | 1, 2 | ComponentType | Store components inside a Section or NestedInput. | | | rows | 30 | number | Define the number of rows needed in TextAreaInput. | | | cols | 26, 29 | number | Define the number of columns the options in RadioInput and CheckboxInput will be divided to. | | | options | 22, 23, 26, 29 | Option[] | Define options for ListSelectInputRepeat, MultipleSelectInput, RadioInput, and CheckboxInput. | | | range | 18 | Range[] | Define the min, max, and value of each step for a RangeSliderInput. | | | description | 1, 2 | string | Adds description for a Section or NestedInput. | | | answer | All | any | Storing answer collected on data collection. | ListSelectInputRepeat and MultipleSelectInput must add: [{"label": "lastId#0","value": "0"}] | | sourceQuestion | 2 | string | Define the source question for a NestedInput field. | | | sourceOption | 22, 23, 26, 27, 29 | string | Source of the options used in the field. | | | typeOption | 22, 23, 26, 27, 29 | number | Type of the options used in the field. | 1: Its component, 2: A lookup table, 3: Other component. | | currency | 20 | string | Define the currency that will be used in a CurrencyInput field, limited to IDR and USD. | | | separatorFormat | 20 | string | Define the separator that will be used in a CurrencyInput field. | | | isDecimal | 20 | boolean | Define whether or not a CurrencyInput field allows decimal. | | | maskingFormat | 24 | string | Define the format of maskingInput used. | 9 for numbers, a for letters, and * for alphanumeric format. e.g. : 'maskingFormat' : '9999-aa99' | | expression | 4 | string | A variable expression. | getValue function can be used to get the value of other field. | | componentVar | 4 | string[] | Define the component(s) used in VariableInput. | | | render | 4 | boolean | Define whether or not the variable will be rendered. | | | renderType | 4 | number | Variable output type. | 1: single output, 2: array output | | enable | All | boolean | Define whether or not a component have a condition(s) that need to be fulfilled before it can be filled. | | | enableCondition | All | string | An expression to define a condition enabled for the field to be filled. | getProp function can be used to get the client mode, getValue function can be used to get the value of other field. | | componentEnable | All | string[] | A list of component(s) used in a condition expression. | | | enableRemark | All | boolean | Define whether or not a component allows a remark. | | | titleModalDelete | 21, 22 | string | Title of the warning that will show up when user tries to delete an item in ListTextInputRepeat or ListSelectInputRepeat. | | | contentModalDelete | 21, 22 | string | Content of the warning that will show up when user tries to delete an item in ListTextInputRepeat or ListSelectInputRepeat. | |

Input for ListSelectInputRepeat, MultipleSelectInput, RadioInput, and CheckboxInput


Option[] defines option components for ListSelectInputRepeat, MultipleSelectInput, RadioInput, and CheckboxInput input.

  • label: label of an option that will show up in the form.
  • value: value of an option that will be recorded.
  • open: define whether or not an option is open-ended. The value recorded will be both the value of an option and the label entered on data collection.
[
    {
        "label":"Hobbies",
        "dataKey":"hobbies",
        "type":29,
        "cols":3,
        "options":[
            {
                "label":"Sleeping",
                "value":"1"
            },
            {
                "label":"Play Games",
                "value":"2"
            },
            {
                "label":"Watching Movie",
                "value":"3"
            },
            {
                "label":"Cooking",
                "value":"4"
            },
            {
                "label":"Working",
                "value":"5"
            },
            {
                "label":"Travelling",
                "value":"6"
            },
            {
                "label":"Other",
                "value":"13",
                "open":true
            }
        ]
    }
]

Input for RangeSliderInput


Range[] defines components of RangeSliderInput input.

  • min: minimum of a range.
  • max: maximum of a range.
  • step: added value for each step.
[
   {
       "label":"Happiness Index",
       "dataKey":"happy",
       "type":18,
       "range":[
            {
                "min":0,
                "max":100,
                "step":5
            }
        ]
    }
]

Input for sourceSelect


sourceOption defines components of sourceSelect input.

  • id: unique identifier of the lookup table that will be used.
  • tableName: the name of the lookup table that will be used.
  • value: the column name in the lookup table that will be recorded as the option’s value.
  • desc: the column name in the lookup table that will be shown as the option’s label.
  • parentCondition: an array consisting of key and value. key refers to the parent lookup table’s value, while value refers to the parent lookup table’s desc.
[
    {
        "label":"Child nested",
        "dataKey":"childnested",
        "description":"ChildNested",
        "type":2,
        "sourceQuestion":"hobbies",
        "components":[
            [
                {
                    "label":"name--- $NAME$",
                    "dataKey":"name_",
                    "type":4,
                    "expression":"let nm = ''; let list = getValue('hobbies'); if(list !== undefined && list.length > 0) { let rowIndex = getRowIndex(0); let filter = list.filter(obj => obj.value == rowIndex); nm=filter[0].label }; nm;",
                    "componentVar":["most_fav"],
                    "render":true,
                    "renderType":1
                }
            ]
        ]
    }
]

Preset

Preset is used to provide prefilled data given prior to data collection. This prefilled data usually obtained from previous data collection or a listing conducted before the actual data collection. Preset consists of dataKey of the corresponding field and the prefilled answer to that field.

preset.json
│
└───predata
│   │
│   └───
│       │   dataKey
│       │   answer
│   │
│   └───
│       │   dataKey
│       │   answer
│
{
   "description":"sample template",
   "dataKey":"sample_tpl1",
   "predata":[
      {
         "dataKey":"nama_lengkap",
         "answer":"Setyadi"
      }
   ]
}

Response

Response is initially empty, and is used to store any response given later during data collection. Response consists of dataKey of the corresponding field and the answer collected from that field. answer from a field that has both label and value will record both. answer will only be collected if the value entered to the corresponding field satisfied both the condition enabled and validation test function. Below is the structure and example of the response JSON object:

response.json
│
│   description
│   dataKey
│   templateVersion
│   validationVersion
│   createdBy
│   updatedBy
│   createdAt
│   updatedAt
└───answers
│   	   │
│   	   └───
│       	│   dataKey
│       	│   answer	
│   	   │
│   	   └───
│       	│   dataKey
│       	│   answer
│
{
   "description":"sample template",
   "dataKey":"sample_tpl1",
   "answers":[
      {
         "dataKey":"agree",
         "answer":true
      },
      {
         "dataKey":"full_name",
         "answer":"Agung"
      },
      {
         "dataKey":"members",
         "answer": {
            "value" : "3",
            "label" : "Clementine Bauch"
         }
      },
      {
         "dataKey":"address",
         "answer":"Jalan Otista"
      },
      {
         "dataKey":"field_usage",
         "answer":[
            {
               "label": "lastId#1",
               "value": "0"
            },
            {
               "label": "Farm",
               "value": "1"
            },
            {
               "label": "Meadows",
               "value": "3"
            }
         ]
      },
   ]
}

Validation

Validation is used to validate the answer given during data collection against a test function. Unlike other similar framework, validation is handled in a FALSE condition, meaning the test function is a condition where the value entered to a form control would be false. Validation has several components you can add:

  • dataKey: component identifier.
  • validations: store validation for each dataKey.
  • componentValidation: a list of the dataKey of components used in the test function.
  • test: the test function. A getValue function can be used to get the value of other field.
  • message: the warning message that will show up when the value entered did not fulfill the test function.
  • type: define whether the validation will be just a warning or an error.

Below are the structure and example of the validation JSON object:

validation.json
│
└───description
│   dataKey
│   version
│   testFunctions
│   │
│   └───
│       │   dataKey
│       │   componentValidation
│       │   validations
│			│
│			└───
│			    │   test
│			    │   message
│			    │   type	
│			│
│			└───
│			    │   test
│			    │   message
│			    │   type
│
{
    "description":"sample template",
    "dataKey":"sample_tpl1",
    "version":"0.0.1",
    "testFunctions":[
        {
            "dataKey":"age",
            "componentValidation":["age"],
            "validations": [
                {
                    "test":"getValue('age') >= 8 && getValue('age') < 10",
                    "message":"Min.10 y.o.",
                    "type":1
                },
                {
                    "test":"getValue('age') < 8",
                    "message":"Min.8 y.o.",
                    "type":2
                }
            ]
        }
    ]
}

Remark

Remark is initially empty and used to store notes on each field collected later during data collection. This note can be used to provide additional information of a field and bypass validation if the data found during data collection doesn’t satisfy the test function. Below is the structure and example of the remark JSON object:

remark.json
│
└───dataKey
|   notes
│   │
│   └───
│       │   dataKey
│       │   comments
│		    │
│		    └───
│		        │   sender
│		        │   dateTime
│		        │   comment	
│		    │
│		    └───
│		        │   sender
│		        │   dateTime
│		        │   comment
│
{
    "dataKey": "",
    "notes": [
        {
            "dataKey": "full_name",
            "comments": [
                {
                    "sender": "AdityaSetyadi",
                    "datetime": "2022-03-17 15:09:54",
                    "comment": "Based on the ID Card"
                },
                {
                    "sender": "AdityaSetyadi",
                    "datetime": "2022-03-18 10:10:40",
                    "comment": "Not the same with his driving license"
                }
            ]
        },
        {
            "dataKey": "land_area#1",
            "comments": [
                {
                    "sender": "AdityaSetyadi",
                    "datetime": "2022-03-18 01:09:15",
                    "comment": "Unknown"
                }
            ]
        }
    ]
}

Contributing

Your assistance is greatly appreciated if you want to contribute and make it better.

Further development ideas:

  • FormGear templates design platform
  • FormGear validation creator platform

License

FormGear is licensed under MIT License.

Our Team