Configure a Dropdown with an Extra Option for Other

See SAIL Recipes for information about how to work through recipes and adapt them to your application.

Goal

Show a dropdown that has an "Other" option at the end of the list of choices. If the user selects "Other", show a required text field.

We describe two approaches: the first populates a dropdown list with parallel arrays of data while the second populates the list with an array of CDT values. Each approach has an additional expression that is suited for offline use.

Expression 1

This expression shows a dropdown whose option labels and values come from parallel arrays.

=load(
  local!choiceLabels: {"Fruits", "Vegetables", "Other"},
  local!choiceValues: {10, 20, -1},
  local!selectedFoodType: tointeger(null),
  local!other,
  a!formLayout(
    label: "SAIL Example: Dropdown from parallel arrays with Other option",
    firstColumnContents: {
      a!dropdownField(
        label: "Food Type",
        instructions: "Value saved: " & local!selectedFoodType,
        choiceLabels: local!choiceLabels,
        placeholderLabel: "--- Select Food Type ---",
        choiceValues: local!choiceValues,
        value: local!selectedFoodType,
        saveInto: local!selectedFoodType
      ),
      if(
        local!selectedFoodType = -1,
        a!textField(
          label: "Other",
          value: local!other,
          saveInto: local!other,
          required: true
        ),
        {}
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: a!buttonWidgetSubmit(
        label: "Submit",
        value: null,
        saveInto: if(
          or(isnull(local!selectedFoodType), local!selectedFoodType = -1),
          /* Clear value if user selected Other `*/
          local!selectedFoodType,
          /*` Clear value if user selected an available option */
          local!other
        )
      )
    )
  )
)

Expression 1 (Offline)

This expression shows how to modify the above expression for offline use.

=load(
  local!choiceLabels: {"Fruits", "Vegetables", "Other"},
  local!choiceValues: {10, 20, -1},
  local!selectedFoodType: tointeger(null),
  local!other,
  a!formLayout(
    label: "SAIL Example: Dropdown from parallel arrays with Other option",
    firstColumnContents: {
      a!dropdownField(
        label: "Food Type",
        instructions: "Value saved: " & local!selectedFoodType,
        choiceLabels: local!choiceLabels,
        placeholderLabel: "--- Select Food Type ---",
        choiceValues: local!choiceValues,
        value: local!selectedFoodType,
        saveInto: local!selectedFoodType
      ),
      a!textField(
        label: "Other",
        instructions: "Required if Food Type is Other",
        value: local!other,
        saveInto: local!other,
        required: local!selectedFoodType = -1
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: a!buttonWidgetSubmit(
        label: "Submit",
        value: null,
        saveInto: if(
          or(isnull(local!selectedFoodType), local!selectedFoodType = -1),
          /* Clear value if user selected Other `*/
          local!selectedFoodType,
          /*` Clear value if user selected an available option */
          local!other
        )
      )
    )
  )
)

Expression 2

This expression shows a dropdown whose options come from a CDT array, to which we append an extra entry for the "Other" option.

=load(
  local!foodTypes: {
    {id: 1, name: "Fruits"},
    {id: 2, name: "Vegetables"}
  },
  local!choices: append(local!foodTypes, {id: -1, name: "Other"}),
  local!selectedFoodType,
  local!other,
  a!formLayout(
    label: "SAIL Example: Dropdown from CDT array with Other option",
    firstColumnContents: {
      a!dropdownField(
        label: "Food Type",
        instructions: "Value saved: " & local!selectedFoodType,
        choiceLabels: index(local!choices, "name", {}),
        placeholderLabel: "--- Select Food Type ---",
        choiceValues: local!choices,
        value: local!selectedFoodType,
        saveInto: local!selectedFoodType
      ),
      if(
        and(
          not(isnull(local!selectedFoodType)),
          tointeger(local!selectedFoodType.id) = -1
        ),
        a!textField(
          label: "Other",
          value: local!other,
          saveInto: local!other,
          required: true
        ),
        {}
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: a!buttonWidgetSubmit(
        label: "Submit",
        value: null,
        saveInto: if(
          or(
            isnull(local!selectedFoodType), 
            tointeger(local!selectedFoodType.id) = -1
          ),
          /* Clear value if user selected Other `*/
          local!selectedFoodType,
          /*` Clear value if user selected an available option */
          local!other
        )
      )
    )
  )
)

Expression 2 (Offline)

This expression shows how to modify the above expression for offline use.

=load(
  local!foodTypes: {
    {id: 1, name: "Fruits"},
    {id: 2, name: "Vegetables"}
  },
  local!choices: append(local!foodTypes, {id: -1, name: "Other"}),
  local!selectedFoodType,
  local!other,
  a!formLayout(
    label: "SAIL Example: Dropdown from CDT array with Other option",
    firstColumnContents: {
      a!dropdownField(
        label: "Food Type",
        instructions: "Value saved: " & local!selectedFoodType,
        choiceLabels: index(local!choices, "name", {}),
        placeholderLabel: "--- Select Food Type ---",
        choiceValues: local!choices,
        value: local!selectedFoodType,
        saveInto: local!selectedFoodType
      ),
      a!textField(
        label: "Other",
        value: local!other,
        saveInto: local!other,
        required: and(
          not(isnull(local!selectedFoodType)),
          tointeger(local!selectedFoodType.id) = -1
        )
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: a!buttonWidgetSubmit(
        label: "Submit",
        value: null,
        saveInto: if(
          or(
            isnull(local!selectedFoodType), 
            tointeger(local!selectedFoodType.id) = -1
          ),
          /* Clear value if user selected Other `*/
          local!selectedFoodType,
          /*` Clear value if user selected an available option */
          local!other
        )
      )
    )
  )
)

Test it out

  1. Select "Other" in the dropdown, enter a value and click on the Submit button.
  2. Select "Fruits" in the dropdown and click on the Submit button.
  3. Select "Other" in the dropdown, don't enter any value, and click on the Submit button. Notice that the form can't be submitted unless the user enters a value in the text field.
  4. When the text field is blank, select "Fruits" from the dropdown to hide the text field. You can successfully submit even though the local!other variable is null.

To write your data to process

Expression 1

  1. Save your interface as sailRecipe
  2. Create interface inputs: selectedFoodType (Number Integer), other (Text)
  3. Delete local variables: local!selectedFoodType, local!other
  4. In your expression, replace:
    • local!selectedFoodType with ri!selectedFoodType
    • local!other with ri!other
  5. In your process model, on the process start form or forms tab of an activity, enter the name of your interface in the search box and select it
  6. Click Yes when the process modeler asks, "Do you want to import the interface inputs?"
    • On a task form, this will create create node inputs
    • On a start form, this will create parameterized process variables

Expression 2

Any Type is not a supported data type in the process modeler. Before creating the process model, you will need to create a CDT that matches the data structure of local!cdt.
  1. Save your interface as sailRecipe
  2. Create your CDT with at least 2 fields, one for the selected id, and one for the label to show in the dropdown.
  3. Create interface inputs: cdt (CDT (array)), other (Text)
  4. Delete local variables: local!selectedFoodType, local!other
  5. In your expression, replace:
    • local!selectedFoodType with ri!cdt
    • tointeger(local!selectedFoodType.id) with ri!cdt.id
    • local!other with ri!other
    • The value of local!foodTypes with a CDT array
  6. If the id field in your CDT is not an integer, also replace -1 with a value of the same type as your id field.
  7. In your process model, on the process start form or forms tab of an activity, enter the name of your interface in the search box and select it
  8. Click Yes when the process modeler asks, "Do you want to import the interface inputs?"
    • On a task form, this will create create node inputs
    • On a start form, this will create parameterized process variables

Notable implementation details

  • Notice that we cleared out the opposite variable upon submission so that only one variable gets updated. That is, if the user filled out the "Other" field and then switched the dropdown back to an available option, local!other would be set to null on submission of the form.
  • When you configure your dropdown, replace the value of local!foodTypes with a!queryEntity() or queryrecord() to return your array of options.
FEEDBACK