Filter one Multiselect based on another Multiselect in a List View

The feature in action:

If you'd like to have two multiselect components with the same values in each component, but if the value is selected in one, it shouldn’t be able to be selected in the other all while inside a list view, then this is the post for you! The simple solution (two multiselects, no list views involved) is linked here.

  1. Create your query that will populate the multiselect components. Your multiselects' data could also come from any other array.

  1. Create a JS query to check if we should be populating the multiselect with default or filtered data.

let results = [];
for (let i=0; i<listView1.instances; i++) {
  let first_arr = multiselect2[i].value.length ? getActors.data.first_name.filter(x => !multiselect2[i].value.includes(x)) : getActors.data.first_name
  let anti_arr = multiselect1[i].value.length ? getActors.data.first_name.filter(x => !multiselect1[i].value.includes(x)) : getActors.data.first_name
  let obj = {first: first_arr, anti: anti_arr}
  results.push(obj);
}
return results;
  1. Make sure to trigger this JS query on success of the main query (getActors in this example) to populate the multiselects on page load!

  2. Set the multiselect’s data property to the JS query’s data using the Javascript Data source option. Since we’re inside a list view, we need to use the index to grab the correct data object. For the first/left/OG/etc. multiselect, key into the ‘first’ object and ‘anti’ for the second. These names are arbitrarily set in the JS query, so feel free to change!

  1. From each multiselect component, add an event handler to trigger this JS query on change. This way, we ensure that the data is properly filtering with every new selection or change

.

See attached JSON of this app below and feel free to import in your own Retool organization (docs on importing here).
multiselect depending on multiselect in listview.json (18.1 KB)

This should be a good jumping-off point, though you may need to make some slight adjustments based on your data source!

3 Likes

Hey @victoria, thank you for this how-to.

I noticed that you are using JS queries and I was wondering why.

I've been mostly doing this with Transformers, as it saves a little bit of work - I don't need to Trigger it each time the data changes.
I also experienced some user frustration/data corruption where for some reason the query didn't trigger and user saw and selected old query results.

This is one the main benefit of react, it reacts to changes in state.

So my question is, if there is a reason you decided to go with JS queries instead of a transformer, and should I consider doing the switch too, despite the mentioned drawbacks?

That is a fantastic question, @Mendy. I feel like I had a reason at some point when I was building the app, but I can't remember anymore :grimacing: Transformers definitely seem like the obvious, automatically-updating choice here. Let me see if there's a reason why I used JS queries and get back to you!

There are cases where Transformers can impact performance.
If the transformer access a lot of components/queries that change often but doesn't require the transformer result to change as well, that's a waste of cpu power and can sometime reduce performance

Definitely! I usually choose JS queries for that reason (unless I know the data is going to be changing a lot).

In this very specific example, it does make sense to use a transformer (instead of a JS query triggered by event handlers), but the transformer doesn't seem to be re-running on change, so the data of the second multiselect isn't getting filtered out. Not super sure why yet, but that's probably why I stuck with a JS query :sweat_smile:

Hey ! Since Retool supports LoDash natively, have you tried using the _.omitBy() function as a param of your second component ?

Feature Demo GIF

Here's the code for this implementation :smiley: :

 _.omitBy(test_data_array, multiSelectOption => firstInput.value.includes(multiSelectOption))

You can add the code to both components to make them filtering each other. Or only in the child component if you needed the parent to control the options available for the child.

Note: Using this method, the child multiselect component will automatically unselect any value that is not present in the updated available options set

2 Likes

This is beautiful! Thank you for taking the time to share this much more elegant solution :raised_hands: