Creating Web Components¶
Components allow you to build interactive web pages using Python and HTML without writing JavaScript. They function like mini-views that update instantly when users interact with them.
While standard Django views are static, components use HTMX to handle real-time updates—such as updating a list or a dashboard—without a full page refresh.
When to Use Components¶
Use components only when you need real-time interactivity. Static HTML and standard Django views are preferred for simpler pages. Ideal use cases include:
- Dynamic submission forms that change based on user input.
- Dashboards requiring real-time data updates.
- Interactive scientific elements like molecule sketchers or live filters.
How It Works¶
All interactive components are centered on the HtmxComponent class. Each component consists of a Python Class (logic) and an HTML Template (layout).
Registration and Rendering¶
- Discovery: Components are automatically found if placed in your app's
components/directory. Simmate converts class names (e.g.,TodoComponent) to tags (e.g.,todo-component). - Initial Render: Using
{% htmx_component 'todo-component' %}initializes the class, runs themount()method, and renders the HTML. - Caching: The instance is cached with a unique
component_id. This allows Simmate to remember the component's state across multiple interactions.
Managing State and Input¶
- Persistent State: Any attribute defined on your class (e.g.,
self.tasks) stays in memory as long as the user remains on the page. - User Inputs (
form_data): Values from tags like{% htmx_text_input %}are automatically synced to theself.form_datadictionary. Use this to read user input or reset fields after an action. - Interactivity: When a user clicks a button or interacts with an element, an AJAX request is sent. Simmate retrieves the cached object, updates
form_data, executes the requested Python method, and re-renders the component instantly.
Basic Example: A Todo List¶
In your app, set up the following structure. Note that templates are namespaced within a folder named after your app:
my_app/
├── components/
│ ├── __init__.py
│ └── todo.py
├── templates/
│ └── my_app/
│ ├── todo.html
│ └── home.html
├── urls.py
└── views.py
1. The Python Logic¶
Define your state and methods in my_app/components/todo.py:
from simmate.website.htmx.components import HtmxComponent
class TodoComponent(HtmxComponent):
template_name = "my_app/todo.html"
def mount(self):
"""Initializes state when the component first renders."""
self.tasks = []
def add_task(self):
"""Logic triggered by a button click."""
new_task = self.form_data.get("task")
if new_task:
self.tasks.append(new_task)
self.form_data["task"] = "" # Clear the input field
2. The Component Template¶
Create the layout in my_app/templates/my_app/todo.html:
{% extends "htmx/form_base.html" %}
{% block form %}
<h3>My Todo List</h3>
{% htmx_text_input name="task" placeholder="Enter a new task..." %}
{% htmx_button method_name="add_task" label="Add Task" %}
<ul>
{% for t in component.tasks %}
<li>{{ t }}</li>
{% empty %}
<li>No tasks yet!</li>
{% endfor %}
</ul>
{% endblock %}
3. Usage in a View¶
Render the component in your main template (my_app/templates/my_app/home.html):
{% extends "core/site_base.html" %}
{% block body %}
<div class="container">
{% htmx_component 'todo-component' %}
</div>
{% endblock %}
Lifecycle Hooks¶
Override these methods to control component behavior:
mount(): Called once during the first initialization.pre_parse(): Called before AJAX POST data is processed.post_parse(): Called after POST data is applied toself.form_data.process(): The default method triggered if an interactive element (like a button) does not specify amethod_name.