Related
With this script I can sum all the values set in the cells of a formIO table element
value = Object.keys(data).reduce((acc, key) => {
if (key === "totalTableData") return acc;
if (data[key]) {
return acc + data[key];
}
return acc;
}, 0);
The problem is when I sum 1 + 1, for example, the result I get is 32 (1+1=32). I don't know why this happens even after copying the solution example in this question (Get column data sum from FormIO table element). Also, how could be the script to sum specific fields and not the whole table?
JSON:
{
"label": "Table",
"cellAlignment": "left",
"key": "table",
"type": "table",
"numRows": 4,
"input": false,
"tableView": false,
"rows": [
[
{
"components": []
},
{
"components": [
{
"label": "HTML",
"attrs": [
{
"attr": "",
"value": ""
}
],
"content": "Column 1",
"refreshOnChange": false,
"key": "html111",
"type": "htmlelement",
"input": false,
"tableView": false
}
]
},
{
"components": [
{
"label": "HTML",
"attrs": [
{
"attr": "",
"value": ""
}
],
"content": "Column 2",
"refreshOnChange": false,
"key": "html121",
"type": "htmlelement",
"input": false,
"tableView": false
}
]
},
{
"components": [
{
"label": "HTML",
"attrs": [
{
"attr": "",
"value": ""
}
],
"content": "Column total",
"refreshOnChange": false,
"key": "html124",
"type": "htmlelement",
"input": false,
"tableView": false
}
]
}
],
[
{
"components": [
{
"label": "HTML",
"attrs": [
{
"attr": "",
"value": ""
}
],
"content": "Row 1",
"refreshOnChange": false,
"key": "html125",
"type": "htmlelement",
"input": false,
"tableView": false
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"tableView": false,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "column1row1",
"type": "number",
"input": true
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"tableView": false,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "column2row1",
"type": "number",
"input": true
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"disabled": true,
"tableView": false,
"defaultValue": 0,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "totalRow1",
"type": "number",
"input": true
}
]
}
],
[
{
"components": [
{
"label": "HTML",
"attrs": [
{
"attr": "",
"value": ""
}
],
"content": "Row 2",
"refreshOnChange": false,
"key": "html126",
"type": "htmlelement",
"input": false,
"tableView": false
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"tableView": false,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "column1row2",
"type": "number",
"input": true
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"tableView": false,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "column2row2",
"type": "number",
"input": true
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"disabled": true,
"tableView": false,
"defaultValue": 0,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "totalRow2",
"type": "number",
"input": true
}
]
}
],
[
{
"components": [
{
"label": "HTML",
"attrs": [
{
"attr": "",
"value": ""
}
],
"content": "Row total",
"refreshOnChange": false,
"key": "html127",
"type": "htmlelement",
"input": false,
"tableView": false
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"disabled": true,
"tableView": false,
"defaultValue": 0,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "totalColumn1",
"type": "number",
"input": true
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"disabled": true,
"tableView": false,
"defaultValue": 0,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "totalColumn2",
"type": "number",
"input": true
}
]
},
{
"components": [
{
"label": "Number",
"hideLabel": true,
"mask": false,
"disabled": true,
"tableView": false,
"defaultValue": 0,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"truncateMultipleSpaces": false,
"key": "totalTableData",
"type": "number",
"decimalLimit": 0,
"input": true
}
]
}
]
],
"numCols": 4
}
The answer has disappeared... This is what they answered me:
Set this script in Calculated Value (element's data tab):
const fieldsToSum = ['field1', 'field2', 'field3'];
value = fieldsToSum.reduce((acc, key) => {
if (data[key]) {
return acc + Number(data[key]);
} return acc;
}, 0);
I know we can create custom components, but I am curious if there is a way to group a few together into either a Container or a Panel?
For instance, why make everyone create the same 4 fields in a pleasing layout over and over to create a panel for getting a user's Address? Seems like it would be nice to be able to define some buttons on the left that says "Address Form Block", or "Contact Info Block" which then inserts everything in a group.
I am still pretty new to using Formio.JS, so I don't know if this is possible, but I would hope I am not the first to want something like this.
Example form:
Sample Code:
Formio.builder(document.getElementById('builder'), {
"display": "form",
"components": [
{
"title": "Demographics",
"collapsible": false,
"key": "demographics",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [
{
"label": "Columns",
"columns": [
{
"components": [
{
"label": "Last Name",
"tableView": true,
"key": "lastName1",
"type": "textfield",
"input": true
}
],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [
{
"label": "First Name",
"tableView": true,
"key": "lastName",
"type": "textfield",
"input": true
}
],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [
{
"label": "Middle Initial",
"tableView": true,
"key": "middleInitial",
"type": "textfield",
"input": true
}
],
"size": "md",
"width": 2,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 2
}
],
"key": "columns",
"type": "columns",
"input": false,
"tableView": false
},
{
"label": "Birthdate",
"hideInputLabels": false,
"inputsLabelPosition": "top",
"useLocaleSettings": false,
"tableView": false,
"fields": {
"day": {
"hide": false
},
"month": {
"hide": false
},
"year": {
"hide": false
}
},
"key": "birthdate",
"type": "day",
"input": true,
"defaultValue": "00/00/0000"
},
{
"label": "Phone Number",
"tableView": true,
"key": "phoneNumber",
"type": "textfield",
"input": true
}]},
{
"title": "Address",
"collapsible": false,
"key": "address",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [
{
"label": "Street Address",
"tableView": true,
"key": "streetAddress",
"type": "textfield",
"input": true
},
{
"label": "Columns",
"columns": [
{
"components": [
{
"label": "City",
"tableView": true,
"key": "city",
"type": "textfield",
"input": true
}
],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 6
},
{
"components": [
{
"label": "State",
"tableView": true,
"key": "state",
"type": "textfield",
"input": true
}
],
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 3
},
{
"components": [
{
"label": "Zipcode",
"tableView": true,
"key": "zipcode",
"type": "textfield",
"input": true
}
],
"size": "md",
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 3
}
],
"key": "columns1",
"type": "columns",
"input": false,
"tableView": false
}
]
},
{
"label": "Signature",
"tableView": false,
"key": "signature",
"type": "signature",
"input": true
},
{
"type": "button",
"label": "Submit",
"key": "submit",
"disableOnInvalid": true,
"input": true,
"tableView": false
}
]
});
You can use a custom component option like.
const AddressTool = {
"title": "Address",
"collapsible": false,
"key": "address",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [{
"label": "Street Address",
"tableView": true,
"key": "streetAddress",
"type": "textfield",
"input": true
},
{
"label": "Columns",
"columns": [{
"components": [{
"label": "City",
"tableView": true,
"key": "city",
"type": "textfield",
"input": true
}],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 6
},
{
"components": [{
"label": "State",
"tableView": true,
"key": "state",
"type": "textfield",
"input": true
}],
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 3
},
{
"components": [{
"label": "Zipcode",
"tableView": true,
"key": "zipcode",
"type": "textfield",
"input": true
}],
"size": "md",
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 3
}
],
"key": "columns1",
"type": "columns",
"input": false,
"tableView": false
}
]
};
const DemographicsTool = {
"title": "Demographics",
"collapsible": false,
"key": "demographics",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [{
"label": "Columns",
"columns": [{
"components": [{
"label": "Last Name",
"tableView": true,
"key": "lastName1",
"type": "textfield",
"input": true
}],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [{
"label": "First Name",
"tableView": true,
"key": "lastName",
"type": "textfield",
"input": true
}],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [{
"label": "Middle Initial",
"tableView": true,
"key": "middleInitial",
"type": "textfield",
"input": true
}],
"size": "md",
"width": 2,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 2
}
],
"key": "columns",
"type": "columns",
"input": false,
"tableView": false
},
{
"label": "Birthdate",
"hideInputLabels": false,
"inputsLabelPosition": "top",
"useLocaleSettings": false,
"tableView": false,
"fields": {
"day": {
"hide": false
},
"month": {
"hide": false
},
"year": {
"hide": false
}
},
"key": "birthdate",
"type": "day",
"input": true,
"defaultValue": "00/00/0000"
},
{
"label": "Phone Number",
"tableView": true,
"key": "phoneNumber",
"type": "textfield",
"input": true
}
]
};
let formioOptions = {
noDefaultSubmitButton: false,
builder: {
basic: false,
advanced: false,
data: false,
premium: false,
layout: false,
custom: {
title: 'Custom Component Set',
weight: 10,
components: {
Demographics: {
title: 'Demographics',
key: 'demographics',
icon: 'terminal',
schema: DemographicsTool
},
Address: {
title: 'Address',
key: 'address',
icon: 'terminal',
schema: AddressTool
}
}
}
}
};
and then call the builder
Formio.builder(document.getElementById('builder'), {
"components": [{
"title": "Test1",
"input": false,
"key": "panel1",
"tableView": false,
"label": "Test 1",
"type": "panel",
"collapsed": false,
"collapsible": true,
"components": [AddressTool, DemographicsTool]
}]
}, formioOptions);
Requirement:
I have to iterate multiple objects, it contains properties. so I have to iterate each object and check properties values(the key name is mapping) matching with the first object key.
for example:
Node1, Node2, Node3
JSON:
{
"Node1": {
"type": "object",
"properties": {
"x": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"y": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
}
},
"defChecked": true
},
"Node2": {
"type": "object",
"properties": {
"body": {
"type": "string",
"mapping": "Node1.inputs.x",
"checked": false,
"defChecked": true
},
"subject": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"toemail": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"body_placeholders": {
"type": "string",
"mapping": "",
"checked": true,
"defChecked": true
},
"subject_placeholders": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
}
},
"defChecked": true
},
"Node3": {
"type": "object",
"properties": {
"email": {
"type": "text",
"description": null,
"default": null,
"mapping": "Node2.inputs.toemail",
"isSelected": true,
"defChecked": true
},
"firstname": {
"type": "text",
"description": null,
"default": null,
"mapping": "",
"isSelected": true,
"defChecked": true
},
"lastname": {
"type": "text",
"description": null,
"default": null,
"mapping": "Node2.inputs.body_placeholders",
"isSelected": true,
"defChecked": true
},
"id": {
"type": "uuid",
"description": null,
"default": null,
"mapping": "",
"isSelected": true,
"defChecked": true
}
},
"defChecked": true
}
}
Output:
{
"Node1": {
"type": "object",
"properties": {
"y": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
}
},
"defChecked": true
},
"Node2": {
"type": "object",
"properties": {
"subject": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"subject_placeholders": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
}
},
"defChecked": true
},
"Node3": {
"type": "object",
"properties": {
"firstname": {
"type": "text",
"description": null,
"default": null,
"mapping": "",
"isSelected": true,
"defChecked": true
},
"id": {
"type": "uuid",
"description": null,
"default": null,
"mapping": "",
"isSelected": true,
"defChecked": true
}
},
"defChecked": true
}
}
for the above JSON - Node1 has a property 'x's and this property mapped or not another object properties(Node2 and Node3). if you see the JSON it x mapped with Node2 like ( Node1.inputs.x)
if it's matches then i have to remove the object Node1.x from the json. similary for others
You could do something like this:
let input = {
"Node1": {
"type": "object",
"properties": {
"x": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"y": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
}
},
"defChecked": true
},
"Node2": {
"type": "object",
"properties": {
"body": {
"type": "string",
"mapping": "Node1.inputs.x",
"checked": false,
"defChecked": true
},
"subject": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"toemail": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
},
"body_placeholders": {
"type": "string",
"mapping": "",
"checked": true,
"defChecked": true
},
"subject_placeholders": {
"type": "string",
"mapping": "",
"checked": false,
"defChecked": true
}
},
"defChecked": true
},
"Node3": {
"type": "object",
"properties": {
"email": {
"type": "text",
"description": null,
"default": null,
"mapping": "Node2.inputs.toemail",
"isSelected": true,
"defChecked": true
},
"firstname": {
"type": "text",
"description": null,
"default": null,
"mapping": "",
"isSelected": true,
"defChecked": true
},
"lastname": {
"type": "text",
"description": null,
"default": null,
"mapping": "Node2.inputs.body_placeholders",
"isSelected": true,
"defChecked": true
},
"id": {
"type": "uuid",
"description": null,
"default": null,
"mapping": "",
"isSelected": true,
"defChecked": true
}
},
"defChecked": true
}
};
for (let i = 1; i < 3; i++) {
Object.entries(input["Node" + (i + 1)].properties).forEach(prop => {
if (prop[1].mapping.includes("Node" + i)) {
let attrToCancel = prop[1].mapping.split('.')[prop[1].mapping.split('.').length -1];
delete input["Node" + i].properties[attrToCancel];
delete input["Node" + (i + 1)].properties[prop[0]];
}
});
}
console.log(input)
Get the Object.entries of input, then intercept the mapping property, read it and cancel the property on previous Node.
if you post your code we can help you more
but for now, if you want to replace values between that array you can make that there's a lot of ways but that depends on much of the data and the way to use it
const updateData = (YourData) => {
for (const [MyNodeName, MyNodeValue] of Object.entries(YourData)) {
if (MyNodeValue.properties) {
for (const [propKey, propValue] of Object.entries(MyNodeValue.properties)) {
if (propValue.mapping && propValue.mapping !== "") {
var updateKeyArr = propValue.mapping.split(".inputs.");
console.log(updateKeyArr)
if (updateKeyArr.length > 0) {
if (YourData[updateKeyArr[0]] &&
YourData[updateKeyArr[0]].properties[updateKeyArr[1]]
) {
YourData[MyNodeName].properties[propKey].mapping = YourData[updateKeyArr[0]].properties[updateKeyArr[1]];
}
}
}
}
}
}
return YourData;
}
console.log(updateData(data))
I am just starting to mess around with form.io and am a bit stuck. I am trying to create some conditionals within each row of a datagrid. E.g. If a radio button equaling "payIn" is selected then another field should appear in that same row. I can do this statically by assigning the array number to the component's JSON but that obviously doesn't solve the problem of this working dynamically (this was just used to test the logic since it always points to the first row). What could I do/use to make this logic work independently in each row within the datagrid?
{
"label": "Pay In Reason",
"widget": "choicesjs",
"hidden": true,
"tableView": true,
"data": {
"values": [
{
"label": "Employee",
"value": "employee"
},
{
"label": "Bank",
"value": "bank"
}
],
"json": "",
"url": "",
"resource": "",
"custom": ""
},
"selectThreshold": 0.3,
"key": "payInReason",
"conditional": {
"show": true,
"when": "dataGrid[0].payInOut",
"eq": "payIn"
},
"type": "select",
"indexeddb": {
"filter": {}
},
"input": true,
"placeholder": "",
"prefix": "",
"customClass": "",
"suffix": "",
"multiple": false,
"defaultValue": null,
"protected": false,
"unique": false,
"persistent": true,
"clearOnHide": true,
"refreshOn": "",
"redrawOn": "",
"modalEdit": false,
"labelPosition": "top",
"description": "",
"errorLabel": "",
"tooltip": "",
"hideLabel": false,
"tabindex": "",
"disabled": false,
"autofocus": false,
"dbIndex": false,
"customDefaultValue": "",
"calculateValue": "",
"calculateServer": false,
"attributes": {},
"validateOn": "change",
"validate": {
"required": false,
"custom": "",
"customPrivate": false,
"strictDateValidation": false,
"multiple": false,
"unique": false
},
"overlay": {
"style": "",
"left": "",
"top": "",
"width": "",
"height": ""
},
"allowCalculateOverride": false,
"encrypted": false,
"showCharCount": false,
"showWordCount": false,
"properties": {},
"allowMultipleMasks": false,
"clearOnRefresh": false,
"limit": 100,
"dataSrc": "values",
"valueProperty": "",
"lazyLoad": true,
"filter": "",
"searchEnabled": true,
"searchField": "",
"minSearch": 0,
"readOnlyValue": false,
"authenticate": false,
"template": "<span>{{ item.label }}</span>",
"selectFields": "",
"searchThreshold": 0.3,
"uniqueOptions": false,
"fuseOptions": {
"include": "score",
"threshold": 0.3
},
"customOptions": {},
"id": "eas39m"
}
Thanks for your help!
Something like this works: show = (row.component === 'value')
I'm trying to make a rule which makes the user select only nodes that belong to a specific tree ; The user mustn't have the possibily to choose multiple nodes from different levels like this :
Do you have any idea how to do so ?
Thanks in advance.
I found the solution if anyone is facing the same issue :
the html part :
<div id="jstree"/>
The javascript part :
$(function() {$("#jstree").on("select_node.jstree", function (evt, data) {
var selectedNode= $('#jstree').jstree(true).get_selected(true);
for(var i = 0, j = selectedNode.length; i < j; i++) {
if(selectedNode.length > 1){
var res = selectedNode[i].parents[selectedNode[i].parents.length-2];
if(data.node.parent != res){
$('#jstree').jstree("deselect_all");
}
}
}
});
$('#jstree').jstree({
'core': {
'data': [{
"id": "1.0",
"text": "Fresh Products",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [{
"id": "2.06.0",
"text": "Ethnic & Specialty",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [{
"id": "2.16.0",
"text": "Ethnic & Specialty",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}, {
"id": "2.17.0",
"text": "Natural & Organic",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}],
"liAttributes": null,
"aAttributes": null
}, {
"id": "2.07.0",
"text": "Natural & Organic",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}, {
"id": "2.08.0",
"text": "Prepared Foods",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}, {
"id": "2.09.0",
"text": "Seafood",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}, {
"id": "2.010.0",
"text": "Seafood",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}],
"liAttributes": null,
"aAttributes": null
}, {
"id": "2.0",
"text": "Frozen Products",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [],
"liAttributes": null,
"aAttributes": null
}, {
"id": "3.0",
"text": "Store Equipment ",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [],
"liAttributes": null,
"aAttributes": null
}, {
"id": "4.0",
"text": "Packaged Grocery",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [],
"liAttributes": null,
"aAttributes": null
}, {
"id": "5.0",
"text": "Retail Technology",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [],
"liAttributes": null,
"aAttributes": null
}, {
"id": "6.0",
"text": "HBC/Non-Foods",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": [{
"id": "2.090.0",
"text": "Seafood",
"icon": "",
"state": {
"opened": false,
"disabled": false,
"selected": false
},
"children": false,
"liAttributes": null,
"aAttributes": null
}],
"liAttributes": null,
"aAttributes": null
}]
},
"search": {
"case_insensitive": true,
"show_only_matches" : true
},
"plugins": ["search"]
});
});