gradio icon indicating copy to clipboard operation
gradio copied to clipboard

Implement form validation (mandatory fields)

Open apolinario opened this issue 1 year ago • 16 comments

  • [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

apolinario avatar Nov 25 '22 09:11 apolinario

yes i've had same feedback before (required prop on inputs)

julien-c avatar Nov 25 '22 10:11 julien-c

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]+"

pngwn avatar Nov 25 '22 13:11 pngwn

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

abidlabs avatar Nov 28 '22 02:11 abidlabs

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

apolinario avatar Nov 28 '22 08:11 apolinario

@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)

freddyaboulton avatar Mar 21 '23 16:03 freddyaboulton

We can also skip the queue for validation functions, since presumably they should run very fast

abidlabs avatar Mar 21 '23 17:03 abidlabs

have you implemented a non-empty text input validation?

bxclib2 avatar Jun 21 '23 19:06 bxclib2

Not yet @bxclib2

abidlabs avatar Jun 21 '23 20:06 abidlabs

@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!

dwipper avatar Nov 18 '23 14:11 dwipper

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 avatar Nov 20 '23 19:11 freddyaboulton

@freddyaboulton Thanks. What would the syntax for the set be vs. the list inputs=[cmpt1, cmpt2,cmpt3,cmpt4] ?

dwipper avatar Nov 20 '23 19:11 dwipper

You can use set([cmpt1, ..]) or {cmpt1, cmpt2}

freddyaboulton avatar Nov 21 '23 17:11 freddyaboulton

@julien-c Hello, is there any code implementation for submitting form verification? Thank you very much

1390806607 avatar Mar 11 '24 14:03 1390806607

@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=[])

dwipper avatar Mar 11 '24 15:03 dwipper

So, it seems like there's no easy way to enforce required inputs on the field currently?

sayakpaul avatar Mar 20 '24 03:03 sayakpaul

@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.

dwipper avatar Mar 20 '24 15:03 dwipper