Add Multiple Text Components Dynamically

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

Goal

Show a dynamic number of text components to simulate a multi-text input box. A new text box is shown as soon as the user starts typing into the last input box.

The main expression uses two supporting rules, so let's create them first.

  • ucDynamicFieldsUpdateArray: Updates the guest array at the specified index, and appends a null item at the end of the array. If the last item in the array is already null, no new item is added.
  • ucDynamicFieldEach: Returns a text field populated with the guest value at the given index.

Create expression rule ucDynamicFieldsUpdateArray with the following rule inputs:

  • index (Number Integer)
  • guests (Text Array)
  • newValue (Text)

Enter the following definition for the rule:

=with(
  local!newGuestList: updatearray(ri!guests, ri!index, ri!newValue),
  if(
    isnull(local!newGuestList[count(local!newGuestList)]),
    local!newGuestList,
    append(local!newGuestList, "")
  )
)

Create expression rule ucDynamicFieldEach with the following rule inputs:

  • index (Number Integer)
  • guests (Text Array)

Enter the following definition for the rule:

=a!textField(
  label: if(ri!index=1, "Guest Names", ""),
  value: ri!guests[ri!index],
  saveInto: a!save(ri!guests, rule!ucDynamicFieldsUpdateArray(ri!index, ri!guests, save!value)),
  refreshAfter: "KEYPRESS"
)

Now that we've created the two supporting rules, let's move on to the main expression.

Expression

=load(
  local!guests: {""},
  a!formLayout(
    label: "SAIL Example: Add Text Components Dynamically",
    firstColumnContents: {
      /* The guests array is passed to the rule directly, creating a partial function */
      a!applyComponents(
        function: rule!ucDynamicFieldEach(index: _, guests: local!guests),
        array: 1+enumerate(count(local!guests))
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: a!buttonWidgetSubmit(
        label: "Submit"
      )
    )
  )
)

Test it out

  1. Type into the text field and notice that an empty one is appended.

Offline

Since components cannot be added dynamically when offline, you should include multiple text fields initially in case they are needed. To support this use case for offline, we will create a different expression with a different supporting rule.

Create expression rule ucTextFieldEach with the following rule inputs:

  • index (Number Integer)
  • guests (Text Array)

Enter the following definition for the rule:

=a!textField(
  label: if(ri!index=1, "Guest Names", ""),
  labelPosition: if(ri!index=1, "ABOVE", "COLLAPSED"),
  value: ri!guests[ri!index],
  saveInto: ri!guests[ri!index]
)

Now create your main interface with the following definition:

=load(
  local!guests: repeat(5, ""),
  /* Replace 5 with the maximum number of text fields that you expect will be needed */
  a!formLayout(
    label: "SAIL Example: Add Text Components Dynamically",
    firstColumnContents: {
      /* The guests array is passed to the rule directly, creating a partial function */
      a!applyComponents(
        function: rule!ucTextFieldEach(guests: local!guests, index: _),
        array: 1+enumerate(count(local!guests))
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: a!buttonWidgetSubmit(
        label: "Submit",
        saveInto: a!save(local!guests, reject(fn!isnull, local!guests))
        /* This will remove any null values from the array upon submission */
      )
    )
  )
)

Test it out

  1. There are now 5 text fields available to the user immediately.
  2. Enter values in some of the text fields but leave others blank and submit the form. Notice that null values are removed from the array and only non-null values are saved.

To write your data to process

  1. Save your interface as sailRecipe
  2. Create interface input: guests (Text Array)
  3. Remove the load() function
  4. Delete local variable: local!guests
  5. In your expression, replace:
    • local!guests with ri!guests
  6. 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
  7. 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 variabl
FEEDBACK