# Components
You often have a snippet of templating that you'd like to re-use.
Many existing templating systems have "macros" for this: units of templating
that can be re-used and called from other templates.
The whole mechanism, though, is quite magical:
- Where do the macros come from? Multiple layers of context magic and specially
named directories provide the answer.
- What macros are available at the cursor position I'm at in a template? It's
hard for an editor or IDE to predict and provide autocomplete.
- What are the macros arguments and what is this template's special syntax for
providing them? And can my editor help on autocomplete or tell me when I got
it wrong (or the macro changed its signature)?
- How does my current scope interact with the macro's scope, and where does it
get other parts of its scope from?
The `tdom` package makes this more Pythonic through the use of "components."
Instead of some sorta-callable, a component is a normal Python callable: a
function with normal Python arguments and return values.
## Simple Heading
Here is a component callable — a `Heading` function — which returns
a `Node`:
```python
def Heading() -> Template:
return t"
My Title
"
result = html(t"<{Heading} />")
assert str(result) == 'My Title
'
```
## Simple Props
As expected, components can have props, passed in as what looks like HTML
attributes. Here we pass a `title` as an argument to `Heading`, using a simple
HTML attribute string value:
```python
def Heading(title: str) -> Template:
return t"{title}
"
result = html(t'<{Heading} title="My Title">{Heading}>')
assert str(result) == 'My Title
'
```
## Children As Props
If your template has children inside the component element, your component will
receive them as a keyword argument:
```python
def Heading(children: Iterable[Node], title: str) -> Node:
return html(t"{title}
{children}
")
result = html(t'<{Heading} title="My Title">Child{Heading}>')
assert str(result) == 'My Title
Child
'
```
Note how the component closes with `{Heading}>` when it contains nested
children, as opposed to the self-closing form in the first example. If no
children are provided, the value of children is an empty tuple.
Note also that components functions can return `Node` or `Template` values as
they wish. Iterables of nodes and templates are also supported.
The component does not have to list a `children` keyword argument. If it is
omitted from the function parameters and passed in by the usage, it is silently
ignored:
```python
def Heading(title: str) -> Node:
return html(t"{title}
Ignore the children.
")
result = html(t'<{Heading} title="My Title">Child{Heading}>')
assert str(result) == 'My Title
Ignore the children.
'
```
## Optional Props
Since this is typical function-argument stuff, you can have optional props
through argument defaults:
```python
def Heading(title: str = "My Title") -> Template:
return t"{title}
"
result = html(t"<{Heading} />")
assert str(result) == 'My Title
'
```
## Passsing Another Component as a Prop
Here's a useful pattern: you can pass a component as a "prop" to another
component. This lets the caller (in this case, the `result` line) do the
driving:
```python
def DefaultHeading() -> Template:
return t"Default Heading
"
def Body(heading: Callable) -> Template:
return t"<{heading} />"
result = html(t"<{Body} heading={DefaultHeading} />")
assert str(result) == 'Default Heading
'
```
## Default Component for Prop
As a variation, let the caller do the driving but make the prop default to a
default component if none was provided:
```python
def DefaultHeading() -> Template:
return t"Default Heading
"
def OtherHeading() -> Template:
return t"Other Heading
"
def Body(heading: Callable) -> Template:
return html(t"<{heading} />")
result = html(t"<{Body} heading={OtherHeading}>{Body}>")
assert str(result) == 'Other Heading
'
```
## Conditional Default
One final variation for passing a component as a prop... move the "default or
passed-in" decision into the template itself:
```python
def DefaultHeading() -> Template:
return t"Default Heading
"
def OtherHeading() -> Template:
return t"Other Heading
"
def Body(heading: Callable | None = None) -> Template:
return t"<{heading if heading else DefaultHeading} />"
result = html(t"<{Body} heading={OtherHeading}>{Body}>")
assert str(result) == 'Other Heading
'
```
## Generators as Components
You can also have components that act as generators. For example, imagine you
have a todo list. There might be a lot of todos, so you want to generate them in
a memory-efficient way:
```python
def Todos() -> Iterable[Template]:
for todo in ["first", "second", "third"]:
yield t"{todo}"
result = html(t"")
assert str(result) == ''
```
## Nested Components
Components can be nested just like any other templating or function call:
```python
def Todo(label: str) -> Template:
return t"{label}"
def TodoList(labels: Iterable[str]) -> Template:
return t"{[Todo(label) for label in labels]}
"
title = "My Todos"
labels = ["first", "second", "third"]
result = html(t"{title}
<{TodoList} labels={labels} />")
assert str(result) == 'My Todos
'
```