diff --git a/tutorials/02-data-tables.md b/tutorials/02-data-tables.md new file mode 100644 index 0000000..ca618b0 --- /dev/null +++ b/tutorials/02-data-tables.md @@ -0,0 +1,135 @@ +**`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 +![Data Tables Demo](../assets/data-tables-demo.gif) +``` + +--- + +## 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. + +--- + +git add tutorials/01-hello-fluid-frame.md tutorials/02-data-tables.md +git commit -m "feat(tutorials): add Hello Fluid Frame + Data Tables with persistence, querying, and RepeatingPanel display" +git push origin main +```` + +--- + +If you want, I can also make **Tutorial 03 — Server Modules & Security** a bit more difficult by adding role-based access control so that only logged-in users with a certain role can call a sensitive server function. That would round out the trio. Want me to do that next?