**`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. ---