127 lines
3.0 KiB
Markdown
127 lines
3.0 KiB
Markdown
**`tutorials/02-data-tables.md`**
|
||
|
||
````markdown
|
||
# 02 — Data Tables: Storing Greetings Like a Responsible Adult
|
||
|
||
**Goal:** Save each greeting into Anvil’s built-in database (Data Tables) and show them in a list.
|
||
|
||
---
|
||
|
||
## 1. What You’ll Build
|
||
- TextBox + Button → Server
|
||
- Server saves greeting in a table
|
||
- Client displays all saved greetings in a table view (RepeatingPanel)
|
||
|
||
---
|
||
|
||
## 2. Set Up Data Tables
|
||
1. In your Anvil app → **Data Tables** (left sidebar) → **+ Add Table**
|
||
- Name: `greetings`
|
||
- Columns:
|
||
- `name` (Text)
|
||
- `message` (Text)
|
||
- `ts` (Datetime, Default: `now()`)
|
||
2. Click **Publish Changes**.
|
||
|
||
---
|
||
|
||
## 3. Server-Side Code
|
||
Edit `ServerModule1`:
|
||
```python
|
||
from anvil import tables as t, tables
|
||
import anvil.server
|
||
from datetime import datetime
|
||
|
||
@anvil.server.callable
|
||
def save_greeting(name, message):
|
||
"""Saves a greeting into the Data Table."""
|
||
tables.app_tables.greetings.add_row(
|
||
name=name,
|
||
message=message,
|
||
ts=datetime.now()
|
||
)
|
||
return f"Greeting saved for {name} at {datetime.now().strftime('%H:%M:%S')}"
|
||
|
||
@anvil.server.callable
|
||
def get_greetings():
|
||
"""Returns all greetings, newest first."""
|
||
return tables.app_tables.greetings.search(
|
||
tables.order_by("ts", ascending=False)
|
||
)
|
||
````
|
||
|
||
---
|
||
|
||
## 4. Client-Side Code
|
||
|
||
Modify `Form1` from Tutorial 01:
|
||
|
||
### a) Add a RepeatingPanel
|
||
|
||
1. Drag a **RepeatingPanel** onto `Form1` (ID: `repeating_panel_1`).
|
||
2. Bind its `items` property in code.
|
||
|
||
### b) Update Button Click
|
||
|
||
```python
|
||
def button_1_click(self, **event_args):
|
||
self.button_1.enabled = False
|
||
try:
|
||
name = self.text_box_1.text or "World"
|
||
message = anvil.server.call('hello_server', name)
|
||
Notification(message, timeout=2).show()
|
||
# Save greeting
|
||
save_status = anvil.server.call('save_greeting', name, message)
|
||
print("[Client] Save status:", save_status)
|
||
# Refresh list
|
||
self.refresh_greetings()
|
||
finally:
|
||
self.button_1.enabled = True
|
||
|
||
def refresh_greetings(self):
|
||
greetings = anvil.server.call('get_greetings')
|
||
self.repeating_panel_1.items = greetings
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Create a Row Template for RepeatingPanel
|
||
|
||
1. Double-click the RepeatingPanel → Create **FormTemplate1**.
|
||
2. Add three Labels:
|
||
|
||
* `label_name` → Bind text: `self.item['name']`
|
||
* `label_message` → Bind text: `self.item['message']`
|
||
* `label_ts` → Bind text: `self.item['ts']`
|
||
3. Style it so it doesn’t look like a 90s spreadsheet.
|
||
|
||
---
|
||
|
||
## 6. Bonus Challenge (Optional)
|
||
|
||
* Add a “Clear All” button (server function to delete all rows).
|
||
* Add pagination (limit query results, add “Load More” button).
|
||
* Highlight greetings from the last minute in green.
|
||
|
||
---
|
||
|
||
## 7. Assets
|
||
|
||
* **GIF:** Show saving multiple greetings and the list updating.
|
||
* Save as `/assets/data-tables-demo.gif`
|
||
* Reference here:
|
||
|
||
```markdown
|
||

|
||
```
|
||
|
||
---
|
||
|
||
## 8. Takeaways
|
||
|
||
* Data Tables are like SQLite without the SQLite drama.
|
||
* Always validate server inputs before saving.
|
||
* Query ordering matters — newest first keeps users happy.
|
||
|
||
---
|