Skip to content

Commit 4565df0

Browse files
committed
add events for form component
1 parent 3863eb2 commit 4565df0

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-6
lines changed

src/reactpy_django/components.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
from django.forms import Form
3131
from django.views import View
3232

33+
from reactpy_django.types import FormEvent
34+
3335

3436
def view_to_component(
3537
view: Callable | View | str,
@@ -120,6 +122,9 @@ def django_form(
120122
form: type[Form],
121123
*,
122124
extra_props: dict[str, Any] | None = None,
125+
on_success: Callable[[FormEvent], None] | None = None,
126+
on_error: Callable[[FormEvent], None] | None = None,
127+
on_submit: Callable[[FormEvent], None] | None = None,
123128
form_template: str | None = None,
124129
top_children: Sequence = (),
125130
bottom_children: Sequence = (),
@@ -128,6 +133,9 @@ def django_form(
128133
return _django_form(
129134
form=form,
130135
extra_props=extra_props or {},
136+
on_success=on_success,
137+
on_error=on_error,
138+
on_submit=on_submit,
131139
form_template=form_template,
132140
top_children=top_children,
133141
bottom_children=bottom_children,

src/reactpy_django/forms/components.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from pathlib import Path
4-
from typing import TYPE_CHECKING, Any
4+
from typing import TYPE_CHECKING, Any, Callable
55
from uuid import uuid4
66

77
from django.forms import Form
@@ -17,6 +17,7 @@
1717
set_value_prop_on_select_element,
1818
)
1919
from reactpy_django.forms.utils import convert_boolean_fields, convert_multiple_choice_fields
20+
from reactpy_django.types import FormEvent
2021

2122
if TYPE_CHECKING:
2223
from collections.abc import Sequence
@@ -29,14 +30,20 @@
2930

3031
@component
3132
def _django_form(
32-
form: type[Form], extra_props: dict, form_template: str | None, top_children: Sequence, bottom_children: Sequence
33+
form: type[Form],
34+
extra_props: dict,
35+
on_success: Callable[[FormEvent], None] | None,
36+
on_error: Callable[[FormEvent], None] | None,
37+
on_submit: Callable[[FormEvent], None] | None,
38+
form_template: str | None,
39+
top_children: Sequence,
40+
bottom_children: Sequence,
3341
):
3442
# TODO: Implement form restoration on page reload. Maybe this involves creating a new setting called
3543
# `form_restoration_method` that can be set to "URL", "CLIENT_STORAGE", "SERVER_SESSION", or None.
3644
# Perhaps pre-rendering is robust enough already handle this scenario?
37-
# Additionaly, "URL" mode would limit the user to one form per page.
45+
# Additionally, "URL" mode would limit the user to one form per page.
3846
# TODO: Test this with django-colorfield, django-ace, django-crispy-forms
39-
# TODO: Add pre-submit, post-submit, error, and success hooks
4047
# TODO: Add auto-save option for database-backed forms
4148
uuid_ref = hooks.use_ref(uuid4().hex.replace("-", ""))
4249
top_children_count = hooks.use_ref(len(top_children))
@@ -59,12 +66,17 @@ def _django_form(
5966
"Do NOT initialize your form by calling it (ex. `MyForm()`)."
6067
)
6168
raise TypeError(msg) from e
62-
raise e
69+
raise
6370

6471
# Run the form validation, if data was provided
6572
if submitted_data:
6673
initialized_form.full_clean()
67-
print("Form errors:", initialized_form.errors.as_data())
74+
success = not initialized_form.errors.as_data()
75+
form_event = FormEvent(form=initialized_form, data=submitted_data or {})
76+
if success and on_success:
77+
on_success(form_event)
78+
if not success and on_error:
79+
on_error(form_event)
6880

6981
def on_submit_callback(new_data: dict[str, Any]):
7082
"""Callback function provided directly to the client side listener. This is responsible for transmitting
@@ -74,6 +86,8 @@ def on_submit_callback(new_data: dict[str, Any]):
7486

7587
# TODO: The `use_state`` hook really should be de-duplicating this by itself. Needs upstream fix.
7688
if submitted_data != new_data:
89+
if on_submit:
90+
on_submit(FormEvent(form=initialized_form, data=new_data))
7791
set_submitted_data(new_data)
7892

7993
return html.form(

src/reactpy_django/types.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
if TYPE_CHECKING:
2020
from collections.abc import MutableMapping, Sequence
2121

22+
from django.forms import Form
23+
2224
from reactpy_django.websocket.consumer import ReactpyAsyncWebsocketConsumer
2325

2426

@@ -51,6 +53,14 @@ def __call__(self, *args: FuncParams.args, **kwargs: FuncParams.kwargs) -> None:
5153
self.execute(*args, **kwargs)
5254

5355

56+
@dataclass
57+
class FormEvent:
58+
"""State of a form provided to Form custom events."""
59+
60+
form: Form
61+
data: dict[str, Any]
62+
63+
5464
class AsyncPostprocessor(Protocol):
5565
async def __call__(self, data: Any) -> Any: ...
5666

0 commit comments

Comments
 (0)