Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make TextInput work for forms #7567

Open
MarcSkovMadsen opened this issue Dec 23, 2024 · 3 comments
Open

Make TextInput work for forms #7567

MarcSkovMadsen opened this issue Dec 23, 2024 · 3 comments

Comments

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Dec 23, 2024

When comparing to Reflex its clear they have given more thought to their TextInput for forms.

We are missing basic attributes like

  • required (True or False)
  • type (for example "email")
  • pattern (".+@example\.com")

That can help users when they enter text.

Reflex will

  • make a TextInput active is its required and nothing is input
  • show a tooltip explaining what is missing (for example a @ in an email)
reflex-form.mp4
@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented Dec 23, 2024

This is the best alternative/ workaround I can come up with.

panel-form.mp4
import panel as pn
import param
import json

FORM_TEXT = """\
<h1>Join Newsletter</h1>

Get the latest updates and news about Panel.
"""

FORM_ICON = """\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"> <path d="M3 7a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10z"></path> <path d="M3 7l9 6l9 -6"></path> </svg> 
"""

class FormState(param.Parameterized):
    name = param.String(default="", doc="The name of the user.")
    email = param.String(default="", doc="The email of the user.")
    message = param.String(default="", label="Message", doc="An optional message from the user")

    is_not_valid = param.Boolean(default=False)
    validation_errors = param.Dict()
    validation_message = param.String()

    def __init__(self, **params):
      params["name"]=params.get("name", "")
      super().__init__(**params)
    
    def _validate(self):
        errors = {}
        
        if not self.name:
            errors["name"] = "No *Name* entered."
        if not self.email:
            errors["email"] = "No *Email* entered."
        elif not "@" in self.email or not "." in self.email:
            errors["email"] = "Not a valid *Email*."
        
        self.validation_errors=errors
        self.is_not_valid = bool(errors)
        self.validation_message = "**Error**. " + " ".join(errors.values())
    
    def _to_dict(self):
      return {
        "name": self.name, "email": self.email, "message": self.message
      }

    def _reset_to_defaults(self):
        self.param.update(name=self.param.name.default, email=self.param.email.default, message=self.param.message.default)
    
    def submit(self, event):
        self._validate()
        
        if not self.validation_errors:
            pn.state.notifications.success(f"Form submitted: {self._to_dict()}", duration=2000)
            self._reset_to_defaults()

    

def create_form():
    form_state = FormState()

    header = pn.Row(
      pn.pane.SVG(FORM_ICON, margin=0, height=80, sizing_mode="fixed"),
      FORM_TEXT,
    )

    error_pane = pn.pane.Alert(object=form_state.param.validation_message, visible=form_state.param.is_not_valid, alert_type="danger", stylesheets=["p {margin-bottom: 0}"])
    
    name_input = pn.widgets.TextInput.from_param(form_state.param.name, name="Name*", placeholder="User Name")
    email_input = pn.widgets.TextInput.from_param(form_state.param.email, name="Email*", placeholder="Email Address")
    message_input = pn.widgets.TextAreaInput.from_param(form_state.param["message"], placeholder="An optional message")
    
    submit_button = pn.widgets.Button(name="Send", on_click=form_state.submit, button_type="primary")
    
    return pn.Column(header, error_pane, name_input, email_input, message_input, submit_button, sizing_mode="fixed", width=500, styles={"margin": "auto"})



pn.extension(notifications=True, design="bootstrap", sizing_mode="stretch_width")

form = create_form()
form.servable()

@MarcSkovMadsen
Copy link
Collaborator Author

If some of these features are added the create_form.md tutorial #7568 should be updated.

@ahuang11
Copy link
Contributor

pattern ("[email protected]")

There's regex for param.String which theoretically would propagate to TextInput.

Slightly surprised there's no bounds or min_length or max_length for param.String, but I might have missed it.

Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants