Templates in PyWeber¶
Templates are the core building blocks of PyWeber applications. They represent HTML pages or components that can be dynamically manipulated through Python code.
Related guides
- Element model —
{{placeholders}}in HTML files - Reactivity —
e.update()after changes
Creating Templates¶
A template in PyWeber can be created from an HTML file or from an HTML string:
import pyweber as pw
# From an HTML file in the templates directory
class HomePage(pw.Template):
def __init__(self, app: pw.Pyweber):
super().__init__(template="home.html")
# Initialize elements and events
self.setup()
def setup(self):
# Select and manipulate elements
self.title = self.querySelector("h1")
self.button = self.querySelector("#action-button")
# Add event handlers
self.button.events.onclick = self.handle_click
def handle_click(self, e: pw.EventHandler):
self.title.content = "Button was clicked!"
e.update() # Update the UI
# From an HTML string
class SimpleTemplate(pw.Template):
def __init__(self, app: pw.Pyweber):
super().__init__(template="<div><h1>Simple Template</h1><button id='action-button'>Click Me</button></div>")
# Initialize as before
self.setup()
Dynamic Templates (New in 0.8.4)¶
PyWeber 0.8.4 introduces support for dynamic templates, allowing you to inject values into your templates at runtime:
import pyweber as pw
class DynamicTemplate(pw.Template):
def __init__(self, app: pw.Pyweber, username: str):
# Pass dynamic values to the template
super().__init__(
template="welcome.html",
username=username,
current_date="April 6, 2025"
)
In your HTML template (welcome.html):
You can also inject Elements into your templates:
import pyweber as pw
class CompositeTemplate(pw.Template):
def __init__(self, app: pw.Pyweber):
# Create a navigation element
nav = pw.Element(
tag="nav",
classes=["main-nav"]
)
# Add items to navigation
nav.childs.append(pw.Element(tag="a", content="Home", attrs={"href": "/"}))
nav.childs.append(pw.Element(tag="a", content="About", attrs={"href": "/about"}))
# Pass the element to the template
super().__init__(
template="layout.html",
navigation=nav
)
In your HTML template (layout.html):
Template Class API¶
Constructor¶
Template(template: str, status_code: int = 200, **kwargs)
template: Path to an HTML file (relative to the templates directory) or an HTML stringstatus_code: HTTP status code to return when this template is rendered (default: 200)**kwargs: Dynamic values to inject into the template (new in 0.8.4)
Properties¶
| Property | Type | Description |
|---|---|---|
template |
str | The raw HTML template string |
root |
Element | The root element of the parsed HTML |
status_code |
int | The HTTP status code for this template |
events |
dict | Dictionary of registered event handlers |
data |
Any | Custom data that can be attached to the template |
Element Selection Methods¶
PyWeber provides several methods to select elements within a template:
querySelector¶
Selects the first element that matches the CSS selector.
# Select by ID
button = template.querySelector("#submit-button")
# Select by class
title = template.querySelector(".main-title")
# Select by tag name
paragraph = template.querySelector("p")
querySelectorAll¶
Selects all elements that match the CSS selector.
# Select all paragraphs
paragraphs = template.querySelectorAll("p")
# Select all elements with a specific class
items = template.querySelectorAll(".item")
getElement / getElements (since 1.0.2)¶
from pyweber.utils.types import GetBy
# Single element by id, class, attrs, or style
button = template.getElement(by=GetBy.ID, value="submit-button")
items = template.getElements(by=GetBy.CLASSES, value="item")
Removed in 1.0.2
getElementById, getElementByClass, and getElementByUUID were removed. Use getElement / getElements with GetBy instead.
HTML Manipulation¶
parse_html¶
Parses HTML into an Element tree. If no HTML is provided, uses the template's HTML.
# Parse new HTML and replace the current template
new_element = template.parse_html("<div><h1>New Content</h1></div>")
template.root = new_element
build_html¶
Builds HTML string from the Element tree. If no element is provided, uses the template's root element.
# Get the current HTML
html = template.build_html()
# Get HTML for a specific element
div_html = template.build_html(template.querySelector("div"))
Working with Elements¶
Elements in a template can be manipulated after selection:
# Change content
title = template.querySelector("h1")
title.content = "New Title"
# Change attributes
image = template.querySelector("img")
image.set_attr("src", "/images/new-image.jpg")
image.set_attr("alt", "New Image")
# Change styles
button = template.querySelector("button")
button.set_style("background-color", "blue")
button.set_style("color", "white")
# Work with classes
button.add_class("primary-button")
button.remove_class("disabled")
button.toggle_class("active") # Add if not present, remove if present
HTML Comments (New in 0.8.4)¶
PyWeber 0.8.4 adds support for HTML comments through a special 'comment' tag:
# Create a comment element
comment = pw.Element(
tag="comment",
content="This is a comment that will be rendered as <!-- This is a comment -->"
)
# Add it to the DOM
container.childs.append(comment)
html = """
<div>
<!-- This is a comment -->
<p>This is a paragraph</p>
</div>
"""
element = template.parse_html(html)
# The comment is accessible as a 'comment' tag element
Element Properties and Methods¶
Elements have various properties and methods for manipulation:
Properties¶
| Property | Type | Description |
|---|---|---|
tag |
str | The HTML tag name |
id |
str | The element's ID attribute |
content |
str | The text content of the element |
value |
str | The value attribute (for form elements) |
classes |
list[str] | List of CSS classes |
style |
dict[str, str] | Dictionary of inline styles |
attrs |
dict[str, str] | Dictionary of HTML attributes |
childs |
list[Element] | List of child elements |
events |
TemplateEvents | Event handlers for this element |
parent |
Element | The parent element |
data |
Any | Custom data that can be attached to the element |
Methods¶
Class Management¶
add_class(class_name: str)
remove_class(class_name: str)
has_class(class_name: str) -> bool
toggle_class(class_name: str)
Style Management¶
Attribute Management¶
Child Management¶
Element Selection¶
querySelector(selector: str) -> Element
querySelectorAll(selector: str) -> list[Element]
getElement(by: GetBy, value: str) -> Element
getElements(by: GetBy, value: str) -> list[Element]
Handling Events¶
Events in PyWeber are handled by Python functions:
class ContactForm(pw.Template):
def __init__(self, app: pw.Pyweber):
super().__init__(template="contact_form.html")
# Select form elements
self.form = self.querySelector("form")
self.name_input = self.querySelector("#name")
self.email_input = self.querySelector("#email")
self.submit_button = self.querySelector("#submit")
self.result = self.querySelector("#result")
# Bind events
self.submit_button.events.onclick = self.handle_submit
self.name_input.events.oninput = self.validate_name
def validate_name(self, e: pw.EventHandler):
name = self.name_input.value
if len(name) < 3:
self.name_input.set_style("border-color", "red")
else:
self.name_input.set_style("border-color", "green")
e.update()
def handle_submit(self, e: pw.EventHandler):
name = self.name_input.value
email = self.email_input.value
if len(name) < 3 or "@" not in email:
self.result.content = "Please check your inputs"
self.result.set_style("color", "red")
else:
self.result.content = f"Thank you, {name}! We'll contact you at {email}"
self.result.set_style("color", "green")
e.update()
Adding Events¶
You can add events to elements in several ways:
# Method 1: Using events property
button.events.onclick = self.handle_click
# Method 2: Using add_event method
from pyweber.utils.types import EventType
button.add_event(EventType.CLICK, self.handle_click)
# Method 3: During element creation
button = pw.Element(
tag="button",
content="Click Me",
events=pw.TemplateEvents(
onclick=self.handle_click
)
)
Removing Events¶
# Remove a specific event
button.remove_event(EventType.CLICK)
# Reset all events
button.events = pw.TemplateEvents()
Dynamic Element Creation¶
You can create and modify elements dynamically:
def add_item(self, e: pw.EventHandler):
# Get the container
container = self.querySelector("#items-container")
# Create a new item
new_item = pw.Element(
tag="div",
classes=["item"],
content=f"Item {len(container.childs) + 1}"
)
# Create a delete button
delete_btn = pw.Element(
tag="button",
classes=["delete-btn"],
content="×"
)
# Add event to the delete button
delete_btn.events.onclick = self.delete_item
# Add button to item
new_item.add_child(delete_btn)
# Add to container
container.childs.append(new_item)
e.update()
def delete_item(self, e: pw.EventHandler):
# Prefer e.target (e.element is deprecated)
button = e.target
# Get the parent item
item = button.parent
# Get the container
container = item.parent
# Remove the item from the container
container.remove_child(item)
e.update()
Example: Todo List Application with Dynamic Templates¶
Here's a complete example of a simple todo list application using dynamic templates:
import pyweber as pw
class TodoList(pw.Template):
def __init__(self, app: pw.Pyweber, username: str = "User"):
super().__init__(
template="<div class='container'><h1>{{username}}'s Todo List</h1><div class='input-group'><input id='new-todo' type='text' placeholder='Add new item'><button id='add-button'>Add</button></div><ul id='todo-list'></ul><!-- This is a comment that will be properly handled --></div>",
username=username
)
self.input = self.querySelector("#new-todo")
self.add_button = self.querySelector("#add-button")
self.todo_list = self.querySelector("#todo-list")
# Initialize empty list
self.todos = []
# Add event handlers
self.add_button.events.onclick = self.add_todo
self.input.events.onkeydown = self.handle_key
def handle_key(self, e: pw.EventHandler):
if e.event_data.key == "Enter":
self.add_todo(e)
def add_todo(self, e: pw.EventHandler):
text = self.input.value
if text:
# Create new list item
item = pw.Element(
tag="li",
classes=["todo-item"]
)
# Create text span
text_span = pw.Element(
tag="span",
content=text,
classes=["todo-text"]
)
# Create delete button
delete_btn = pw.Element(
tag="button",
content="×",
classes=["delete-btn"]
)
delete_btn.events.onclick = self.delete_todo
# Add elements to item
item.add_child(text_span)
item.add_child(delete_btn)
# Add to list
self.todo_list.childs.append(item)
self.todos.append(text)
# Clear input
self.input.value = ""
e.update()
def delete_todo(self, e: pw.EventHandler):
button = e.target
# Get the parent item
item = button.parent
# Find the text span
text_span = item.querySelector(".todo-text")
# Remove from our data
if text_span.content in self.todos:
self.todos.remove(text_span.content)
# Remove from DOM
self.todo_list.remove_child(item)
e.update()
def main(app: pw.Pyweber):
app.add_route("/", template=TodoList(app=app, username="John"))
if __name__ == "__main__":
pw.run(target=main)
Best Practices¶
- Organize Templates: Create separate template classes for different pages or components
- Meaningful Names: Use descriptive names for element variables
- Separation of Concerns: Keep event handlers focused on specific tasks
- Update the UI: Always call
e.update()after making changes to ensure the UI is refreshed - Reuse Components: Create reusable template components for common UI elements
- Validate Input: Always validate user input before processing
- Use Dynamic Templates: Leverage the new dynamic template feature for cleaner code (new in 0.8.4)
- Handle Comments Properly: Use the new comment tag for adding comments to your templates (new in 0.8.4)
Next Steps¶
- Element model guide
- Elements in detail
- Routing advanced to connect templates to URLs
- Event handling for interactive applications