111 lines
2.8 KiB
Markdown
111 lines
2.8 KiB
Markdown
# Interaction Patterns Reference
|
|
|
|
## Reactive State
|
|
|
|
Use bindable dataclasses for local page state.
|
|
|
|
```python
|
|
from dataclasses import field
|
|
from nicegui import binding, ui
|
|
|
|
@binding.bindable_dataclass
|
|
class PageState:
|
|
selected_id: int | None = None
|
|
items: list = field(default_factory=list)
|
|
|
|
state = PageState()
|
|
ui.label().bind_text_from(state, "selected_id")
|
|
```
|
|
|
|
## File Upload Pattern
|
|
|
|
- Validate extension and size before storing.
|
|
- Delegate storage to a service method.
|
|
- Notify success and failure explicitly.
|
|
|
|
```python
|
|
async def handle_upload(e: ui.events.UploadEventArguments):
|
|
try:
|
|
if e.size > 10 * 1024 * 1024:
|
|
raise ValueError("File too large")
|
|
if not e.name.endswith(".pdf"):
|
|
raise ValueError("Only PDF allowed")
|
|
await file_service.store(e.content.read(), e.name)
|
|
ui.notify(f"Uploaded: {e.name}", type="positive")
|
|
except ValueError as err:
|
|
ui.notify(str(err), type="negative")
|
|
|
|
ui.upload(on_upload=handle_upload, auto_upload=True)
|
|
```
|
|
|
|
## Form Submission Pattern
|
|
|
|
- Bind UI inputs to dataclass fields.
|
|
- Perform validation in the service layer.
|
|
- Clear form state on success.
|
|
|
|
```python
|
|
@binding.bindable_dataclass
|
|
class FormData:
|
|
name: str = ""
|
|
email: str = ""
|
|
|
|
data = FormData()
|
|
ui.input("Name").bind_value(data, "name")
|
|
ui.input("Email").bind_value(data, "email")
|
|
|
|
async def on_submit():
|
|
try:
|
|
await user_service.create_user(name=data.name, email=data.email)
|
|
ui.notify("User created", type="positive")
|
|
data.name = data.email = ""
|
|
except ValueError as err:
|
|
ui.notify(str(err), type="negative")
|
|
|
|
ui.button("Submit").on_click(on_submit)
|
|
```
|
|
|
|
## Real-Time Updates Decision
|
|
|
|
Use SSE for one-way status streaming.
|
|
Use WebSocket for bidirectional messaging.
|
|
|
|
SSE endpoint example:
|
|
|
|
```python
|
|
@app.get("/events/status")
|
|
async def status_stream():
|
|
async def gen():
|
|
while True:
|
|
yield f"data: {await get_status()}\\n\\n"
|
|
await asyncio.sleep(1)
|
|
return StreamingResponse(gen(), media_type="text/event-stream")
|
|
```
|
|
|
|
## Background Work Pattern
|
|
|
|
- Start long jobs in FastAPI background tasks.
|
|
- Expose status via endpoint or streaming channel.
|
|
- Guard buttons against duplicate submissions during in-flight tasks.
|
|
|
|
## Explicit Refresh Pattern
|
|
|
|
Use @ui.refreshable and call refresh intentionally instead of polling unrelated state.
|
|
|
|
```python
|
|
@ui.refreshable
|
|
async def item_list():
|
|
items = await service.list()
|
|
for item in items:
|
|
ui.label(item.name)
|
|
|
|
ui.button("Refresh").on_click(lambda: item_list.refresh())
|
|
```
|
|
|
|
## Links
|
|
|
|
!!! info "Primary sources"
|
|
- [NiceGUI action events](https://nicegui.io/documentation/section_action_events)
|
|
- [FastAPI server-sent events](https://fastapi.tiangolo.com/advanced/server-sent-events/)
|
|
- [FastAPI WebSockets](https://fastapi.tiangolo.com/advanced/websockets/)
|