Submit form within a button clears form

def create_new_form():
    with st.form("myform"):
        x = st.text_input("Foo", key="foo")
        submit = st.form_submit_button(label="Submit")

    if submit:
        st.write('Submitted')
        st.write(x)

If I run this, the form prints the values properly.

create_new_form()

But if I wrap the above function within a buttonclick (run the below instead), nothing is written and its in its original state

newScenario = st.button('Create New Scenario', key='a')
if newScenario:
    create_new_form()

Wondering if this is a bug or something is wrong with my approach?

It may be the latter :sweat_smile: By the way, you havenโ€™t defined the clear_form function, so I canโ€™t comment on it.

But the larger issue is that youโ€™re conditionally displaying the form based on an st.button click. When you click the newScenario button and then click the form submit button, the app reruns from top to bottom and sets the clicked state of the newScenario button to be false โ†’ form is no longer displayed.

The solution may be to set clear_on_submit=True in st.form

import streamlit as st

def create_new_form():
    with st.form("myform", clear_on_submit=True):
        x = st.text_input("Foo", key="foo")
        submit = st.form_submit_button(label="Submit")

    if submit:
        st.write("Submitted")
        st.write(x)

create_new_form()

clear-form

Is the above the desired behavior? :balloon:

thanks for the explanation!
Can you instead run the below? This will help you understand what I meant.

import streamlit as st

def create_new_form():
    with st.form("myform", clear_on_submit=True):
        x = st.text_input("Foo", key="foo")
        submit = st.form_submit_button(label="Submit")

    if submit:
        st.write("Submitted")
        st.write(x)

# create_new_form()
newScenario = st.button('Create New Scenario', key='a')
if newScenario:
    create_new_form()

That works as expected for me. What do you think is wrong? The form is shown only after you click Create New Scenario. What else do you expect?

1 Like

Pardon my ignorance here, but I expected the process to write โ€œSubmittedโ€ and the value of โ€œxโ€ to be printed on the screen. Instead the form disappears. while I understand that streamlit executes the script in steps, the form_submit_button seem to collide with the st.button and resets it.
Kapture 2023-03-09 at 12.02.38

Yeah, I completely missed the point. Buttons donโ€™t have state, so after you click Submit the script reruns and now the application does not remember that you had clicked Create New Scenario so the function create_new_form_() is not called.

You can use session_state to remember tha fact that the button has been pressed.

import streamlit as st
from streamlit import session_state as ss


def create_new_form():
    with st.form("myform", clear_on_submit=True):
        x = st.text_input("Foo", key="foo")
        submit = st.form_submit_button(label="Submit")

    if submit:
        st.write("Submitted")
        st.write(x)


if "show_form" not in ss:
    ss["show_form"] = False

newScenario = st.button("Create New Scenario", key="a")
if newScenario:
    ss["show_form"] = True

if ss["show_form"]:
    create_new_form()
1 Like

Thank you for sharing this information. This works fine for this scenario. However, The challenge with this approach is that there may be some radio buttons and progress bar etc and they wonโ€™t reflect until the state gets updated. Whereas, I would like to show it in realtime.
For instance, consider this code below.
Ideal state is, when user is typing, I would like to show progress. Also, see the radio button they have selected, validate the amount they have typed etc. Anyway I can achieve while user is typing realtime instead of waiting until we submit the form?

import streamlit as st
import pandas as pd
state = st.session_state

def show_progress(unit):
    progressText = 'Total Amount Status'
    if unit > 100:
        st.progress(100, text=progressText)
    else:
        st.progress(int(unit), text=progressText)

def validate_amt(total_amt, key):
    if key in state:
        val = state[key]['edited_cells']
        totalSum = []
        for k, v in val.items():
            totalSum.append(v)
        show_progress(int((sum(totalSum) * 100) / total_amt))
        if sum(totalSum) > total_amt:
            st.error("Exceeded Total")
        else:
            st.info(f"total={totalSum}")


def create_departments_form():
    return pd.DataFrame(
        {
            "Department": ['d1', 'd2', 'd3'],
            "Amount": [0] * 3
        }
    )


def create_new_scenario_form():
    with st.form("myform1", clear_on_submit=True):
        c1, c2 = st.columns(2)
        with c1:
            formName = st.text_input("Name your form")
        with c2:
            total_amount = st.number_input("Target amount", value=10000)

        if len(formName) > 0:
            pass
        else:
            st.error("Provide form name")

        split_amt = st.radio("How do you want to split the amount?", ('Amount', 'Percentage'))
        st.write(f"picked = {split_amt}")
        key = "dept_amt" if split_amt == "Amount" else "dept_pcnt"
        departments = create_departments_form()
        dataEdit = st.experimental_data_editor(departments,
                                         key=key,
                                         on_change=validate_amt(total_amount, key))
        dataEdit['form_name'] = formName
        # Submit button
        submit = st.form_submit_button("Save Scenario")
        if submit:
            st.write("submitted")
            st.write(dataEdit)


create_new_scenario_form()

There is no way (within Streamlit) to detect what the user is typing in real time.

What you type in a text_input() widget is not available to the application until you press Enter or the focus goes to another widget.

Additionally, if the widget is inside a form, its value is not available to the application until you press the form submit button.

Thanks for all the details.

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