v0.110 ๐จ๐ข (2024-11-25)ยค
__
โ ๏ธ Attention โ ๏ธ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.
Generalยค
BREAKING CHANGES ๐จ๐ขยค
-
Installation changes:
- If your components include JS or CSS, you now must use the middleware and add django-components' URLs to your
urlpatterns
(See "Adding support for JS and CSS")
- If your components include JS or CSS, you now must use the middleware and add django-components' URLs to your
-
Component typing signature changed from
to
-
If you rendered a component A with
Component.render()
and then inserted that into another component B, now you must passrender_dependencies=False
to component A:
Featยค
- Intellisense and mypy validation for settings:
Instead of defining the COMPONENTS
settings as a plain dict, you can use ComponentsSettings
:
# settings.py
from django_components import ComponentsSettings
COMPONENTS = ComponentsSettings(
autodiscover=True,
...
)
- Use
get_component_dirs()
andget_component_files()
to get the same list of dirs / files that would be imported byautodiscover()
, but without actually importing them.
Refactorยค
-
For advanced use cases, use can omit the middleware and instead manage component JS and CSS dependencies yourself with
render_dependencies
-
The
ComponentRegistry
settingsRegistrySettings
were lowercased to align with the global settings: RegistrySettings.CONTEXT_BEHAVIOR
->RegistrySettings.context_behavior
RegistrySettings.TAG_FORMATTER
->RegistrySettings.tag_formatter
The old uppercase settings CONTEXT_BEHAVIOR
and TAG_FORMATTER
are deprecated and will be removed in v1.
-
The setting
reload_on_template_change
was renamed toreload_on_file_change
. And now it properly triggers server reload when any file in the component dirs change. The old namereload_on_template_change
is deprecated and will be removed in v1. -
The setting
forbidden_static_files
was renamed tostatic_files_forbidden
to align withstatic_files_allowed
The old nameforbidden_static_files
is deprecated and will be removed in v1.
Tagsยค
BREAKING CHANGES ๐จ๐ขยค
-
{% component_dependencies %}
tag was removed. Instead, use{% component_js_dependencies %}
and{% component_css_dependencies %}
-
The combined tag was removed to encourage the best practice of putting JS scripts at the end of
<body>
, and CSS styles inside<head>
.On the other hand, co-locating JS script and CSS styles can lead to a flash of unstyled content, as either JS scripts will block the rendering, or CSS will load too late.
-
-
The undocumented keyword arg
preload
of{% component_js_dependencies %}
and{% component_css_dependencies %}
tags was removed. This will be replaced with HTML fragment support.
Fixยค
- Allow using forward slash (
/
) when defining custom TagFormatter, e.g.{% MyComp %}..{% /MyComp %}
.
Refactorยค
{% component_dependencies %}
tags are now OPTIONAL - If your components use JS and CSS, but you don't use{% component_dependencies %}
tags, the JS and CSS will now be, by default, inserted at the end of<body>
and at the end of<head>
respectively.
Slotsยค
Featยค
- Fills can now be defined within loops (
{% for %}
) or other tags (like{% with %}
), or even other templates using{% include %}
.
Following is now possible
{% component "table" %}
{% for slot_name in slots %}
{% fill name=slot_name %}
{% endfill %}
{% endfor %}
{% endcomponent %}
- If you need to access the data or the default content of a default fill, you can set the
name
kwarg to"default"
.
Previously, a default fill would be defined simply by omitting the {% fill %}
tags:
But in that case you could not access the slot data or the default content, like it's possible for named fills:
{% component "child" %}
{% fill name="header" data="data" %}
Hello {{ data.user.name }}
{% endfill %}
{% endcomponent %}
Now, you can specify default tag by using name="default"
:
{% component "child" %}
{% fill name="default" data="data" %}
Hello {{ data.user.name }}
{% endfill %}
{% endcomponent %}
- When inside
get_context_data()
or other component methods, the default fill can now be accessed asComponent.input.slots["default"]
, e.g.:
class MyTable(Component):
def get_context_data(self, *args, **kwargs):
default_slot = self.input.slots["default"]
...
- You can now dynamically pass all slots to a child component. This is similar to passing all slots in Vue:
class MyTable(Component):
def get_context_data(self, *args, **kwargs):
return {
"slots": self.input.slots,
}
template: """
<div>
{% component "child" %}
{% for slot_name in slots %}
{% fill name=slot_name data="data" %}
{% slot name=slot_name ...data / %}
{% endfill %}
{% endfor %}
{% endcomponent %}
</div>
"""
Fixยค
-
Slots defined with
{% fill %}
tags are now properly accessible viaself.input.slots
inget_context_data()
-
Do not raise error if multiple slots with same name are flagged as default
-
Slots can now be defined within loops (
{% for %}
) or other tags (like{% with %}
), or even other templates using{% include %}
.
Previously, following would cause the kwarg name
to be an empty string:
Refactorยค
- When you define multiple slots with the same name inside a template, you now have to set the
default
andrequired
flags individually.
<div class="calendar-component">
<div class="header">
{% slot "image" default required %}Image here{% endslot %}
</div>
<div class="body">
{% slot "image" default required %}Image here{% endslot %}
</div>
</div>
This means you can also have multiple slots with the same name but different conditions.
E.g. in this example, we have a component that renders a user avatar - a small circular image with a profile picture of name initials.
If the component is given image_src
or name_initials
variables, the image
slot is optional. But if neither of those are provided, you MUST fill the image
slot.
<div class="avatar">
{% if image_src %}
{% slot "image" default %}
<img src="{{ image_src }}" />
{% endslot %}
{% elif name_initials %}
{% slot "image" default required %}
<div style="
border-radius: 25px;
width: 50px;
height: 50px;
background: blue;
">
{{ name_initials }}
</div>
{% endslot %}
{% else %}
{% slot "image" default required / %}
{% endif %}
</div>
- The slot fills that were passed to a component and which can be accessed as
Component.input.slots
can now be passed through the Django template, e.g. as inputs to other tags.
Internally, django-components handles slot fills as functions.
Previously, if you tried to pass a slot fill within a template, Django would try to call it as a function.
Now, something like this is possible:
class MyTable(Component):
def get_context_data(self, *args, **kwargs):
return {
"child_slot": self.input.slots["child_slot"],
}
template: """
<div>
{% component "child" content=child_slot / %}
</div>
"""
NOTE: Using {% slot %}
and {% fill %}
tags is still the preferred method, but the approach above may be necessary in some complex or edge cases.
- The
is_filled
variable (and the{{ component_vars.is_filled }}
context variable) now returnsFalse
when you try to access a slot name which has not been defined:
Before:
{{ component_vars.is_filled.header }} -> True
{{ component_vars.is_filled.footer }} -> False
{{ component_vars.is_filled.nonexist }} -> "" (empty string)
After:
{{ component_vars.is_filled.header }} -> True
{{ component_vars.is_filled.footer }} -> False
{{ component_vars.is_filled.nonexist }} -> False
-
Components no longer raise an error if there are extra slot fills
-
Components will raise error when a slot is doubly-filled.
E.g. if we have a component with a default slot:
Now there is two ways how we can target this slot: Either using name="default"
or name="content"
.
In case you specify BOTH, the component will raise an error: