What's Changed
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
Component[Args, Kwargs, Data, Slots]
to
Component[Args, Kwargs, Slots, Data, JsData, CssData]
-
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:prerendered_a = CompA.render( args=[...], kwargs={...}, render_dependencies=False, ) html = CompB.render( kwargs={ content=prerendered_a, }, )
Feat
-
Intellisense and mypy validation for settings:
Instead of defining the
COMPONENTS
settings as a plain dict, you can useComponentsSettings
:# 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
andTAG_FORMATTER
are deprecated and will be removed in v1. -
The setting
reload_on_template_change
was renamed to
reload_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 to
static_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 thename
kwarg to"default"
.Previously, a default fill would be defined simply by omitting the
{% fill %}
tags:{% component "child" %} Hello world {% endcomponent %}
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:{% for slot_name in slots %} {% slot name=slot_name %} {% endfor %}
Refactor
-
When you define multiple slots with the same name inside a template,
you now have to set thedefault
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
orname_initials
variables,
theimage
slot is optional. But if neither of those are provided,
you MUST fill theimage
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 returns
False
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:
{% slot name="content" default / %}
Now there is two ways how we can target this slot: Either using
name="default"
orname="content"
.In case you specify BOTH, the component will raise an error:
{% component "child" %} {% fill slot="default" %} Hello from default slot {% endfill %} {% fill slot="content" data="data" %} Hello from content slot {% endfill %} {% endcomponent %}
New Contributors
Full Changelog: 0.102...0.110