formio.js icon indicating copy to clipboard operation
formio.js copied to clipboard

[Feature] Option for 'Select' component to auto-select the one-and-only option

Open franke-mc opened this issue 6 years ago • 7 comments

This issue is about adding a new option to a select component with "dataSrc": "resource".

Suppose the dynamic select filtering example was working as expected. Wouldn't it be useful to be able to specify that the Model should auto-select if there was only a single Model available for a selected Make? (And then, maybe the select input could also be disabled, since no other option exists anyway?)

Should this be added to formio.js? Is it possible to implement this already using existing mechanisms without modifying the library? I tried to put some code like

const options = instance.selectOptions;
if (_.isEmpty(input) && options.length === 1) {
  const newVal = options[0].value;
  instance.setValue(newVal);
  _.set(data, component.key, newVal);
}

in various "custom JS" places, but I haven't found a way to trigger it reliably right at the time when selectOptions are updated, e.g. as a result of a changed filter query. Is there an existing event that can be used as a trigger, like 'selectOptionsChanged'?

franke-mc avatar Nov 29 '19 02:11 franke-mc

I don't believe we have an event for that - you may be able to add one in setItems:

https://github.com/formio/formio.js/blob/1ee427a7e98ffab803e900255c3ab5c0ea89a72d/src/components/select/Select.js#L222L332

aiwebb avatar Dec 02 '19 15:12 aiwebb

Ok, so one possible solution might look like this:

diff --git a/src/components/select/Select.js b/src/components/select/Select.js
index 4cee86a2b..b60befac9 100644
--- a/src/components/select/Select.js
+++ b/src/components/select/Select.js
@@ -329,6 +329,15 @@ export default class SelectComponent extends Field {
 
     // Say we are done loading the items.
     this.itemsLoadedResolve();
+
+    // Emit an event suitable for triggering custom logic
+    const eventName = this.component.optionsChangedEvent;
+    if (eventName) {
+      this.emit(eventName, {
+        component: this.component,
+        items: items
+      });
+    }
   }
   /* eslint-enable max-statements */
 
diff --git a/src/components/select/editForm/Select.edit.data.js b/src/components/select/editForm/Select.edit.data.js
index 09a41e605..e38949456 100644
--- a/src/components/select/editForm/Select.edit.data.js
+++ b/src/components/select/editForm/Select.edit.data.js
@@ -496,6 +496,14 @@ export default [
       },
     },
   },
+  {
+    type: 'textfield',
+    label: 'Options Changed Event',
+    key: 'optionsChangedEvent',
+    input: true,
+    weight: 20.5,
+    tooltip: 'The event to fire when the select options may have changed.'
+  },
   {
     type: 'checkbox',
     input: true,

Such an event can then be used to trigger a custom logic action:

                             "label": "Select Foo",
                             "limit": 100,
-                            "logic": [],
+                            "logic": [
+                                {
+                                    "name": "fooAutoSelect",
+                                    "actions": [
+                                        {
+                                            "name": "fooAutoSelect",
+                                            "type": "value",
+                                            "value": "//
+MyUtils.autoSelect(data, instance, component);"
+                                        }
+                                    ],
+                                    "trigger": {
+                                        "type": "event",
+                                        "event": "fooOptionsChanged"
+                                    }
+                                }
+                            ],
                             "width": 12,
                             "filter": "{{MyUtils.fooFilter(data)}}",
...
+                            "optionsChangedEvent": "fooOptionsChanged",

... with e.g. the following implementation, which also displays the Select component as disabled in case of an auto-selected option:

export function autoSelect(data, instance, component) {
  const currVal = _.get(data, component.key);
  const options = instance.selectOptions;
  if (options.length === 1) {
    const newVal = options[0].value;
    if (currVal !== newVal) {
      //if (newVal && newVal.data) {
      //  console.debug(`Auto-selecting ${component.key}: ${currVal && currVal.data} ==> {data: ${JSON.stringify(newVal.data)}, ...}`);
      //}
      instance.setValue(newVal);
      _.set(data, component.key, newVal);
      instance.options.readOnly = true;
      instance.redraw();
    }
  }
  else {
    if (instance.options.readOnly) {
      //console.debug(`Auto-select:   ${component.key} will be enabled.`);
      instance.options.readOnly = undefined;
      instance.redraw();
    }
  }
}

Comments?

By the way, as far as I can see there is no way to access the event data (e.g. items in this case) from a (custom logic) JS 'action' triggered by a certain event name. Is this correct?

franke-mc avatar Jan 14 '20 18:01 franke-mc

You are correct that there is no event that automatically fires when the options are refreshed. You can see the function callback at https://github.com/formio/formio.js/blob/1ee427a7e98ffab803e900255c3ab5c0ea89a72d/src/components/select/Select.js#L461-L473 that is fired when the options are refreshed.

One thing that might work is that instead of setting the value to emptyValue at https://github.com/formio/formio.js/blob/1ee427a7e98ffab803e900255c3ab5c0ea89a72d/src/components/select/Select.js#L463 we could set it to the defaultValue so that it runs the customDefaultValue logic again. You could grab the items[0] there as the default value.

randallknutson avatar Jan 14 '20 21:01 randallknutson

We're currently addressing a backlog of GitHub issues, and as part of this effort, some inactive issues may be marked as closed. This isn't a dismissal, but a step toward more efficient tracking. Closing this thread as it is outdated. Please re-open if it is still relevant. Thank you for your contribution!

VikkiAlenn avatar Jan 31 '24 14:01 VikkiAlenn

I was just looking at a feature like described above, can we reopen this please.

ThePhoenixBird avatar Jun 19 '24 21:06 ThePhoenixBird

I have logged a ticket for review by our planning team. In the meantime, we are always willing to review any contributions related to this behavior. Internal reference: FIO-8623

daneformio avatar Jul 03 '24 13:07 daneformio

After review, we don't expect to resource a developer to investigate this in the near future but would be happy to review any contributions to resolve this behavior.

lane-formio avatar Jul 08 '24 16:07 lane-formio