Skip to content

octo-gone/jinja2-components

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jinja2 Components

Jinja2 Components offers a streamlined way to build and reuse your Jinja2 templates from code, inspired by django-components and jinja2-simple-tags.

Key Ideas

  • Modular Design. Break down your templates into reusable components for cleaner code and easier maintenance.
  • Simple Syntax. Use Jinja2 syntax to only use your components, lower you templates depth.
  • Extensible Framework. Customize your components using Python code.

Usage

  1. Install the library.

    pip install jinja2-components
  2. Add extension to the Jinja2 environment.

    from jinja2 import Environment
    from jinja2_components import ComponentsExtension
    
    env = Environment(extensions=[ComponentsExtension])
  3. Define a component and it's template.

    from jinja2 import Template
    from jinja2_components import Component, register
    
    @register(name="hello")
    class Hello(Component):
        template = Template("hello")
  4. Use component inside template as a tag.

    template = env.from_string("{% hello %} world")
    print(template.render())
    # hello world

Examples

Standalone tag

@register(name="hello")
class Hello(Component):
    template = Template("hello")

template = env.from_string("{% hello %} world")
print(template.render())
# hello world

Block tag with body

@register(name="button")
class Button(Component):
    template = Template("<button>{{ body }}</button>")
    block = True

    @classmethod
    def get_context(cls, caller):
        return {"body": str(caller())}

template = env.from_string("{% button %}Hello!{% endbutton %}")
print(template.render())
# <button>Hello!</button>

Replacing body

@register(name="base64")
class Base64(Component):
    template = Template("{{ result }}")
    block = True

    @classmethod
    def get_context(cls, caller):
        content = str(caller()).encode()
        return {"result": b64encode(content).decode()}

template = env.from_string("{% base64 %}hello world{% endbase64 %}")
print(template.render())
# aGVsbG8gd29ybGQ=

template = env.from_string("""\
{% base64 as hw_base64 %}hello world{% endbase64 %}\
Base64 of 'hello world': {{ hw_base64 }}
""")
print(template.render())
# Base64 of 'hello world': aGVsbG8gd29ybGQ=

Component in component

If the component's template was set directly, then this template also requires extensions. You can pass extensions where required or as a workaround use env.from_string.

But it possible to use template_str and template_name class variables for later instantiation from environment.

@register(name="button")
class Button(Component):
    template_str = "<button>{{ body }}</button>"

@register(name="menu")
class Menu(Component):
    template_str = """\
<div class="menu">\
{% for btn in buttons %}
  {% button body=btn %}\
{% endfor %}
</button>
"""

template = env.from_string("{% menu buttons=[1, 2, 3] %}")
print(template.render())
# <div class="menu">
#   <button>1</button>
#   <button>2</button>
#   <button>3</button>
# </button>

Rendering component from code

The idea behind this feature is to pass arguments and get rendered component inside the code.

@register(name="button")
class Button(Component):
    template_str = "<button>{{ body }}</button>"

@register(name="menu")
class Menu(Component):
    template_str = """\
<div class="menu">\
{% for btn in buttons %}
  {% button body=btn %}\
{% endfor %}
</button>
"""

template = env.from_string("{% menu buttons=[1, 2, 3] %}")
print(template.render())
# <div class="menu">
#   <button>1</button>
#   <button>2</button>
#   <button>3</button>
# </button>

print(Menu(env, buttons=[1, 2, 3]))
# <div class="menu">
#   <button>1</button>
#   <button>2</button>
#   <button>3</button>
# </button>

Also it is possible to pass initialized components (because the are strings).

@register(name="menu")
class Menu(Component):
    template_str = """\
<div class="menu">\
{% for btn in buttons %}
  {{ btn }}\
{% endfor %}
</button>
"""

rendered = Menu(env, buttons=[Button(env, body=1), Button(env, body=2), Button(env, body=3)])
print(rendered)
# <div class="menu">
#   <button>1</button>
#   <button>2</button>
#   <button>3</button>
# </button>

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published