Skip to content

Bulk_Update HTMX example - Adding new directory bulk_update with items required as per standard #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions examples/dynamic_user_interface_(htmx)/bulk_update/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from fasthtml.common import *
from collections import defaultdict

app, rt = fast_app()

default_data = [
{'id': 1, 'name': 'Alice', 'age': 25},
{'id': 2, 'name': 'Bob', 'age': 30},
{'id': 3, 'name': 'Charlie', 'age': 28},
]

data = defaultdict(lambda: [dict(d) for d in default_data])

@rt
def index(session):
# if no id, create one so diff users changes don't conflict
if not session.get('id'): session['id'] = unqid()

# Create a table based on the current users data
rows = []
for item in data[session['id']]:
rows.append(
Tr(Td(str(item['id'])),
Td(Input(value=item['name'], name=f"name{item['id']}", _id=f"name{item['id']}")),
Td(Input(value=str(item['age']), name=f"age{item['id']}", _id=f"age{item['id']}"))))

return Div(
Form(
Table(
Thead(Tr(map(Th, ('ID', 'Name', 'Age')))),
Tbody(*rows)),
# Bulk update button that submits all inputs from the table because it's inside fot form.
Button('Bulk Update', hx_post="update", hx_target='#response', hx_indicator="#loading", _type="button", hx_vals={'id': session['id']})),

# Response div that will be updated with the result of the bulk update
Div(id='response'),
# Loading indicator that will be shown when the bulk update is happening
Div(id="loading", style="display:none;", _class="loader"))

@rt
async def update(request, id:str):
changes = []
form_data = await request.form()

# Iterate over the items in the users data
for item in data[id]:
# Get the new name and age from the form data
new_name = form_data.get(f"name{item['id']}")
new_age = form_data.get(f"age{item['id']}")

# Check if the item has changed and if so add it to the changes list
if new_name != item['name'] or new_age != str(item['age']):
changes.append(f"Row {item['id']} changed: Name {item['name']} → {new_name}, Age {item['age']} → {new_age}")
item['name'] = new_name
item['age'] = int(new_age)

# Return the changes or a message if there are no changes
return Div(*[Div(change) for change in changes]) if changes else Div("No changes detected")

serve()
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions examples/dynamic_user_interface_(htmx)/bulk_update/info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Bulk Data Update with Fasthtml and HTMX

This example demonstrates how to create a simple web application using Fasthtml and HTMX that allows users to bulk update data displayed in an HTML table. Users can modify the values in the table's input fields and then click a button to submit all the changes at once.

## Key Features

* **Dynamic Table Generation:** The HTML table is dynamically generated from a Python list of dictionaries. Each dictionary represents a row in the table.
* **Client-Side Updates (with HTMX):** The changes are submitted via an AJAX-like request using HTMX. This avoids a full page reload, providing a smoother user experience.
* **Server-Side Processing:** The updates are processed on the server using a Python function.
* **Data Persistence (In-Memory):** The example uses an in-memory data structure (a list of dictionaries) to store the data. In a real-world application, you would replace this with a database or other persistent storage.
* **Preventing Accidental Submissions:** The "Bulk Update" button uses `_type="button"` to prevent the form from being submitted when the user presses Enter in the input fields. This ensures that only a button click triggers the update process.

## How it Works

1. **Data Initialization:** The `data` list of dictionaries holds the initial data for the table.

2. **`index` Route (Table Display):**
- The `index` route function generates the HTML for the table.
- It iterates through the `data` list and creates table rows (`<tr>`) with cells (`<td>`).
- Each cell in the 'Name' and 'Age' columns contains an `<input>` element, allowing the user to edit the values.
- The table is wrapped in a `<form>` element.
- A "Bulk Update" button is included in the form. The `hx_post` attribute on the button specifies the route (`/update`) that will handle the form submission. The `hx_target` attribute specifies where the response from the server should be displayed (`#response`). `hx_indicator` shows a loading indicator while the request is in progress. `_type="button"` prevents form submission on Enter key press.

3. **`/update` Route (Data Processing):**
- The `update` route function handles the POST request when the "Bulk Update" button is clicked.
- It retrieves the form data using `await request.form()`.
- It iterates through the `data` list and compares the new values from the form with the original values.
- If a value has changed, it updates the `data` list and adds a message to the `changes` list.
- Finally, it returns a `Div` containing the messages about the changes.

## How to Use

1. Run the example.
2. The table will be displayed in your browser.
3. Edit the 'Name' and 'Age' values in the input fields as needed.
4. Click the "Bulk Update" button.
5. The changes will be processed, and a message will appear below the button indicating which rows were updated.

## Code Explanation (Key Parts)

```python
@rt # Root route
def index():
# ... (code to generate the table HTML)
return Div(
Form(
Table( #... ),
Button('Bulk Update', hx_post="update", hx_target='#response', hx_indicator="#loading", _type="button")
),
Div(id='response'), # Target for the server response
Div(id="loading", style="display:none;", _class="loader"), # Loading indicator
)

@rt("update", methods=["POST"])
async def update(request):
# ... (code to process the form data and update the data list)
return Div(*[Div(change) for change in changes]) if changes else Div("No changes detected")
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[REQUIRED]
ImageAltText=Bulk-Update
ComponentName=Bulk-Update
ComponentDescription=Demonstrates bulk updating of table data.
author = Shankhya
tags = forms, tables, data
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def mk_form(add_option:str=None, options:str='isaac,hamel,curtis'):
LabelInput("Add an Option", id="add_option"),
Button("Add"),
# fh-frankenui select allows for search boxes
UkSelect(map(Option, opts), searchable=True),
Select(map(Option, opts), searchable=True),
# When the "Add" button is pressed, make a new form
get=mk_form,
# Store options state in DOM
Expand Down
137 changes: 0 additions & 137 deletions examples/todo_series/_kanban/app.py

This file was deleted.

Binary file removed examples/todo_series/_kanban/card_thumbnail.gif
Binary file not shown.
Binary file removed examples/todo_series/_kanban/card_thumbnail.png
Binary file not shown.
49 changes: 0 additions & 49 deletions examples/todo_series/_kanban/info.md

This file was deleted.

4 changes: 0 additions & 4 deletions examples/todo_series/_kanban/metadata.ini

This file was deleted.

Loading