Simple react component for building webforms. View Demo
Feature | Description |
---|---|
Initial values | |
Question Group Description | |
Translations | |
Multiple Question Dependency | |
Rule based response validation | |
Save Datapoint | |
Computed field value | |
Clear response | |
Custom style | |
Tooltip | |
Extra component on Question | |
HTML Support on Question | |
Field Suffix / Prefix | |
Download response to tabular format |
npm install --save akvo-react-form
yarn add akvo-react-form
Type | Description |
---|---|
input | Input |
number | InputNumber |
cascade | Cascade Select |
text | TextArea |
date | Date |
option | Option |
multiple_option | Multiple Select |
tree | Tree Select |
table | Table (Multiple Question) |
autofield | Autofieled |
image | Image |
entity | Entity cascade select |
import React from 'react';
import 'akvo-react-form/dist/index.css'; /* REQUIRED */
import { Webform } from 'akvo-react-form';
import * as forms from './example.json';
const App = () => {
const onChange = ({ current, values, progress }) => {
console.log(progress);
};
const onFinish = (values, refreshForm) => {
console.log(values);
};
return (
<div className="full-width">
<Webform
forms={forms.default}
onChange={onChange}
onFinish={onFinish}
/>
</div>
);
};
export default App;
If using the autosave parameter, a form refresh is required to remove traces of the previous fields.
onFinish
includes a function to do this, you also need to change the autosave parameter if the submission is successful. If not, the form refresh will clean up the current datapoint.
Example:
akvo-react-form/example/src/App.js
Lines 41 to 46 in 8da791c
Props | Description | Type | Default |
---|---|---|---|
sidebar | Option to show / hide sidebar | Boolean | true |
sticky | Sticky header and sidebar (Not support for IE9) | Boolean | false |
onFinish | Trigger after submitting the form and verifying data successfully | function(values) |
- |
onChange | Trigger after field value changed | function({current,values,progress}) |
- |
onCompleteFailed | Trigger when submit is clicked with blank required question | function(values, errorFields) |
- |
submitButtonSetting | Submit Button Setting | Object{loading: Boolean, disabled: Boolean} | undefined |
{} |
extraButton | Extra Button Next to Submit Button | ReactComponent | undefined |
- |
initialValue | Set value by Form initialization | Array[Initial Value] | undefined |
Array[] |
printConfig | Support survey print functionality | Object{showButton: Boolean, filename: String, hideInputType: Array["field type"], header: ReactComponent} | undefined |
- |
downloadSubmissionConfig | Support download submission to Excel | Object{visible: Boolean, filename: String, horizontal: Boolean} | undefined |
- |
leftDrawerConfig | Show left drawer with custom component | Object{visible: Boolean, title: String, Content: ReactComponent} | undefined |
- |
autoSave | Enable auto save to IndexedDB | autoSaveObject | undefined |
- |
fieldIcons | Show icon for input and number question type | Boolean | true |
formRef | Set react useRef for Form from host |
React useRef |
null |
languagesDropdownSetting | Languages Dropdown Setting | Object{showLanguageDropdown: Boolean , languageDropdownValue: ISO 639-1 codes} | undefined |
{} |
UIText | UI localization custom param | Object{[ISO 639-1 codes]: {...translations}} | undefined |
{} |
showSpinner | Show a loading spinner when seeding data in progress | Boolean | false |
Props | Description | Type |
---|---|---|
Unique{any} | Object to be translated | Object{any} |
language | Language | Enum[ISO 693-1] |
Props | Description | Type |
---|---|---|
name | Form Name / Title | String |
question_group | List of Question Group | Array[Question Group] |
Unique{any} | Cascade definition, can be any properties | Array[Cascade] |
languages | List of available languages | Array[enum[ISO 639-1]] | undefined |
defaultLanguage | Default active language | Enum[ISO 639-1]] | undefined |
translations | List of translations | Array[Translations] | undefined |
Props | Description | Type |
---|---|---|
name | Question Group Name / Title | String |
order | Question Group Order | Integer | undefined |
description | Question Group Description | String | undefined |
question | List of Question | Array[Question] |
translations | List of translations | Array[Translations] | undefined |
Cascading select questions are sets of questions whose options depend on the response to a previous question. Cascade object should be pre-defined on the question definition root object itself.
Props | Description | Type |
---|---|---|
value | Cascade Value | Unique (Integer | String) |
label | Cascade Label | String |
children | Children of current object | Array[Cascade] | undefined |
translations | List of translations | Array[Translations] | undefined |
Example:
{
"name": "Community Culinary Survey 2021",
"translations": [
{
"name": "Komunitas Kuliner Survey 2021",
"language": "id"
}
],
"languages": ["en", "id"],
"question_group": [
{
"name": "Registration",
"order": 1,
"translations": [
{
"name": "Registrasi",
"language": "id"
}
],
"question": [
{
"id": 1,
"name": "Location",
"order": 1,
"type": "cascade",
"option": "administration",
"required": true,
"translations": [
{
"name": "Lokasi",
"language": "id"
}
]
}
]
}
],
"cascade": {
"administration": [
{
"value": 1,
"label": "Jawa Barat",
"children": [
{
"value": 1,
"label": "Garut"
}
]
}
]
}
}
Cascading select also support for a chain API call for the cascade dropdown list.
Props | Description | Type |
---|---|---|
endpoint | Cascade API | String |
initial | Initial Parameter | Integer | String | undefined |
list | Object name of array | res.data?.[list] | res.data | String | undefined |
Example:
"name": "Community Culinary Survey 2021",
"question_group": [{
"name": "Registration",
"order": 1,
"question": [{
"id": 1,
"name": "Location",
"order": 1,
"type": "cascade",
"api": {
"endpoint": "https://tech-consultancy.akvo.org/akvo-flow-web-api/cascade/seap/cascade-296940912-v1.sqlite/",
"initial": 0,
"list": false
},
"required": true
}]
}]
Props | Description | Type |
---|---|---|
id | Cascade Value | Unique (Integer | String) |
name | Cascade Label | String |
API Example : https://tech-consultancy.akvo.org/akvo-flow-web-api/cascade/seap/cascade-296940912-v1.sqlite/0
[
{
"code": "ACEH",
"id": 47,
"name": "ACEH",
"parent": 0
},
{
"code": "BALI",
"id": 128,
"name": "BALI",
"parent": 0
}
]
Entity cascade selection is a dropdown option that requires an API and depends on the selected parent question, by setting the extra
field and API format as follows.
Props | Description | Type |
---|---|---|
type | Set to "entity" (required) | String |
name | Entity name | String |
parentId | Parent entity question id | Number |
Props | Description | Type |
---|---|---|
id | Entity Value | Unique (Integer | String) |
name | Entity Label | String |
[
{
"id": 91,
"name": "School - Bandung 1",
},
{
"id": 92,
"name": "School - Bandung 2",
}
]
Example:
{
"id": 67,
"label": "School cascade",
"name": "school_cascade",
"type": "cascade",
"required": false,
"order": 7,
"api": {
"endpoint": "https://akvo.github.io/akvo-react-form/api/entities/1/"
},
"extra": {
"type": "entity",
"name": "School",
"parentId": 5 // question id: 5 (eg: administration type of question)
}
},
Props | Description | Type |
---|---|---|
id | Question ID | Unique (Integer | String) |
order | Question Order | Integer | undefined |
tooltip | Question Tooltip | String | undefined |
type | Question Type | number | input | text | option | multiple_option | cascade | tree | autofilled | table | image |
option | List of Option (for option type of question ) | Array[Option] | String (cascade object name, only for 'cascade' type) | undefined |
columns | Columns of table (for table type question) question | Array[Columns] | undefined |
dependency | List of Question Dependency | Array[Dependency] | undefined |
rule | Question rule to be validated (Only for 'number' type of question) | {min: Integer, max: Integer} |
meta | Question set to be used as data point name | Boolean | undefined |
required | Set field as required | Boolean | undefined |
requiredSign | Set custom required field symbol/mark before question label. requiredSign content will show if required param set to true |
ReactComponent | String | undefined |
partialRequired | Set a custom required rule for type cascade. Set true to fill without having to select all the cascade level options when the required param is true |
Boolean | undefined |
translations | List of translations | Array[Translations] | undefined |
extra | Extra Component | Array[ExtraComponent] | Object[ExtraEntity] |
addonBefore | Addon before Field (only support for number and input type of question) | ReactComponent | String | undefined |
addonAfter | Addon before Field (only support for number and input type of question) | ReactComponent | String | undefined |
allowOther | Allow other field (support for option and multiple_option type of question) | Boolean | undefined |
allowOtherText | Text Replacement for allow other field (support for option and multiple_option type of question) | String | undefined |
checkStrategy | The way show selected item in box when question type is tree. Default: show checked treeNodes (just show parent treeNode), "children": show only children node | parent | children | undefined |
expandAll | Whether to expand all treeNodes by default. Default: false |
Boolean | undefined |
fn | Function for autofilled type of question | Autofield Function | undefined |
dataApiUrl | Api data that returns pair of object and value for hint | String | undefined |
limit | Set limit / maximum file size in Megabyte (MB) for image type of question | Integer | undefined |
disabled | Define to disabled the question field | Boolean | undefined |
pre | Define the default value only for option and multiple_option type questions based on the source question and its answer |
Object | undefined |
displayOnly | Define the question for display only and do not include it in the payload submission | Boolean | undefined |
Autofield data use Javascript function in 1 line
Props | Description | Type |
---|---|---|
content | Content of the Extra Component | ReactComponent | String |
placement | Placement for the Extra Component | before | after |
translations | List of translations | Array[Translations] | undefined |
Rule should be defined as object, currently we only support min max value for number type of question.
Props | Type |
---|---|
min | Integer | undefined |
max | Integer | undefined |
allowDecimal | Boolean | undefined |
Example:
{
"id": 1,
"name": "Weight",
"order": 1,
"type": "number"
"required": true,
"tooltip": {"text": "Information Text"},
"rule": {"min": 5,"max": 10},
"addonAfter": "Kilograms",
"meta": true,
"translations": [{
"name": "Berat Badan",
"language": "id"
}
],
"extra": [{
"placement": "before",
"content": "Extra Component before the question",
"translations": [{
"content": "Komponen Tambahan sebelum pertanyaan ini",
"language": "id"
}]
}]
}
If question has dependency, question will be hidden by default. The question will only shows when dependency question values matches with the dependency rules.
Props | Description | Type |
---|---|---|
id | Question ID | Integer | String |
options | List of dependency options to be validated, for 'option' type of the dependency question | Array[String] | undefined |
min | Minimum dependency value to be validate, for 'number' type of the dependency question | Array[String] | undefined |
max | Maximum dependency value to be validate, for 'number' type of the dependency question | Array[String] | undefined |
equal | Dependent answer is equal to | Integer | String | undefined |
notEqual | Dependent answer is not blank and not equal to | Integer | String | undefined |
Example:
{
"id": 11,
"name": "Where do you usually order Rendang from ?",
"dependency": [
{
"id": 9,
"options": ["Yes"]
},
{
"id": 10,
"min": 8
}
],
"order": 5,
"type": "option",
"option": [
{
"name": "Pagi Sore",
"order": 1
},
{
"name": "Any Rendang Restaurant",
"order": 2,
"translations": [
{
"name": "Restoran Rendang Manapun",
"language": "id"
}
]
}
],
"required": true,
"translations": [
{
"name": "Dimana anda biasanya membeli Rendang?",
"language": "id"
}
]
}
Option is valid only for option
type of question
Props | Description | Type |
---|---|---|
name | Option Name / Label | String |
order | Question Group Order | Integer | undefined |
translations | List of translations | Array[Translations] | undefined |
color | Color of the option | String | undefined |
Columns is valid only for table
type of question
Props | Description | Type |
---|---|---|
name | Column / Question object key | String |
type | Column / Question Type | number | input | text | option |
label | Column / Question Label | String |
option | Option value | Array[Option] | undefined |
Props | Description | Type |
---|---|---|
question | Question ID | Unique (Integer | String) |
value | Value of the Question | String | Integer | Object{lat,lng} | Array[Integer | String] | Date Format |
repeatIndex | Repeat Index in Repeated Question Group. Default: 0 | Integer | undefined |
Example: Initial Value Example
Props | Description | Type |
---|---|---|
fnString | String of function | String |
multiline | Wether function is multiline or not | Bool | undefined |
fnColor | Color for the answer field | Object |
Example for fnString:
function () { return #1 / #2 }
OR
() => { return #1.includes("Test") ? #2 / #3 : 0 }
Example for fnColor:
{
"Answer A": "#CCFFC4"
"Answer B": "#FECDCD"
}
Prefix #N is use to indicate the value of question id N. Note that we don't use javascript eval to overcome the security issue, the function will be sanitized before it's executed.
Props | Description | Type |
---|---|---|
formId | Required | Integer |
name | Name for datapoint | String | undefined |
buttonText | Custom text for save button | String | undefined |
Auto save object require formId
when it's enabled. This will filter list of saved data for particular formId
. To show the list of saved datapoint we can use dataStore.
Example:
import React, { useState, useEffect } from 'react'
import { dataStore } from 'akvo-react-form'
const DataList = () => {
const [datapoint, setDatapoint] = useState([])
useEffect(() => {
const listData = dataStore.list(formId)
listData.then((x) => {
setDatapoint(x)
})
}, [])
return (
<table>{dataPoints.map((x, xi) => (
<tr key={xi}>
<td>
{xi + 1}. {x.name}
</td>
<td>
<button onClick={x.load}>
Load
</button>
<button onClick={x.remove}>
Delete
</button>
</Space>
</td>
</tr>
))}
</table>)
}
Props | Description | Type |
---|---|---|
source_question | Required | String |
source_answer | Name for datapoint | String |
default_value | Custom text for save button | Array |
Here the prefilled question is represented as an object
...
"pre": {
"<source_question>": {
"<source_answer>": ["<default_value>"]
}
}
...
Please check the Form Definition Example which contains all the current features of akvo-react-form.
AGPL-3.0 © akvo