Summary
I’m trying to write a for loop to generate forms, with buttons beneath each form that dictates how many entry boxes are in each respective form (one button to add a box, one button to remove a box). My resulting code only changes the number of boxes in the last form.
Steps to reproduce
This is the code I want to be able to use, with the for loop (because in the actual use case the number of forms will be passed in from a different page of the app):
Code snippet:
roles = ["Role 1", "Role 2"]
for i in range(len(roles)):
with st.form(key=f"role_"+roles[i]):
if f"counter_form_"+roles[i] not in st.session_state:
st.session_state[f"counter_form_"+roles[i]] = 0
st.write(f"Role: {roles[i]}")
col1, col2 = st.columns(2)
with col1:
for j in range(st.session_state[f"counter_form_"+roles[i]]):
st.text_input(f"Input {i}"
, key=f"input{i}.{j}")
with col2:
for j in range(st.session_state[f"counter_form_"+roles[i]]):
st.selectbox(label=f"Select {i}"
, options=["Option 1", "Option 2", "Option 3"]
, key=f"select{i}.{j}")
submit_privileges = st.form_submit_button(label=f"Submit {roles[i]}")
if submit_privileges:
st.write(f"Form {i} submitted")
def increment_function():
st.session_state[f"counter_form_Role 2"] += 1
def decrement_function():
st.session_state[f"counter_form_Role 2"] -= 1
col1, col2 = st.columns(2)
with col1:
increment = st.button(label=f"Increment {roles[i]}"
, on_click=increment_function)
with col2:
decrement = st.button(label=f"Decrement {roles[i]}"
, on_click=decrement_function)
**Code Snippet for what does work as intended, but the forms are generated hard-coded:
with st.form(key=f"role_"+"Role 1"):
if f"counter_form_"+"Role 1" not in st.session_state:
st.session_state[f"counter_form_"+"Role 1"] = 0
st.write(f"Role: Role 1")
col1, col2 = st.columns(2)
with col1:
for j in range(st.session_state[f"counter_form_"+"Role 1"]):
st.text_input(f"Input Role 1.{j}"
, key=f"input Role 1.{j}")
with col2:
for j in range(st.session_state[f"counter_form_"+"Role 1"]):
st.selectbox(label=f"Select Role 1"
, options=["Option 1", "Option 2", "Option 3"]
, key=f"select Role 1.{j}")
submit_privileges = st.form_submit_button(label=f"Submit Role 1")
if submit_privileges:
st.write(f"Form Role 1 submitted")
def increment_function():
st.session_state[f"counter_form_"+"Role 1"] += 1
def decrement_function():
st.session_state[f"counter_form_"+"Role 1"] -= 1
col1, col2 = st.columns(2)
with col1:
increment = st.button(label=f"Increment Role 1"
, on_click=increment_function)
with col2:
decrement = st.button(label=f"Decrement Role 1"
, on_click=decrement_function)
with st.form(key=f"role_"+"Role 2"):
if f"counter_form_"+"Role 2" not in st.session_state:
st.session_state[f"counter_form_"+"Role 2"] = 0
st.write(f"Role: Role 2")
col1, col2 = st.columns(2)
with col1:
for j in range(st.session_state[f"counter_form_"+"Role 2"]):
st.text_input(f"Input Role 2.{j}"
, key=f"input Role 2.{j}")
with col2:
for j in range(st.session_state[f"counter_form_"+"Role 2"]):
st.selectbox(label=f"Select Role 2"
, options=["Option 1", "Option 2", "Option 3"]
, key=f"select Role 2.{j}")
submit_privileges = st.form_submit_button(label=f"Submit Role 2")
if submit_privileges:
st.write(f"Form Role 2 submitted")
def increment_function():
st.session_state[f"counter_form_"+"Role 2"] += 1
def decrement_function():
st.session_state[f"counter_form_"+"Role 2"] -= 1
col1, col2 = st.columns(2)
with col1:
increment = st.button(label=f"Increment Role 2"
, on_click=increment_function)
with col2:
decrement = st.button(label=f"Decrement Role 2"
, on_click=decrement_function)
Expected behavior:
When I run the code I want to be able to run, I want to be able to press one of the Increment or Decrement buttons to add or subtract rows of inputs from the form directly above it. However, that only works in the hard-coded solution I provided above. In the for-loop option, it does not matter which button I press (which form it is associated with); the button(s) only update the last form.
Debug info
- Streamlit version: 1.25.0
- Python version: 3.10.5
- OS version: MacOS
- Browser version: Chrome
Thank you in advance!