How do I handle different sets of Options for a single-choice field?

Today, I need to do an inventory of a customer’s warehouse. The Data Model has a single-choice field with 4 options:
North, East, South, West

Next week another project comes up, and for that project, the options should only show:
East, NorthEast

How can I do this without physically modifying code and re-deploying?
Can I get dynamic choices somehow? … populated with values from a user-maintained table in the database and not hardcoded in the app?

I’d rather not create a 2nd field with the Options. Although I could, and easily condition the View to show the “new” field instead of the other. But that can’t go on forever as each project just needs some custom Choices for the same data model field.

Thx

Hi @jaymer

If you want the options bound to a DB field to be dynamic, then the most flexible approach is to store the options in a separate table, render the objects in the table to the user and store the selection as either a relationship or as a free text field on the object instead of a single-choice.

The single-choice data type is by definition a predefined list of options, so you cannot dynamically include additional options at runtime.

Another option which may be applicable in some cases is to define the schema with all the possible options for the single-choice field, e.g.

<field name="selection" label="Selection" type="single-choice" >
  <option key="option1">Option 1</option>
  <option key="option2">Option 2</option>
  <option key="option3">Option 3</option>
  <option key="option4">Option 4</option>
</field>

And then in the UI you don’t use a single-choice-radio or single-choice-dropdown which has to be bound directly to the the data type, but instead provide the user with a dynamic list of options (which has to be a subset of all the options) in something like a actionSheet or optionList or even a table (using temporary objects)
e.g.

async function presentDynamicSelection() {
  let dynamicOptions = [{key: "option1", label: "Option 1"}, {key:"option3", label: "Option 3"}];
  let labels = dynamicOptions.map(function(el) {return el.label});
  let result = await optionList(labels);
  if (result === -1) {
    return;
  }

  let selection = dynamicOptions[result];
  // set the single-choice data type field on the DB object = selection the user made 
  dbObject.single_choice_field = selection.key;
}

I hope that makes sense

Thx T - yes, this would be ideal - beyond the basic ‘hardcoded’ info we need to collect during an Inventory, would be nice to, on a per customer basis, define a list of additional Fields, -AND- specify choices, if applicable.

example of hardcoded choices:

        <field name="q1" label="Condition" type="single-choice" >
           <option key="Fair">Fair</option>
           <option key="Poor">Poor</option>
           <option key="Damaged">Damaged</option>
        </field>
        <field name="q2" label="Power Test" type="single-choice" >
           <option key="Yes">Yes</option>
           <option key="No">No</option>
           <option key="N/A">N/A</option>
        </field>

I have user-defined Questionnaires/Checklists setup in our desktop app and would like to mimic that behavior in JA.


I understand I’ve got a ‘separate table’ that defines data to collect:

Tenant integer
Project integer
Sort integer
theQuestion text
theAnswerDataType text (values: text, integer, choices (North, East, South, West)

So apparently I need to use the Dynamic options strategy, and I need multiple rows (1 for each additional dynamic field). I’d be happy to define this structure at init() time, as for now it’s fine to be a per-project item. All users today working on Project-nnn will be shown the same additional field structure. Some fields remain empty and the ones that are filled in I will handle at SAVE time. Still sound doable ?
I have no idea what to expect in the UI having not see this in JA yet. I envision its another element on my entry form below the existing database fields and before my Save/Cancel buttons at the bottom.

Yes. Typically for dynamic checklists / questions you would have each question be a row of “master data” in a table and then you render those questions in the UI in for example an object table. Possible answers can either be separate objects, or in some cases a comma separated string on the master question object.

JourneyApps does not have a predefined UI/UX for this, so whatever works for you, but honestly an object table makes most sense.

As for how you store the actual user provided answers, that is your decision again. You could have a predefined number of “extra empty fields” on an existing model, the problem is how many fields do you need to hardcode to store potential answers on. So another option is to keep the user provided answers as related (child) DB objects to the main ‘inspection’ object (or whatever your main transactional object is)

Hi @jaymer

+1 to @tielman’s recommendation for storing dynamic options in the DB.

To build the UI for a dynamic dropdown with a similar look to the desktop app, I would suggest the following UI components:

  1. A single cell object-table with edit-typeahead presenting the dynamic list of options
  2. A grid component to lay out the form

Example

In this example we will use this schema model to represent the dropdown options:

<!-- schema.xml -->
<model name="dropdown_option" label="Dropdown Option">
  <field name="key" label="key" type="text" />
  <field name="label" label="label" type="text" />
  <display>{label}</display>
</model>

On the backend, I created three options that represent a theoretical “condition”.
Note: We added an EMPTY option with no label, that is used to initialize the dropdown.

Next, the view.xml

  1. We use two view variables to store
    a. the array of options, queried from the DB
    b. the selected option, initialized with the EMPTY option
  2. A object-table with
    a. a single column with edit-typeahead bound to the selection object’s key and display set to the object’s label
    b. controls set to none
<!-- view.xml -->
<var name="options" type="array:dropdown_option" />
<var name="selection" type="array:dropdown_option" />

<object-table query="selection" controls="none" >
  <column display="{label}">
    <edit-typeahead value="$object.key" 
                    on-search="$:onSearch(searchValue)" 
                    on-change="$:onChange(newValue)" />
  </column>
</object-table>
  1. on-search provides the the options presented to the user
  2. on-change updates the selection variable to the selected option or EMPTY option
// view.js
function init() {
    view.options = DB.dropdown_option.where('key != ?', 'EMPTY').toArray();
    view.selection = DB.dropdown_option.where('key = ?', 'EMPTY').toArray();
}

function onSearch(searchValue) {
    return view.options;
}

function onChange(newValue) {
    var newSelectionObj = DB.dropdown_option.where('key = ?', newValue != null ? newValue : 'EMPTY').toArray();
    view.selection = newSelectionObj;
}
Resulting UI

The dropdown field is empty and when the user selects it, they are presented with a list of options (also a search box and close/clear icon).

Layout using grid

We use a grid component to lay out our form.

  1. Set the grid to span 2 columns and add two cell nodes to it (Note: we set the column-min-width to a reasonable value 30 px, the default is 250 px)
  2. Add an info component in the first cell to serve as the label (Note: we found that an empty <info/> before it, helped with alignment)
  3. Add our dynamic dropdown object-table in the second cell node
<!-- view.xml -->
<grid column-count="2" column-min-width="30">
  <cell>
    <!-- empty <info/> serves as padding -->
    <info/>
    <info value="Condition"  />
  </cell>
  <cell>
    <object-table query="selection" controls="none" >
      <column display="{label}">
        <edit-typeahead value="$object.key" on-search="$:onSearch(searchValue)" on-change="$:onChange(newValue)" />
      </column>
    </object-table>
  </cell>
</grid>
Resulting UI on a mobile screen

We hope this basic example helps you. This approach can be extended and applied to more complex forms that may include different types of dynamic dropdown options.