1.1.1 • Published 2 years ago

form-gear v1.1.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

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:

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 TypeCodeDescription
Section1Adds section to form.
NestedInput2Adds nested input field to existing field.
InnerHTML3Adds text written in HTML format.
VariableInput4Variable input.
DateInput11Date input.
DateTimeLocalInput12Date and time input field with no time zone.
TimeInput13Time input field.
MonthInput14Month input field.
WeekInput15Week input field.
SingleCheckInput16Checkbox input field, lets user choose only one option out of limited choices.
ToggleInput17Toggle input.
RangeSliderInput18Range slider input, lets user to select a value or range of value from a specified min and max.
UrlInput19URL input field.
CurrencyInput20Currency input field, limited to IDR and USD.
ListTextInputRepeat21Text input field, creating a list by letting user add more written input as needed.
ListSelectInputRepeat22Dropdown input field, creating a list by letting user add more choices as needed.
MultipleSelectInput23Drop-down input, lets user to choose one or more options of limited choices.
MaskingInput24Number input with certain formatting, such as phone number and taxpayer identification number.
TextInput25Single-line input field.
RadioInput26Radio button, lets user choose one options of limited choices.
SelectInput27Drop-down input.
NumberInput28Numeric input field.
CheckboxInput29Checkbox input field, lets user choose one or more options of limited choices.
TextAreaInput30Adjustable text area input field.
EmailInput31Email address input field.
PhotoInput32Photo input, lets user add picture with .jpg, .jpeg, .png, and .gif format.
GpsInput33GPS input.
CsvInput34CSV 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 TypeCorresponding ControlTypeInput TypeDescriptionNotes
dataKeyAllstringComponent identifier.
labelAllstringComponent label that will show up in the form.
hintAllstringProvide hint on how to fill the corresponding field.
typeAllControlTypeanyDefine the ControlType of a field.
components1, 2ComponentTypeStore components inside a Section or NestedInput.
rows30numberDefine the number of rows needed in TextAreaInput.
cols26, 29numberDefine the number of columns the options in RadioInput and CheckboxInput will be divided to.
options22, 23, 26, 29Option[]Define options for ListSelectInputRepeat, MultipleSelectInput, RadioInput, and CheckboxInput.
range18Range[]Define the min, max, and value of each step for a RangeSliderInput.
description1, 2stringAdds description for a Section or NestedInput.
answerAllanyStoring answer collected on data collection.ListSelectInputRepeat and MultipleSelectInput must add: [{"label": "lastId#0","value": "0"}]
sourceQuestion2stringDefine the source question for a NestedInput field.
sourceOption22, 23, 26, 27, 29stringSource of the options used in the field.
typeOption22, 23, 26, 27, 29numberType of the options used in the field.1: Its component, 2: A lookup table, 3: Other component.
currency20stringDefine the currency that will be used in a CurrencyInput field, limited to IDR and USD.
separatorFormat20stringDefine the separator that will be used in a CurrencyInput field.
isDecimal20booleanDefine whether or not a CurrencyInput field allows decimal.
maskingFormat24stringDefine the format of maskingInput used.9 for numbers, a for letters, and * for alphanumeric format. e.g. : 'maskingFormat' : '9999-aa99'
expression4stringA variable expression.getValue function can be used to get the value of other field.
componentVar4string[]Define the component(s) used in VariableInput.
render4booleanDefine whether or not the variable will be rendered.
renderType4numberVariable output type.1: single output, 2: array output
enableAllbooleanDefine whether or not a component have a condition(s) that need to be fulfilled before it can be filled.
enableConditionAllstringAn 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.
componentEnableAllstring[]A list of component(s) used in a condition expression.
enableRemarkAllbooleanDefine whether or not a component allows a remark.
titleModalDelete21, 22stringTitle of the warning that will show up when user tries to delete an item in ListTextInputRepeat or ListSelectInputRepeat.
contentModalDelete21, 22stringContent 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

1.1.1

2 years ago

1.0.2

2 years ago

1.1.0

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago

1.0.3

2 years ago

0.1.2

2 years ago

0.1.3

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago

0.0.12

2 years ago

0.0.11

2 years ago

0.0.10

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago