I know there are other examples of custom signup flows, but I decided to make my own with ChatGPT that builds on the existing Anvil login system.
So below is only a post-signup signup form: the login and official signup are both achieved with Anvil’s default form. After the user signs up, they are then shown this form that prompts them for more details:
"""Component for Extra Signup Information."""
from anvil import *
import anvil.server
class SignupForm(ColumnPanel):
"""Post Signup Information Form."""
def __init__(self, user, **properties):
"""Initialize the form with the Anvil user object."""
super().__init__(**properties)
# Store the user object for use during signup
self.user = user
# Create UI components programmatically
self.first_name_label = Label(text="First Name:")
self.first_name_input = TextBox(placeholder="Enter your first name")
self.last_name_label = Label(text="Last Name:")
self.last_name_input = TextBox(placeholder="Enter your last name")
self.admin_signup_checkbox = CheckBox(text="Sign up as admin")
self.admin_signup_checkbox.set_event_handler(
"change", self.admin_signup_checkbox_change
)
self.admin_code_panel = ColumnPanel(visible=False)
self.admin_code_label = Label(text="Admin Code:")
self.admin_code_input = TextBox(placeholder="Enter admin code")
self.complete_signup_button = Button(text="Complete Signup")
self.complete_signup_button.set_event_handler("click", self.complete_signup)
# Add components to the form
self.add_component(self.first_name_label)
self.add_component(self.first_name_input)
self.add_component(self.last_name_label)
self.add_component(self.last_name_input)
self.add_component(self.admin_signup_checkbox)
self.admin_code_panel.add_component(self.admin_code_label)
self.admin_code_panel.add_component(self.admin_code_input)
self.add_component(self.admin_code_panel)
self.add_component(self.complete_signup_button)
def complete_signup(self, **event_args):
"""Method to complete the signup process."""
first_name = self.first_name_input.text
last_name = self.last_name_input.text
admin_code = (
self.admin_code_input.text if self.admin_code_panel.visible else None
)
if not all([first_name, last_name]):
alert("Please enter your full name.", title="Incomplete Form")
return
try:
# Call the server function to update the user's details.
result = anvil.server.call(
"complete_signup", first_name, last_name, admin_code
)
if result == "success":
alert(
"Signup successful! You can now access your account.",
title="Signup Complete",
)
self.raise_event("x-close-alert")
elif result == "invalid_code":
alert("The admin code you entered is invalid.", title="Invalid Code")
else:
alert("Signup failed. Please try again.", title="Error")
except Exception as e:
alert(f"An error occurred: {e}", title="Error")
def admin_signup_checkbox_change(self, **event_args):
"""Method to show/hide the admin code section."""
self.admin_code_panel.visible = self.admin_signup_checkbox.checked
I used ChatGPT’s new canvas feature to get most of the way to this component and then customized it as I felt necessary. I did have to tell it to replace the SignupFormTemplate with ColumnPanel inheritance as I saw @meredydd do in his demo in the video of the last Anvil monthly meeting, as well as remove the event handlers from the component initializations, but other than that most of GPT’s UI handling was pretty good.
I implemented this form in my Login form like this:
user = anvil.users.login_with_form()
if not anvil.server.call("check_signup_complete"):
alert(SignupForm(user), buttons=[])
As you can see, I just use Anvil’s built in login/signup form and then if there’s missing information, give the user the custom form.
This works great for my use case because I heavily use the anvil routes in this app and have the same signup check on the server to redirect the user to this form if they aren’t signed up or haven’t filled out all the info yet – there’s no avoiding it
My original redirector: