I have a form that includes a text_area and a few other controls. I’m trying to update the content of the text_area in a callback.
What works:
If I define a key argument for the text_area and get in the callback via the on_click argument of the submit_button I can do something like st.state_session.my_key = foo to update the the text of the text_area However, I can’t pass arguments (values from other components of the form) to the callback correctly because I run into the issue that’s covered in the FAQ where arguments are always one step behind. I’m a bit surprised because these components do not have a key and the FAQ says this is an issue when using st.session_state and doesn’t say anything about forms but apparently the same thing applies to all elements in a form?
What doesn’t work:
I noticed some of the examples in the doc use the pattern below:
...
submit = st.form_submit_button(...)
if submit:
callback_func(a,b,c)
...
This has the benefit that the argument passed to the callback are correct but for some reasonu pdating the text_area in the callback now fails:
streamlit.errors.StreamlitAPIException: `st.session_state.my_text_area` cannot be modified after the widget with key `my_text_area` is instantiated.
Streamlit won’t have knowledge of any changes made to inputs inside a form until it’s submitted. This is the design of the form element. If you want to update a widget based on some action, that action will have to be outside of a form (either the submission of the form or something else entirely). Please can you describe the workflow you are trying to implement specifically so I can understand what you are looking for? I can probably mock up a simple example for you. (By the way, if you put keys in all your form widgets, you can get them from session state inside of the callback, which I gather is what you’re going for…)
So this almost work in the sense that the text_area in the form gets updated but it’s always one step behind in terms of values. I thought callback runs first before the whole script is rerun which is why I updated the session_state in the callback and hoped that then the text_area is redrawn but I must be missing something.
If you want to do something in a callback with a freshly submitted value, you have to grab it by its key within the callback and can’t have it as an argument provided with the on_click or on_change function.
I added a numeric version, too, in case it was of interest.
import streamlit as st
# Added a numeric value that can be incremented, just as an example
if 'result' not in st.session_state:
st.session_state.result = 0
# No argument in the callback; current value to be added is grabbed directly from session state
def my_callback():
# Get submitted value
temperature = st.session_state.temperature
# Append string to key of widget
st.session_state.my_key = st.session_state.my_key + str(temperature)
# Add value to non-widget key in session state
st.session_state.result += temperature
with st.form("foo"):
st.text_area("label", key="my_key")
st.markdown(f'#### The total temperature is {st.session_state.result:.2f}.')
temp = st.slider(
"Temperature",
min_value=0.0,
max_value=100.0,
value=1.0,
key='temperature'
)
# Option 1:
submit = st.form_submit_button(
"compute",
on_click=my_callback
)