# 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">') 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') assert str(result) == '

My Title

Child
' ``` Note how the component closes with `` 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') 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}>") 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}>") 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"" title = "My Todos" labels = ["first", "second", "third"] result = html(t"

    {title}

    <{TodoList} labels={labels} />") assert str(result) == '

    My Todos

    ' ```