How can I use callbacks to change the available options in a multi-select box with st.text_input?

Summary

I have a text box created using the st.text_input field where I ask a user for a search term. The app then uses the search term to retrieve values from a snowflake table, and then uses the results of that query to create a list of selectable items from a multi-select box. How can I make this work so that the search is executed after the user enters the search term without having to click on submit?

Steps to reproduce

Code snippet:

import streamlit as st
import sqlite3
import hashlib
#from session_state import get
import matplotlib.pyplot as plt

import snowflake.connector
import pandas as pd
import os
from snowflake import connector


# Extract an individual environment variable
user = os.environ.get('SNOWFLAKE_USER')
password = os.environ.get('SNOWFLAKE_PASSWORDS')
snowflake_account_locator = os.environ.get('SNOWFLAKE_ACCOUNT_LOCATOR')
snowflake_region = os.environ.get('SNOWFLAKE_AZURE_REGION');
account = '{0}.{1}'.format(snowflake_account_locator, snowflake_region)
warehouse = 'COMPUTE_WH'
schema = 'public'
database =  'ACCESSSQ'



# Set up the database connection
conn = snowflake.connector.connect(
    account=account,
    user=user,
    password=password,
    warehouse=warehouse,
    database=database,
    schema=schema
)

def run_query(query):
    cursor = conn.cursor()
    cursor.execute(query)
    df = cursor.fetch_pandas_all()
    return df

# Create the Streamlit app
def main():
    st.set_page_config(page_title="My App", page_icon=":guardsman:", layout="wide")

    session_state = get_session_state()
    st.sidebar.title("Navigation")
    pages = {
        #"Profile": profile,
        #"Settings": settings,
        "New Vendor": new_profile,
    }
    page = st.sidebar.radio("Go to", list(pages.keys()))
    pages[page](session_state)

def get_session_state():
    if "session_state" not in st.session_state:
        st.session_state["session_state"] = SessionState()
    return st.session_state["session_state"]

class SessionState:
    def __init__(self):
        self.logged_in = False
        self.selected_items = []


def new_profile(session_state):
    

    st.title("Create new profile")

    with st.form(key='columns_in_form'):
        c1, c2 = st.columns(2)
        with c1:
            name = st.text_input("Company name")
        with c2:
            url = st.text_input("Website")
        c3 = st.columns(1)
        with c3[0]:
            description = st.text_area("Company description")
        c4 = st.columns(1)
        with c4[0]:
            search_term = st.text_input("Enter a search term:")
            query = "SELECT Name, Id FROM technology WHERE lower(provider) like lower('%{0}%') GROUP BY Name, Id".format(search_term)
            technologies = run_query(query)
            technology_names = list(technologies['NAME'])
            default_selected_items = [item for item in session_state.selected_items if item in technology_names]
            updated_items = st.multiselect("Select options:", technology_names, default=default_selected_items)
            submit_button = st.form_submit_button(label='Technology Supplier Submission')
            if submit_button:
                session_state.selected_items.append(updated_items)
            st.write(session_state.selected_items)
 

if __name__ == "__main__":
    main()

Simply run the code. Enter a search term into the search box, but instead of clicking on the submit button, have the search automatically run and modify the available clickable items from the multi-select box. Additionally, when it’s adding items to the session_state.selected_items list at the moment, it seems to be changing the selected_items list in a strange way. It seems to be adding entirely new elements to the list instead of inserting them into the same list, such that the list becomes horribly nested. How can I solve that?

Here is a simplified answer:

Assign a key to the text input (e.g.‘keyword’) and a callback function. Within the callback function, run a query based on the session state value given by that key. Assign the results to some new key (e.g. ‘results’). Then, checking whether or not some results exist, proceed to use them to populate your multiselect widget.

import streamlit as st

def get_results():
    # Use st.session_state.keyword to run a query
    st.session_state.results = [st.session_state.keyword+f'_{i}' for i in range(1,11)]

st.text_input('Keyword', key='keyword', on_change=get_results)

if 'results' not in st.session_state:
    st.stop()

selections = st.multiselect('Options', st.session_state.results)
st.write(selections)

Note however, that this will not work inside of a form. A form blocks Streamlit from updating the result at the time the user types it in and delays that action until the user submits the form.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.