htmldjangodjango-templates

Reusable HTML component libraries in Django


I have a Django website with many pages all with their own unique content and markup (as opposed to a single template that is dynamically filled with data). These pages' templates are derived from a common "tool" template, which in turn derives from a "base" template. Each page also has a JS file which does the scripting for that page. Each pages data is spread around 3 or 4 locations:

However, although each page has a unique layout and content, I still want to be able to share commonly used parametrised HTML "snippets" between pages. For example, one page has an input with a dropdown button that, using Bootstrap, looks like this:

<div class="input-group">
    <span class="input-group-btn">
        <a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">
            Common baudrates
            <span class="caret"></span>
        </a>
        <ul class="dropdown-menu pull-right" id="common-baudrate-list">
            <li><a href='#'>9600</a></li>
            <li><a href='#'>115200</a></li>
        </ul> 
    </span>           
    <input class="form-control" type="text" id="baudrate-input" val="115200">
</div>

I'd like to make a re-uasable parametrised function/template/tag which allows me to put in a structure like this given something like "group ID", "list of entries", "left/right button position", "default value".

The ways I see to do it so far are:

None of these seem like a neat, re-usable and loosely coupled way to deal with this (for example, if I change up to Bootstrap 4 in future, I may need to re-write these components, and I'd rather have to do it just once). Having a library of components like this will also make future pages easier to construct. Is there a canonical "right" way to do this, or even a commonly used idiom for it?


Edit to show solution implementation

Using inclusion tags as answered below by Wolph, I avoided using a clunky construction like this

{% with [1,2,3] as items %}
    {% my_tag items.split %}
{% endwith %}

as follows:

@register.inclusion_tag('components/this_component.html')
def input_with_dropdown(dropdown_title, dropdown_items, default_value, group_id, button_left, *args, **kwargs):

    split_char = kwargs['split_char'] if 'split_char' in kwargs else ' '

    return {
        'dropdown_items': dropdown_items.split(split_char),
        'dropdown_title': dropdown_title,
        'group_id': group_id,
        'default_value' : default_value,
        'button_left': button_left
    }

And then passing in the variables like this:

{% input_with_dropdown 'Common baudrates' '9600 48000 115200' 115200 'baudrate-input' 0 %}

Solution

  • Have you taken a look at inclusion tags? https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags

    I believe that these would do exactly what you want, you can still specify parameters but you can just pass along a template to have it render really easily.

    Example:

    from django import template
    register = template.Library()
    
    @register.inclusion_tag('list_results.html')
    def list_results(results):
        return {
            'results': results,
            'count': len(results),
        }
    

    list_results.html:

    Found {{ count }} results.<br>
    <ul>
    {% for result in results %}
        <li>{{ result }}</li>
    {% endfor %}
    </ul>