Anvil Community Forum

Form Validation with Anvil

Often, when you’re collecting data with a form, you’ll need certain input before you can continue. Some fields are mandatory, sometimes a date must be in the future, and sometimes the user just has to check a confirmation box before they can proceed.

I’ve written a little library to make validating your input easier. All you need to do is design your form, place Labels for your error messages (eg a Label that says “you must enter a username”), and write a few lines of code to define what you require for validation:

def __init__(self, **properties):
  self.init_components(**properties)

  v = form_checker.validation.Validator()
  # Make text_box_1 mandatory, and show err_1_lbl if it's not filled out
  v.require_text_box(self.text_box_1, self.err_1_lbl)

  # submit_button is only enabled when you've filled everything out:
  v.enable_when_valid(self.submit_button)

The Validator will automatically show the error labels for components that aren’t valid. (You can even choose whether to show the message immediately, or only after the user interacts with that component.)

It’s pretty flexible - you can define your own conditions for what counts as valid input, and what events trigger a check.

Click here to get the library, and see a working example:

https://anvil.works/ide#clone:PAIXGBNAS4VRHKCU=4QQLTK7KC6L63L262AW3ESDT


Note: This library is only for use in client-side code, so it can be bypassed by a malicious user. It’s a user-interface tool; don’t rely on this to keep malicious data out of your app!

6 Likes

So what is the official way to securely do this? Or is what you have here the only option really atm?

Good question! If you want to enforce constraints, you’ll have to do that on the server. The usual pattern is:

  1. User enters data in a form. You can use something like this form validation library to give them feedback about required fields etc.

  2. User clicks “Submit”, and you call a server function to (eg) store that data in a data table.

  3. The server function checks that the data is valid. This can be a lot simpler than the UI (ie a one-line if statement), because the server is within its rights to throw an ugly error if it’s not valid. (If the server has been presented with invalid data, someone’s been fiddling with the client code!)

yea basically I just want to make sure certain fields have data in them, because most of my data is from pull downs and such I really only need to make sure they put a snake name in, etc. So I can just setup something in my existing function I guess like you have to validate the data.

I just started using the form validation library. It works well, but I’ve run into an odd case.

If I put validation on a text box that has the type number, the validation doesn’t seem to work. At least, the error message doesn’t appear, and the save button doesn’t get disabled.

Here’s a project that shows the issue: https://anvil.works/build#clone:KIIUMGL7P3YYDHGV=2DXIRYF4MBERORRUFWOU6Q4G

The first text box is of type text, the second is number. If you change the second text box to text, it works fine.

There doesn’t seem to be anything in the validation library that should be affected by the input mask, but maybe I’m not understanding something about how the text box type affects the component.

Looks like a bug in the validation library. The check for TextBoxes is

                 lambda tb: tb.text != '',

(Line 59 of validation)

But empty TextBoxes of number type have text set to None when they’re empty. So it needs to change to:

                 lambda tb: tb.text not in ('', None),

I’ve applied that change to the Form Validation app. Here’s a modified version of your test app:

https://anvil.works/build#clone:IVT2DPKOOUMTAI2U=FL3UKXVWOUEZWH5JRMVT4SCH

Thanks for letting us know!

1 Like

Thanks, that works great!

1 Like

I do like the fact that you can pick what events of each control trigger the validation.
I don’t like the fact that if you do that, you lose the event handlers that were setup in the IDE.

Is it possible to either:

  • have a component.add_event_handler() instead of a component.set_event_handler()
  • or have a component.get_event_handler(), so I can decide whether to execute the handler previously assigned from the IDE?

When you have a list of event-handlers, the set of possibilities explodes – for bad and good.

  • Insert at front, back, or after handler X
  • A handler may consume the event, or pass it along to the next handler.