Basic Usage¶
htm.py
advertises itself as a Python implementation of htm, a JavaScript templating package.
htm
advertises itself as a replacement for JSX, a simpler way to do VDOM-compatible templating.
Let’s take a look at some of the templating patterns in htm.py
.
Hello World¶
Let’s start with generating <div>Hello World</div>
.
This is a single node (the <div>
) with one child, a text node (Hello World
.)
Here is a “template”, the html
function, which generates the VDOM-like output:
from htm import htm
@htm
def html(tag, props, children):
return tag, props, children
result01 = html("""
<div>Hello World</div>
""")
What is this VDOM-like output? A series of possibly-nested tuples, as explained in TODO.
>>> result01
('div', {}, ['Hello World'])
Dynamic Values¶
That was pretty boring…all static, no “templating”. Let’s have the template insert a value:
name = 'World'
result02 = html("""
<div>Hello {name}</div>
""")
As you can see, we use brackets, as in {name}
, to insert values.
The output is just a little be different now:
>>> result02
('div', {}, ['Hello ', 'World'])
Static Attributes¶
Wonder what the second item – the empty {}
– is for in the tuple?
That’s where attributes for that node would go.
Let’s add an attribute:
name = 'World'
result03 = html("""
<div title="Say Hi">Hello {name}</div>
""")
This returns:
>>> result03
('div', {'title': 'Say Hi'}, ['Hello ', 'World'])
So now you see the structure of the tuple:
First item is the tag name
Second item is for attribute information
Third item is for child nodes
Dynamic Attributes¶
Attributes aren’t always static, of course. You can make them dynamic by – you guessed it – curly brackets containing an expression:
message = 'Say Howdy'
name = 'World'
result04 = html("""
<div title="{message}">Hello {name}</div>
""")
And the result:
>>> result04
('div', {'title': 'Say Howdy'}, ['Hello ', 'World'])
Nested Children¶
We’re dealing with a single node. But what happens when we have a tree of nodes, which is usually the case? That’s where nesting comes in, and that’s where the VDOM approach shines.
message = 'Say Howdy'
name = 'World'
result05 = html("""
<section>
<div title="{message}">Hello {name}</div>
</section>
""")
The result now has a tuple whose third item – the children – is a sequence:
>>> result05
('section', {}, [('div', {'title': 'Say Howdy'}, ['Hello ', 'World'])])
Inline Python¶
Perhaps we want the name
value in all uppercase?
This “template” language supports Python expressions inside the brackets:
message = 'Say Howdy'
name = 'World'
result06 = html("""
<section>
<div title="{message}">Hello {name.upper()}</div>
</section>
""")
The output is the same, but with World
in all caps:
>>> result06
('section', {}, [('div', {'title': 'Say Howdy'}, ['Hello ', 'WORLD'])])
Looping¶
Rendering a list of things is very common.
JSX, Jinja2, and most other template-like environments make it easy to do so.
In JSX, you use the language’s looping facilities.
Same in htm.py
:
message = 'Say Howdy'
names = ['World', 'Universe', 'Galaxy']
result07 = html("""
<ul title="{message}">
{[
html('<li>Hello {name}</li>')
for name in names
] }
</li>
""")
What’s the output?
The children of ul
– meaning, the third element of the tuple – holds a sequence of sequences:
>>> result07
('ul', {'title': 'Say Howdy'}, [[('li', {}, ['Hello ', 'World']), ('li', {}, ['Hello ', 'Universe']), ('li', {}, ['Hello ', 'Galaxy'])]])
Conditional Rendering¶
Maybe you only want a footer when a value is provided:
from htm import htm
@htm
def html(tag, props, children):
return tag, props, children
# start
message = 'Say Howdy'
not_message = 'So Sad'
show_message = True
result08 = html("""
<h1>Show?</h1>
{message if show_message else not_message}
""")
Our output now contains the children of the caller, placed in the spot determined by the subcomponent:
>>> result08
[('h1', {}, ['Show?']), 'Say Howdy']
Here’s another variation that inserts a subtemplate conditionally:
from htm import htm
@htm
def html(tag, props, children):
return tag, props, children
# start
message = 'Say Howdy'
not_message = 'So Sad'
show_message = True
result08b = html("""
<div>
<h1>Show?</h1>
{html('''<p>{message}</p>''') if show_message else html('''<p>{not_message}</p>''')}
</div>
""")
We now get some richer output:
>>> result08b
('div', {}, [('h1', {}, ['Show?']), ('p', {}, ['Say Howdy'])])
Subcomponents¶
In React and JSX, you frequently split things into lots – LOTS – of small, single-focused components.
Let’s extract the <li>
into a subcomponent:
message = 'Say Howdy'
names = ['World', 'Universe', 'Galaxy']
def greeting(name):
return html('<li>Hello {name}</li>')
result09 = html("""
<ul title="{message}">
{[greeting(name) for name in names]}
</li>
""")
The results are the same:
>>> result09
('ul', {'title': 'Say Howdy'}, [[('li', {}, ['Hello ', 'World']), ('li', {}, ['Hello ', 'Universe']), ('li', {}, ['Hello ', 'Galaxy'])]])