Static String#
Let’s look at some non-dynamic uses of templating to learn the basics.
Render a String Literal#
Let’s start with the simplest form of templating: just a string, no tags, no attributes: For the purposes of illustration, we do the VDOM in one step and the rendering in a second.
from viewdom import html
from viewdom import render
def main() -> str:
"""Main entry point."""
vdom = html("Hello World")
result = render(vdom)
return result
We start by importing the html
function from viewdom
.
This is, essentially, htm.py
in action.
It takes a “tagged” template string and returns a VDOM.
The render
function, imported from vdom
, does the rendering.
Simple Render#
Same thing, but in a <div>
: nothing dynamic, just “template” a string of HTML, but done in one step:
def main() -> str:
"""Main entry point."""
result = render(html("<div>Hello World</div>"))
return result
We get back a VDOM
– an optimized dataclass – with:
The name of the “tag” (
<div>
)The properties passed to that tag (in this case, an empty dict)
The children of this tag (in this case, a text node of
Hello World
)
The second item in the VDOM tuple – the props dictionary – now has a key of ‘class’ with the assigned class name value.
Show the VDOM Itself#
Let’s take a look at that VDOM structure. This time, we’ll return the VDOM rather than rendering it to a string:
def main() -> str:
"""Main entry point."""
result = html('<div class="container">Hello World</div>')
return result
In our test we see that we got back a VDOM
– an optimized dataclass:
assert main() == VDOMNode(
tag="div", props={"class": "container"}, children=["Hello World"]
)
What does it look like?
The name of the “tag” (
<div>
)The properties passed to that tag (in this case, an empty dict)
The children of this tag (in this case, a text node of
Hello World
)
The second item in the VDOM tuple – the props dictionary – now has a key of ‘class’ with the assigned class name value.
Expressions as Attribute Values#
We can go one step further with this and use a little bit of “expressions”. Let’s pass in a Python symbol as part of the template, inside curly braces:
def main() -> str:
"""Main entry point."""
vdom = html('<div class="container{1}">Hello World</div>')
result = render(vdom)
return result
Everything is the same, except the value of the class
prop now has a Python int
included in the string.
If it looks like Python f-strings
, well, that’s the point.
We did an expression inside that prop value, using a Python expression that evaluated to just the number 1
.
Shorthand Syntax#
As a shorthand, when the entire attribute value is an expression, you can just use curly braces instead of putting in double quotes:
def main() -> str:
"""Main entry point."""
vdom = html('<div class={"Container1".lower()}>Hello World</div>')
result = render(vdom)
return result
The VDOM’s third item contains the “children”.
Child Nodes in VDOM#
Let’s look at what more nesting would look like:
def main() -> VDOMNode:
"""Main entry point."""
vdom = html("<div>Hello <span>World<em>!</em></span></div>")
return vdom
Over in the test, we see what this looks like:
assert main() == VDOMNode(
tag="div",
props={},
children=[
"Hello ",
VDOMNode(
tag="span",
props={},
children=["World", VDOMNode(tag="em", props={}, children=["!"])],
),
],
)
It’s a nested Python datastructure – pretty simple to look at.
Expressing the Doctype#
One last point: the HTML doctype is a tricky one to get into the template itself as its syntax is brackety like tags.
Instead, define it as a variable using markupsafe.Markup
and insert the variable into the template:
def main() -> str:
"""Main entry point."""
doctype = Markup("<!DOCTYPE html>\n")
vdom = html("{doctype}<div>Hello World</div>")
result = render(vdom)
return result
Reducing Boolean Attribute Values#
The renderer also knows to collapse truthy-y values into simplified HTML attributes.
Thus, instead of editable="1"
you just get the attribute name without a value:
def main() -> str:
"""Main entry point."""
vdom = html("<div editable={True}>Hello World</div>")
result = render(vdom)
return result