Use Array Functions for Multi-Select Widgets and Looping
Here’s how to use an array function to create a configuration wizard widget that lets the user select multiple values.
Create a Multi-Select Widget
This ArrayType variable creates a wizard widget that lets the user select multiple values.
1"Account_Fields": {
2 "label": "Pick the fields for your dataset",
3 "description": "Multiselect sobjectfield test",
4 "defaultValue": [
5 {
6 "sobjectName": "Account",
7 "fieldName": "Name"
8 }
9 ],
10 "required": false,
11 "variableType": {
12 "type": "ArrayType",
13 "itemsType": {
14 "type": "SobjectFieldType"
15 }
16 },
17 "excludeSelected": true
18 }Results in the following wizard widget:

The ArrayType variable contains an itemsType attribute, which accepts the following:
SObjectTypeSObjectFieldTypeDatasetTypeDatasetDateTypeDatasetDimensionTypeDatasetMeasureTypeStringTypeNumberType
How the Widget Stores Selections
The widget places values in an array. The following show how the widget displays values for the user to select:


Reference Array Items by Index
Items in the array can be referenced by index. Arrays of StringType and NumberType are easy to work with, but arrays of SObjectType and SObjectFieldType contain multiple attributes for each item in the array.
Array of SobjectFieldType:
| Expression | Returns |
|---|---|
${Variables.Account_Fields[0]} | (sobjectName=Account, fieldName=Name, sobjectLabel=Account, fieldLabel=Account Name) |
${Variables.Account_Fields[0].fieldName} | Name |
${Variables.Account_Fields[0].fieldLabel} | Account Name |
fieldName is required for workflows.
Note
Access Array Values in Template Files
The selection values from the widget are stored in the variable. There are two ways to access them in the template files (rules, dashboards, workflow).
Without looping
Use array values from SobjectFieldType by index.
1"fields":[
2 {"name":"${Variables.Account_Fields[0].fieldName}"},
3 {"name":"${Variables.Account_Fields[1].fieldName}"},
4 {"name":"${Variables.Account_Fields[2].fieldName}"},
5 {"name":"${Variables.Account_Fields[3].fieldName}"},
6 {"name":"${Variables.Account_Fields[4].fieldName}"}
7]This example, without looping, would be tricky to use, because there would always need to be at least five items in the array. Any fewer would fail, and anything over five would not be used.
With looping
Use array values from SobjectFieldType with the array:forEach function. In this example, a “set” action loops through the SObjectFieldTypes in the array and adds them to the “fields” attribute in the Extract_Account step of the workflow. Each entry in “fields” needs “name” and “var.fieldName”.
rules.json
1"name": "AddAccountFilesToWorkflow",
2 "appliesTo": [
3 {
4 "type": "workflow",
5 "name": "*"
6 }
7 ],
8 "actions": [
9 {
10 "action": "set",
11 "description": "use selected values for sfdcDigest in workflow",
12 "path": "$.workflowDefinition.Extract_Account.parameters.fields",
13 "value": "${array:forEach(Variables.Account_Fields, '{\"name\": \"${var.fieldName}\"}')}"
14 }
15 ]workflow.json
1"Extract_Account":{
2 "action":"sfdcDigest",
3 "parameters":{
4 "fields":[],
5 "object":"Account"
6 }
7}Result after processing
If Account Name, Account Source, and Industry are selected:
1"Extract_Account":{
2 "action":"sfdcDigest",
3 "parameters":{
4 "fields":[
5 {"name":"Name"},
6 {"name":"AccountSource"},
7 {"name":"Industry"}
8 ]
9 }
10}Combine Arrays
These functions extend the power of array:forEach.
array:union
Uses only unique values, so no values are duplicated.
1"value": "${array:union(array:forEach(Variables.Account_Fields, '{\"name\": \"${var.fieldName}\"}'),array:forEach(Variables.Account_Fields2, '{\"name\": \"${var.fieldName}\"}'))}"array:concat
Two new arrays:
1"value": "${array:concat(array:forEach(Variables.Account_Fields, '{\"name\": \"${var.fieldName}\"}'),array:forEach(Variables.Account_Fields2, '{\"name\": \"${var.fieldName}\"}'))}"One new array with an existing array:
1"value":"${array:concat(Rules.CurrentNode, array:forEach(Variables.Account_Fields, '{\"name\":\"${var.fieldName}\"}'))}"Which actions support array functions
You can use array functions in the “set” and “put” actions, but not in the “add” or “delete” actions.
In “add”, the value is being added to an existing array, so the result is an array inside an array, which is not well-formed JSON. To add more array values to an existing array, use “set” with the array:concat function. For “delete”, value is not used.
Note
Use StringType and NumberType Arrays
You can use array values from StringType the same way as from NumberType.
variables.json
1"Account_Fields_String": {
2 "label": "Pick string fields for your dataset",
3 "description": "Multiselect string test",
4 "defaultValue": ["Name"],
5 "required": true,
6 "variableType": {
7 "type": "ArrayType",
8 "itemsType": {
9 "type": "StringType",
10 "enums": ["Name", "AccountSource", "Industry", "Type"]
11 }
12 }
13 }rules.json
1"value": "${array:forEach(Variables.Account_Fields_String, '{\"name\": \"${var}\"}')}"Replace Multiple Yes/No Widgets
You can also use the multiselect widget to replace multiple StringType widgets with “Yes” and “No” selections. This example uses three StringType widgets on one wizard page:

You can replace this with a single ArrayType widget:

The JSON for this variable is:
1"SelectedFeatures": {
2 "label": "Please select the features you would like to enable?",
3 "description": "The selected features for this app",
4 "defaultValue": [
5 "Apex Execution"
6 ],
7 "variableType": {
8 "type": "ArrayType",
9 "itemsType" : {
10 "type" : "StringType",
11 "enums" : [
12 "Apex Execution",
13 "API",
14 "Content Transfer",
15 "Dashboard",
16 "Login",
17 "Report Export",
18 "Rest API",
19 "Setup Audit Trail",
20 "UI Tracking",
21 "URI",
22 "Visualforce Request",
23 "Wave Change",
24 "Wave Interaction",
25 "Wave Performance"
26 ]
27 }
28 },
29 "required": true
30 }You can use the selected values to set constants in rules.json, which you can then reference in conditionals for actions or in template-info.json.
1"constants" : [
2 {"name":"hasApexExecution", "value": "${array:contains(Variables.SelectedFeatures, 'Apex Execution')}"},
3 {"name":"hasAPI", "value": "${array:contains(Variables.SelectedFeatures, 'API')}"},
4 {"name":"hasContentTransfer", "value": "${array:contains(Variables.SelectedFeatures, 'Content Transfer')}"},
5 {"name":"hasDashboard", "value": "${array:contains(Variables.SelectedFeatures, 'Dashboard')}"},
6 {"name":"hasLogin", "value": "${array:contains(Variables.SelectedFeatures, 'Login')}"},
7 {"name":"hasReport", "value": "${array:contains(Variables.SelectedFeatures, 'Report Export')}"},
8 {"name":"hasRestApi", "value": "${array:contains(Variables.SelectedFeatures, 'Rest API')}"}
9]