Conditions and loops
Citry can show content when a Python expression is true and repeat content for every item in an iterable. Each tool has two forms:
- Put a shorthand attribute on one HTML or component tag.
- Use a built-in tag around a larger piece of markup.
Choose the form that keeps the resulting HTML easiest to see.
Show one element conditionally
Put c-if directly on the element you want to show:
<p c-if="account.is_active">Your account is active.</p>
Citry evaluates the value as a Python expression. When it is truthy, the entire <p> renders. When it is falsy, nothing from that element renders.
Add adjacent c-elif and c-else elements when there are several possible results:
<p c-if="role == 'admin'">Administrator tools</p>
<p c-elif="role == 'editor'">Editor tools</p>
<p c-else>Reader account</p>
Citry renders the first truthy branch and skips the rest. Conditions use normal Python truthiness: a non-empty list is truthy and an empty list is falsy. c-else is bare and must not have a value.
Wrap several elements in a condition
Use the tag form when one branch contains several elements:
<c-if cond="account.is_active">
<h2>Welcome back</h2>
<p>Your account is ready.</p>
</c-if>
<c-else>
<c-ActivationHelp />
</c-else>
The condition belongs in cond="..." without {{ }}. <c-elif> also takes a cond attribute; <c-else> does not.
For compatibility, the tag form treats a bare cond or cond="" as True. Prefer an explicit expression so the intent is visible. The shorthand c-if and c-elif forms require a non-empty expression value.
Every elif and else branch must immediately follow the branch before it. Formatting whitespace and template comments between branches are fine:
<p c-if="ready">Ready</p>
{# Explain why the fallback exists. #}
<p c-else>Still working</p>
Template comments do not render, so neither they nor the formatting whitespace around them becomes part of a branch. Text, an HTML comment, an expression, or another element between branches does produce content, so it breaks the chain and is an error. The same rule applies between for and empty branches.
Repeat one element
Put c-for on the element you want to repeat:
<ul>
<li c-for="book in books">{{ book.title }}</li>
<li c-empty>No books yet.</li>
</ul>
The first <li> renders once for each book. The adjacent c-empty element renders only when the loop produces no rows. c-empty is bare and must not have a value.
The loop name is available everywhere on the repeated element, including its other attributes:
<li
c-for="book in books"
c-class="{'featured': book.is_featured}"
>
{{ book.title }}
</li>
Repeat a larger block
Use <c-for> when each item needs several sibling elements:
<c-for each="book in books">
<h2>{{ book.title }}</h2>
<p>{{ book.summary }}</p>
</c-for>
<c-empty>
<p>No books yet.</p>
</c-empty>
Put the loop clause in each="...", again without {{ }}.
Unpack and filter values
The loop clause uses Python comprehension syntax. You can unpack each item:
<c-for each="name, score in scores.items()">
<p>{{ name }}: {{ score }}</p>
</c-for>
You can also filter rows:
<c-for each="book in books if book.is_available">
<p>{{ book.title }}</p>
</c-for>
Multiple for clauses work too:
<c-for
each="shelf in shelves for book in shelf.books"
>
<p>{{ shelf.name }}: {{ book.title }}</p>
</c-for>
c-empty renders when the complete comprehension produces no rows, including when a filter removes every item.
The names you bind exist only inside the loop. Citry does not provide a special loop object, so there is no loop.index or loop.first. Prepare an index in Python, then unpack it in the template:
<!-- indexed_books = list(enumerate(books, start=1)) -->
<p c-for="number, book in indexed_books">
{{ number }}. {{ book.title }}
</p>
Loop targets must be unique and may not replace a name that is already visible. If the template already has a book variable, bind this loop to a different name. This is rejected even when the iterable is empty.
Async comprehensions are not supported.
Combine a condition and a loop carefully
You may put c-if and c-for on the same element:
<p c-if="show_books" c-for="book in books">
{{ book.title }}
</p>
The condition is outside the loop. Citry checks show_books once, before it starts iterating, so the condition cannot use book.
An adjacent c-empty cannot attach to this combined shorthand because the outer condition separates it from the loop. When you need both a condition and an empty state, make the order visible with nested tags:
<c-if cond="show_books">
<c-for each="book in books">
<p>{{ book.title }}</p>
</c-for>
<c-empty>No books yet.</c-empty>
</c-if>
Read Expressions for the Python you can use in a condition or loop clause. Read Attributes for the other dynamic attributes you can put on a repeated element.