Package: django-component-tags
Test Status:Version Info:Compatibility:Create advanced HTML components using Django Tags.DescriptionUseDjango Template Tagsand writereusable html components.FeaturesClass based template tags.Template tag argument parser.Declarative component attributes.Extendable components.Embedded slot components.Media class (css/js) implementation (Django Form-Media class)Notedjango-component-tagsimplements a simple content distribution API inspired by theWeb Components spec draft,
using the{% slot %}component inside another component to serve as distribution outlets for content.ExtraLibraries created withdjango-component-tags:django-component-tags-tailwindcssRequirementsRequires Django 2.2 or newer, tested against Python 3.7 and PyPy.Quick StartInstall the library:pip3 install django-component-tagsUpdate yoursettings.py:INSTALLED_APPS = [
    ...
    'component_tags',
    ...
]

...

TEMPLATES = [
    {
        'OPTIONS': {
            'builtins': ['component_tags.template.builtins'], # slot component
        },
    },
]

...Assuming that you already have anapplicationcalledfoo, lets create a new component tag:# foo/templatetags/foo_tags.pyfromcomponent_tagsimporttemplateregister=template.Library()@register.tagclassLink(template.Component):href=template.Attribute(default='#')classMeta:template_name='tags/link.html'Notedjango-component-tagsextends the default django template library, because it wraps component classes with a parser
function and extracts template tag arguments, everything else is left in the same way.Please check outthe template libraryif you want to know more about this process.Next, creating the component template:# foo/templates/foo/tags/link.html

<a {{ attributes }}>
    {{ nodelist }}
</a>Here we have a couple of variables inside a component template:attributes: component template/class attributes (formatted).nodelist: the content created between{% link %}and{% endlink %}will be rendered here.Finally, you can use it as follows:# foo/templates/foo/index.html
{% load foo_tags %}

{% link %}
    Link 1
{% endlink %}Output:# foo/templates/foo/index.html

<a href="#">
    Link 1
</a>This is the simplest way to start, there is a lot of different settings that you can combine to create complex
html components.ConsiderationsMaking multiple changes on html components and using cache interferes with theMedia Class Library,
which i believe its good onproduction. Django recommends to set upDummyCacheondevelopmentenvironments:CACHES={'default':{'BACKEND':'django.core.cache.backends.dummy.DummyCache',}}ExamplesAdding css/js scriptsAssuming that you already downloaded a css framework in your project likeBootstrapCSS.Lets create a component:# foo/templatetags/foo_tags.pyfromcomponent_tagsimporttemplateregister=template.Library()@register.tagclassLink(template.Component):href=template.Attribute(default='#')classMeta:template_name='tags/link.html'css={'all':('css/bootstrap.min.css',)}js=['js/bootstrap.bundle.min.js',]Rendering the component in the main template:# foo/templates/foo/index.html
{% load foo_tags %}
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>---</title>
    <meta name="description" content="---">
    <meta name="author" content="---">
    {% components_css %}
</head>

<body>
{% link %}
    Link 1
{% endlink %}
{% components_js %}
</body>
</html>Output:# foo/templates/foo/index.html
{% load foo_tags %}
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>---</title>
    <meta name="description" content="---">
    <meta name="author" content="---">
    <link href="/static/css/bootstrap.min.css" type="text/css" media="all" rel="stylesheet">
</head>

<body>
<a class="btn btn-primary" href="#">
    Link 1
</a>
<script src="/static/js/bootstrap.bundle.min.js"></script>
</body>
</html>Adding css classesLets create a html component using thebootstrap framework# foo/templatetags/foo_tags.pyfromcomponent_tagsimporttemplateregister=template.Library()@register.tagclassLink(template.Component):classColorChoices(template.AttributeChoices):primary='btn btn-primary'secondary='btn btn-secondary'success='btn btn-success'danger='btn btn-danger'warning='btn btn-warning'info='btn btn-info'color=template.Attribute(choices=TypeChoices,default=TypeChoices.submit,as_class=True)href=template.Attribute(default='#')classMeta:template_name='tags/link.html'css={'all':('css/bootstrap.min.css',)}js=['js/bootstrap.bundle.min.js',]Rendering the component:# foo/templates/foo/index.html
{% load foo_tags %}
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>---</title>
    <meta name="description" content="---">
    <meta name="author" content="---">
    {% components_css %}
</head>

<body>
{% link color="primary" class="foo-bar" %}
    Link 1
{% endlink %}

{% components_js %}
</body>
</html>Also we added theclassargument to the component tag, so even if the components strictly have class attributes,
you will always have a flexible way to customize your components any time in different scenarios.Output:# foo/templates/foo/index.html
{% load foo_tags %}
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>---</title>
    <meta name="description" content="---">
    <meta name="author" content="---">
    <link href="/static/css/bootstrap.min.css" type="text/css" media="all" rel="stylesheet">
</head>

<body>
<a class="btn btn-primary foo-bar" href="#">
    Link 1
</a>
<script src="/static/js/bootstrap.bundle.min.js"></script>
</body>
</html>Note that it was merged with all attribute classes previously declared.Using slot componentsLets make another html component using thebootstrap framework,
this one is going to be aCardcomponent.# foo/templatetags/foo_tags.pyfromcomponent_tagsimporttemplateregister=template.Library()@register.tagclassCard(template.Component):title=template.Attribute(required=True,as_context=True)classMeta:template_name='tags/card.html'Create the component template:# foo/templates/foo/tags/card.html

<div class="card" style="width: 18rem;">
  <img src="..." class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">{{ title }}</h5>
    <div class="card-text">
        {{ nodelist }}
    </div>
    {% if slot_footer %}
        <div class="card-footer">
            {{ slot_footer }}
        </div>
    {% endif %}
  </div>
</div>Rendering the component:# foo/templates/foo/index.html
{% load foo_tags %}

{% card title='foo' %}
    Some quick example text to build on the card title and make up the bulk of the card's content.
    {% slot 'footer' %}
        <a href="#" class="btn btn-primary">Go somewhere</a>
    {% endslot %}
{% endcard %}Output:# foo/templates/foo/index.html

<div class="card" style="width: 18rem;">
    <img src="..." class="card-img-top" alt="...">
    <div class="card-body">
        <h5 class="card-title">foo</h5>
        <div class="card-text">
            Some quick example text to build on the card title and make up the bulk of the card's content.
        </div>
        <div class="card-footer">
            <a href="#" class="btn btn-primary">Go somewhere</a>
        </div>
    </div>
</div>Adding extra contextBy default, all components used isolated context to work with. If you want to pass global context to the component tag
it is required to use thewithargument.# foo/views.pydeffoo(request,object_id=None):returnrender(request,'foo/index.html',{'object_id':object_id})# foo/templates/foo/index.html
{% load foo_tags %}

{% link color="primary" with id=object_id %}
    Link {{ id }}
{% endlink %}Assuming that the request of the page will be something likehttp://localhost:8000/foo/1/, the output will be:# foo/templates/foo/index.html

<a class="btn btn-primary" href="#">
    Link 1
</a>NoteSlotcomponents doesn’t need to specify global context, they always use the parent context as default.NoteThis project has been set up using PyScaffold 4.0rc2. For details and usage
information on PyScaffold seehttps://pyscaffold.org/.