Iām creating a simple web app, which sort of is like google forms. Based on a variable, say x, from needs to be submitted x times. Hereās a toy code snippet:
x = 10
def my_form(count):
st.session_state.a_list = []
with st.form(key=f"{count}"):
# Question 1 - text_input - st.session_state.a_list.append(q1)
....
# Question 5 - radio button st.session_state.a_list.append(q5)
st.form_submit_button(label="Next")
# I could use a loop like this
for i in range(1, x+1):
my_form(i)
# But it would print out all of the form, x times in a single page.
When the Next button is clicked in the form, itās supposed to clear the page and repeat all the questions.
So I tried to use callback on the same function, something like this:
st.session_state.a_list = []
def my_form(count):
if count <= 1:
return
with st.form(key=f"{count}"):
....
st.form_submit_button(label="Next", onclick=my_form, args=(count-1,))
But I couldnāt figure out how to do implement it correctly, it would end up creating form (or sub components) with same keys.
Please note the way Iām taking x is via streamlit itself (with st.number_input), so that component should disappear once I receive the x value, and open up a page with form questions.
What is your use case for this? Do you imagine one individual having to run through your form 10 times answering the same questions? What is the end goal of your app?
Hi @Marisa_Smith, yea thatās true, Iām creating a data collection form, so one form submission corresponds to one member of the family. The questions are repeated for each member of the family.
If I understand correctly what you are trying to do I donāt think you want/need this counter. The design of st.form will do what youāre looking for without this.
It seems you are creating a dataset, from users who go to your app and input themselves and their familyās into your database.
To truly do this you will need to set up a connection to a database system where you can store all the data as a new line/row in your data frame. I think the easiest way is to use google sheets (either a public or private one), there are tutorials in our docs for both here: https://docs.streamlit.io/en/stable/tutorial/databases.html
Then, when the person fills in their data and hits the st.form_submit_button you can actually have streamlit submit their entry to the google sheet and then clear the data from the form so they can easily start over with another family member by simply using the clear_on_submit paramter like so:
Hey @joe733! Sorry for my delay yesterday was super busy!
OOOooOOOooooOOO gotcha!
kkkkk yes let me think about thisā¦
I think the best method for this is by using st.session_state. I do think itās going to be slightly tricky to get done but between the two of us, Iām sure we can get this to work!
The first page would be a form with just the 2 questions where you would save those values in your st.session_state so you can access them later.
Then depending on the number of family members, you can create a loop (or radio buttons as you mocked up) so that the person inputs the details for each family member into a different form. Once their details have been added, you can save them to their own folder as a .txt file or CSV (whatever your preference). You will need to be careful and check if the file paths exist already so youāre not overwriting any data. These parts would be done with the os package, and with some quick searching, I found a very helpful like about using os to create directories through python scripts!
Doing the loop solution is slightly more tricky, I started mocking up a solution for you (but got interrupted with how busy my week has been). Iām going to add my code below so you have an example if you were curious, but its not complete yet!
import streamlit as st
import os
st.title("Family form")
# container to hold the 1st form
container_1 = st.empty()
# the counter that you will use to compare with the number of people
# in the family will reset this to 0 to finish/restart process
if "counter" not in st.session_state:
st.session_state["counter"] = 0
# form 1 submission tracker reset this to finish/restart process
if "form_1" not in st.session_state:
st.session_state["form_1"] = False
#first form appears if counter is set or reset to 0
if st.session_state["counter"] == 0:
with container_1:
name_form = st.form("Family details", clear_on_submit=True)
with name_form:
# having keys for each input in the form will auto save them to our state!
family = st.text_input("Family Name", key="fam_name")
fam_num = st.number_input("Number of Family Members", 1, key="fam_num")
fam_submit = st.form_submit_button("Next")
st.session_state #prints state to our app while we are creating the solution
if fam_submit:
container_1.empty() #clears the 1st form!
# we will use "Form_1" entry in state while we enter individuals data
st.session_state["form_1"] = True
####
# add in solution using the os package that will create
# a directory based on family name
if st.session_state["form_1"]: # have to have done the 1st form
while st.session_state["counter"] < fam_num:
st.session_state["counter"] += 1
form_title = "family member {} of {}".format(st.session_state["counter"], fam_num)
details = st.form(form_title, clear_on_submit=True)
with details:
first_name = st.text_input("First Name", key="Fist_name")
age = st.number_input("age",30, key="age")
### etc...
detail_submit = st.form_submit_button("Finish")
if detail_submit:
## here make sub directories with the name and save
# a file with the info for later!
# when finished with famliy members reset counter to 0
# and the form_1 submission tracker to false
st.session_state
if st.session_state["counter"] == fam_num:
st.session_state["counter"] = 0
st.session_state["form_1"] = False
As I said itās not finished but itās a good start. Right now on this line:
if fam_submit:
container_1.empty() #clears the 1st form!
You will likely see an error Bad message format:'setIn' cannot be called on an ElementNode. This is already flagged with the team and we are currently working on a solution. Right now the way I am getting around it is just by hitting the ādoneā button and the app continues (cause there is no ārealā error here because we are using values we have stored in our state which are not forgotten on the next run).
Hopefully, this will be pushed before your app needs to go public. BUT in case it isnāt, the next best solution (I think) is to put your 1st form in the sidebar, and then once itās been filled out, the app follows the same logic and the 2nd form appears! (itās exactly the same logic just the form appears in a slightly different place while we sort that error message out!)
In this example, I was trying to do it the more complicated way but the radio buttons in the sidebar are a great solution too! And now with state, you can even have a ānextā option button that will advance the radio buttons for you!
Hey @joe733! Sorry for my delay yesterday was super busy!
Itās totally fine, I understand.
I think the best method for this is by using st.session_state . I do think itās going to be slightly tricky to get done but between the two of us, Iām sure we can get this to work!
The first page would be a form with just the 2 questions where you would save those values in your st.session_state so you can access them later.
ā¦
Wow, thatās a good headstart! Need to wrap my head around a few thingsā¦ They told me the format of the form has some changes. Iām not sure how much.
But thank you so much! Iāll go through them, considering those the changes and would update here If I need more help. Thanks a lot!
- main_fd.py
- multiapp.py
- memberX.py # Individual form
When main_df.py is run it creates n copies of memberX.py and adds them to mutiapp.py which in turn executes a function called app() defined in each memberX.py (X = 1, 2, 3...n).
But when I click on the option say Member 3, the right side goes blank, instead of the expected form (or content as shown in the YouTube video).
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking āAccept allā, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.