HTML attributes
New in version 0.74:
You can use the html_attrs
tag to render HTML attributes, given a dictionary of values.
So if you have a template:
You can simplify it with html_attrs
tag:
where attrs
is:
This feature is inspired by merge_attrs
tag of django-web-components and "fallthrough attributes" feature of Vue.
Removing atttributes¤
Attributes that are set to None
or False
are NOT rendered.
So given this input:
And template:
Then this renders:
Boolean attributes¤
In HTML, boolean attributes are usually rendered with no value. Consider the example below where the first button is disabled and the second is not:
HTML rendering with html_attrs
tag or attributes_to_string
works the same way, where key=True
is rendered simply as key
, and key=False
is not render at all.
So given this input:
And template:
Then this renders:
Default attributes¤
Sometimes you may want to specify default values for attributes. You can pass a second argument (or kwarg defaults
) to set the defaults.
In the example above, if attrs
contains e.g. the class
key, html_attrs
will render:
class="{{ attrs.class }}"
Otherwise, html_attrs
will render:
class="{{ defaults.class }}"
Appending attributes¤
For the class
HTML attribute, it's common that we want to join multiple values, instead of overriding them. For example, if you're authoring a component, you may want to ensure that the component will ALWAYS have a specific class. Yet, you may want to allow users of your component to supply their own classes.
We can achieve this by adding extra kwargs. These values will be appended, instead of overwriting the previous value.
So if we have a variable attrs
:
And on html_attrs
tag, we set the key class
:
Then these will be merged and rendered as:
To simplify merging of variables, you can supply the same key multiple times, and these will be all joined together:
{# my_var = "class-from-var text-red" #}
<div {% html_attrs attrs class="some-class another-class" class=my_var %}>
</div>
Renders:
Rules for html_attrs
¤
- Both
attrs
anddefaults
can be passed as positional args
{% html_attrs attrs defaults key=val %}
or as kwargs
{% html_attrs key=val defaults=defaults attrs=attrs %}
-
Both
attrs
anddefaults
are optional (can be omitted) -
Both
attrs
anddefaults
are dictionaries, and we can define them the same way we define dictionaries for thecomponent
tag. So either asattrs=attrs
orattrs:key=value
. -
All other kwargs are appended and can be repeated.
Examples for html_attrs
¤
Assuming that:
class_from_var = "from-var"
attrs = {
"class": "from-attrs",
"type": "submit",
}
defaults = {
"class": "from-defaults",
"role": "button",
}
Then:
- Empty tag
{% html_attr %}
renders (empty string):
- Only kwargs
{% html_attr class="some-class" class=class_from_var data-id="123" %}
renders:
class="some-class from-var" data-id="123"
- Only attrs
{% html_attr attrs %}
renders:
class="from-attrs" type="submit"
- Attrs as kwarg
{% html_attr attrs=attrs %}
renders:
class="from-attrs" type="submit"
- Only defaults (as kwarg)
{% html_attr defaults=defaults %}
renders:
class="from-defaults" role="button"
- Attrs using the
prefix:key=value
construct
{% html_attr attrs:class="from-attrs" attrs:type="submit" %}
renders:
class="from-attrs" type="submit"
- Defaults using the
prefix:key=value
construct
{% html_attr defaults:class="from-defaults" %}
renders:
class="from-defaults" role="button"
- All together (1) - attrs and defaults as positional args:
{% html_attrs attrs defaults class="added_class" class=class_from_var data-id=123 %}
renders:
class="from-attrs added_class from-var" type="submit" role="button" data-id=123
- All together (2) - attrs and defaults as kwargs args:
{% html_attrs class="added_class" class=class_from_var data-id=123 attrs=attrs defaults=defaults %}
renders:
class="from-attrs added_class from-var" type="submit" role="button" data-id=123
- All together (3) - mixed:
{% html_attrs attrs defaults:class="default-class" class="added_class" class=class_from_var data-id=123 %}
renders:
class="from-attrs added_class from-var" type="submit" data-id=123
Full example for html_attrs
¤
@register("my_comp")
class MyComp(Component):
template: t.django_html = """
<div
{% html_attrs attrs
defaults:class="pa-4 text-red"
class="my-comp-date"
class=class_from_var
data-id="123"
%}
>
Today's date is <span>{{ date }}</span>
</div>
"""
def get_context_data(self, date: Date, attrs: dict):
return {
"date": date,
"attrs": attrs,
"class_from_var": "extra-class"
}
@register("parent")
class Parent(Component):
template: t.django_html = """
{% component "my_comp"
date=date
attrs:class="pa-0 border-solid border-red"
attrs:data-json=json_data
attrs:@click="(e) => onClick(e, 'from_parent')"
/ %}
"""
def get_context_data(self, date: Date):
return {
"date": datetime.now(),
"json_data": json.dumps({"value": 456})
}
Note: For readability, we've split the tags across multiple lines.
Inside MyComp
, we defined a default attribute
defaults:class="pa-4 text-red"
So if attrs
includes key class
, the default above will be ignored.
MyComp
also defines class
key twice. It means that whether the class
attribute is taken from attrs
or defaults
, the two class
values will be appended to it.
So by default, MyComp
renders:
Next, let's consider what will be rendered when we call MyComp
from Parent
component.
MyComp
accepts a attrs
dictionary, that is passed to html_attrs
, so the contents of that dictionary are rendered as the HTML attributes.
In Parent
, we make use of passing dictionary key-value pairs as kwargs to define individual attributes as if they were regular kwargs.
So all kwargs that start with attrs:
will be collected into an attrs
dict.
attrs:class="pa-0 border-solid border-red"
attrs:data-json=json_data
attrs:@click="(e) => onClick(e, 'from_parent')"
And get_context_data
of MyComp
will receive attrs
input with following keys:
attrs = {
"class": "pa-0 border-solid",
"data-json": '{"value": 456}',
"@click": "(e) => onClick(e, 'from_parent')",
}
attrs["class"]
overrides the default value for class
, whereas other keys will be merged.
So in the end MyComp
will render:
<div
class="pa-0 border-solid my-comp-date extra-class"
data-id="123"
data-json='{"value": 456}'
@click="(e) => onClick(e, 'from_parent')"
>
...
</div>
Rendering HTML attributes outside of templates¤
If you need to use serialize HTML attributes outside of Django template and the html_attrs
tag, you can use attributes_to_string
: