gradio
gradio copied to clipboard
Implement form validation (mandatory fields)
- [x] I have searched to see if a similar issue already exists.
Is your feature request related to a problem? Please describe.
There's no form validation on Gradio
Describe the solution you'd like
Implement basic form validation for form fields. Specifically, a suggestion would be to add a mandatory
parameter to all input fields, if true
and one tries to submit without filling that in, they get a front-end error without actually submitting
Additional context
Implementing form validation on the user side increases complexity on apps, I feel that could be sorted on Gradio side
yes i've had same feedback before (required
prop on inputs)
We used to have something similar and then it was removed in 3.0 and then i was going to reimplement recently and then we decided not to. But i can't find the issues where this happened or remember why. @abidlabs Can you remember why we decided not to?
I agree though, I think it is a useful feature. Do we want more complex input validation here or just 'required' fields?
We could easily accept a regex as a string, for validation purposes, a function would be more difficult because then the validation wouldn't happen on the frontend. Realistically, we would also want to validate on the backend because bypassing the frontend checks would probably be pretty easy. But i do think we should also validate on the frontend if possible just to keep things fast.
required
could take a bool or a string: required=True
required="[a-zA-Z0-9]+"
Yes, definitely doable. The only tricky thing I can think of is whether or not a component is required may depend on what event trigger is being called. I.e. you could have 2 events, and in one of them, a component is required, but in the other, the component is not required. How we fit this into the Python API I'm not sure cc @aliabid94 @freddyaboulton
you could have 2 events, and in one of them, a component is required, but in the other, the component is not required
Great point @abidlabs, what if the required
props don't live on each component - but rather the event trigger (button.click
for example) has a list of which components are mandatory?
Realistically, we would also want to validate on the backend because bypassing the frontend checks would probably be pretty easy. But I do think we should also validate on the frontend if possible just to keep things fast.
@pngwn IMO would be fine to split the two tasks and start with front-end validation only. I am aware it is easy to meddle with front-end validation (also for existing limitations e.g.: sliders https://github.com/gradio-app/gradio/issues/1071), but I think just front-end may be simpler and solve a big pain-point as a first step
@abidlabs @aliabid94 I've been thinking about how to implement this given that it came up in #3485 and this is what I have so far. I think we can add a validation
parameter to the event handler. This parameter will take a python function, as that will enable app creators to build very customized validation. We can add a shorthand for the case of every input being present (e.g. gr.all_non_none
).
The bad thing about doing it in the python side is that if there is a long queue, users will not get an immediate error message. However, I think we can work around this nicely with the new success
event. Under the hood, we can do something like:
self.click(validation).success(fn)
We can also skip the queue for validation functions, since presumably they should run very fast
have you implemented a non-empty text input validation?
Not yet @bxclib2
@freddyaboulton Just wondering if anything was implemented around this? If not, any suggestions on implementing a page level check for a "Submit" button?
I'm assuming something like submit_button.click(field_validate, inputs=[],outputs[]).success(process_submit,inputs=[],outputs=[])
If there are 10 fields to check on the screen, other than passing reference to all 10 fields via inputs= , is there another less verbose way to do that? Thanks!
No currently @dwipper ! The approach you have suggested is the correct one. One thing you can do is pass in a set of components as opposed to a list so that the order does not matter.
@freddyaboulton Thanks. What would the syntax for the set be vs. the list inputs=[cmpt1, cmpt2,cmpt3,cmpt4] ?
You can use set([cmpt1, ..])
or {cmpt1, cmpt2}
@julien-c Hello, is there any code implementation for submitting form verification? Thank you very much
@julien-c Here's the code I implemented:
def validate_user_credentials(firstname, lastname, affiliation, username, password1, password2):
# Check if the firstname is not blank
if not firstname:
raise gr.Error("First Name cannot be blank")
# Check if the lastname is not blank
if not lastname:
raise gr.Error("Last Name cannot be blank")
if not affiliation:
raise gr.Error("Affiliation cannot be blank")
# Check if the username is not blank
if not username:
raise gr.Error("Username cannot be blank")
# check if the username already exists. If True, then username exists.
if check_username(username):
error_str = f'User name {username} already exists. Please try another one. If you need a password reset, click below.'
raise gr.Error(error_str)
# Check if the username is a valid email address
if not re.match(r"[^@]+@[^@]+\.[^@]+", username):
raise gr.Error("Username must be a valid email address")
# Check if passwords are not blank
if not password1 or not password2:
raise gr.Error("Passwords cannot be blank")
if len(password1) < 8:
raise gr.Error("Password must be at least 8 characters long")
# Check if password1 meets the criteria
if (not re.search(r"[A-Z]", password1) or
not re.search(r"\d", password1) or
not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password1)):
raise gr.Error("Password must contain at least one capital letter, one number, and one special character")
# Check if password1 and password2 are equal
if password1 != password2:
raise gr.Error("Passwords do not match")
return
The function is called from a button click, and uses .success to do conditional event chaining:
btn.click(fn=validate_user_credentials, inputs=[firstname_tbox, lastname_tbox, affiliation_tbox, username_tbox, password1_tbox, password2_tbox], outputs=None, show_progress="hidden").success(fn=next_function, inputs=[], outputs=[])
So, it seems like there's no easy way to enforce required inputs on the field currently?
@sayakpaul I haven't seen one. It might be possible to do with javascript vs a python function, i.e. use the js= param on the btn.click() event, although I haven't seen any examples of that.