move
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# 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
|
||||
|
||||
- NiceGUI action events: https://nicegui.io/documentation/section_action_events
|
||||
- FastAPI SSE: https://fastapi.tiangolo.com/advanced/server-sent-events/
|
||||
- FastAPI WebSockets: https://fastapi.tiangolo.com/advanced/websockets/
|
||||
Reference in New Issue
Block a user