Use data in components
Most components show data that comes from Python: a title, a list of records, a count. In this step you build a reading list whose heading, books, and book count all come from Python, and you learn how to show, repeat, and hide parts of the HTML based on that data.
If you have not built a component yet, start with Your first component.
Build the reading list
Save this as reading_list.py:
from citry import Component
class ReadingList(Component):
class Kwargs:
books: list[str]
heading: str = "Reading list"
empty_message: str = "Your list is empty."
show_count: bool = True
class Slots:
pass
def template_data(self, kwargs: Kwargs, slots: Slots):
return {
"books": kwargs.books,
"heading": kwargs.heading,
"empty_message": kwargs.empty_message,
"show_count": kwargs.show_count,
"total": len(kwargs.books),
}
template = """
<section>
<h2>{{ heading }}</h2>
<p c-if="show_count and total > 0">
{{ total }} {{ "book" if total == 1 else "books" }}
</p>
<ul c-data-count="total">
<li c-for="book in books">{{ book }}</li>
<li c-empty>{{ empty_message }}</li>
</ul>
</section>
"""
reading_list = ReadingList(
heading="Books for the weekend",
books=["A Wizard of Earthsea", "Kindred", "Piranesi"],
)
if __name__ == "__main__":
print(reading_list)
reading_list
Run it:
python reading_list.py
The result contains this list:
<h2>Books for the weekend</h2>
<p>3 books</p>
<ul data-count="3">
<li>A Wizard of Earthsea</li>
<li>Kindred</li>
<li>Piranesi</li>
</ul>
Citry adds some attributes of its own, so the complete HTML is a little longer.
The file's last line, a bare reading_list, is what the Try live button shows. Python ignores it when you run the file.
Declare the options
Kwargs lists the named options people can give your component:
class Kwargs:
books: list[str]
heading: str = "Reading list"
empty_message: str = "Your list is empty."
show_count: bool = True
books has no default, so it is required. The other options have defaults, so this works:
ReadingList(books=["The Dispossessed"])
It uses βReading listβ as the heading and shows the count. Pass show_count=False to hide the count:
ReadingList(
books=["The Dispossessed"],
show_count=False,
)
Compute new values
By default, the template can use each Kwargs field by its own name. That is how {{ heading }} inserts the heading.
When the template needs a value that Python must compute, define template_data(). Here it adds total, the number of books:
def template_data(
self,
kwargs: Kwargs,
slots: Slots,
) -> dict[str, object]:
return {
"books": kwargs.books,
"heading": kwargs.heading,
"empty_message": kwargs.empty_message,
"show_count": kwargs.show_count,
"total": len(kwargs.books),
}
Once you define template_data(), the template sees only the names it returns. That is why it returns the Kwargs fields too.
Show data in the HTML
The template uses four small tools:
c-if="show_count and total > 0"leaves the count out when you hide it or when the list is empty. The value can be any Python expression.{{ "book" if total == 1 else "books" }}chooses the singular or plural word with a Python expression inside{{ }}.c-for="book in books"makes one<li>for every title.c-data-count="total"evaluatestotaland writes an ordinarydata-countHTML attribute.
If books is empty, the c-empty item appears instead:
<li>Your list is empty.</li>
Conditions and loops and Attributes cover the other forms when you need them.
Catch wrong options
If you leave out books, Citry reports the missing option when the component renders:
reading_list = ReadingList()
print(reading_list)
# TypeError: ReadingList.Kwargs.__init__() missing ... 'books'
A misspelled option is also rejected:
reading_list = ReadingList(books=[], heding="Typo")
print(reading_list)
# TypeError: ReadingList.Kwargs.__init__() got an unexpected
# keyword argument 'heding'. Did you mean 'heading'?
So is an option of the wrong type. books: list[str] rejects a single string:
reading_list = ReadingList(books="The Dispossessed")
print(reading_list)
# TypeError: ReadingList.Kwargs.books expects list[str], got
# str ('The Dispossessed'). ...
Citry checks the outer type only: it confirms that books is a list, but not that each item is a string. Inputs and validation explains what each annotation accepts and how to check more.
Next steps
You now have a component that turns Python data into HTML. Next, build a complete page from components and add a small render check.