Releases: django-components/django-components
0.143.2
What's Changed
Fix
- Fix KeyError when using
{% provide %}, caused by premature cleanup
by @joeyjurjens in #1490
New Contributors
- @joeyjurjens made their first contribution in #1490
Full Changelog: 0.143.1...0.143.2
0.143.1
What's Changed
Fix
- Make django-component's position in Django's
INSTALLED_APPSmore lenient by not calling Django'sURLResolver._populate()ifURLResolverhasn't been resolved before (#1483).
Refactor
- Components now raise error if template data overwrites variables from
context_processors(#1482)
Full Changelog: 0.143.0...0.143.1
0.143.0
What's Changed
Added support for Python 3.14!
Feat
-
You can now define component input defaults directly on
Component.Kwargs.Before, the defaults had to be defined on a separate
Component.Defaultsclass:class ProfileCard(Component): class Kwargs: user_id: int show_details: bool class Defaults: show_details = True
Now, django-components can detect the defaults from
Component.Kwargsand apply
them. So you can mergeComponent.KwargswithComponent.Defaults:class ProfileCard(Component): class Kwargs: user_id: int show_details: bool = True
NOTE: This applies only when
Component.Kwargsis a NamedTuple or dataclass. -
New helper
get_component_defaults():Now, the defaults may be defined on either
Component.DefaultsandComponent.Kwargsclasses.To get a final, merged dictionary of all the component's defaults, use
get_component_defaults():from django_components import Component, Default, get_component_defaults class MyTable(Component): class Kwargs: position: str order: int items: list[int] variable: str = "from_kwargs" class Defaults: position: str = "left" items = Default(lambda: [1, 2, 3]) defaults = get_component_defaults(MyTable) # { # "position": "left", # "items": [1, 2, 3], # "variable": "from_kwargs", # }
-
Simpler syntax for defining component inputs:
When defining
Args,Kwargs,Slots,JsData,CssData,TemplateData, these data classes now don't have to subclass any other class.If they are not subclassing (nor
@dataclass), these data classes will be automatically converted toNamedTuples:Before - the
Args,Kwargs, andSlots(etc..) had to be NamedTuples, dataclasses, or Pydantic models:from typing import NamedTuple from django_components import Component class Button(Component): class Args(NamedTuple): size: int text: str class Kwargs(NamedTuple): variable: str maybe_var: Optional[int] = None class Slots(NamedTuple): my_slot: Optional[SlotInput] = None def get_template_data(self, args: Args, kwargs: Kwargs, slots: Slots, context: Context): ...
Now these classes are automatically converted to
NamedTuplesif they don't subclass anything else:class Button(Component): class Args: # Same as `Args(NamedTuple)` size: int text: str class Kwargs: # Same as `Kwargs(NamedTuple)` variable: str maybe_var: Optional[int] = None class Slots: # Same as `Slots(NamedTuple)` my_slot: Optional[SlotInput] = None def get_template_data(self, args: Args, kwargs: Kwargs, slots: Slots, context: Context): ...
Refactor
-
Extension authors: The
ExtensionComponentConfigcan be instantiated withNoneinstead of a component instance.This allows to call component-level extension methods outside of the normal rendering lifecycle.
Full Changelog: 0.142.3...0.143.0
0.142.3
What's Changed
v0.142.3
Fix
- Fixed compatibility with older versions of django-template-partials. django-components now works with django-template-partials v23.3 and later. (See #1455)
Refactor
-
Component.View.public = Trueis now optional.Before, to create component endpoints, you had to set both:
- HTTP handlers on
Component.View Component.View.public = True.
Now, you can set only the HTTP handlers, and the component will be automatically exposed
when any of the HTTP handlers are defined.You can still explicitly expose/hide the component with
Component.View.public = True/False.Before:
class MyTable(Component): class View: public = True def get(self, request): return self.render_to_response() url = get_component_url(MyTable)
After:
class MyTable(Component): class View: def get(self, request): return self.render_to_response() url = get_component_url(MyTable)
- HTTP handlers on
Docs
- Add "scenarios" code examples by @JuroOravec in #1445
- fix changelog issues / inconsistencies by @KyeRussell in #1450
New Contributors
- @KyeRussell made their first contribution in #1450
Full Changelog: 0.142.2...0.142.3
0.142.2
What's Changed
Fix
- Fix compatibility issue when there was multiple
{% include %}blocks inside a component fill, while those included templates contained{% extends %}tags. See #1389
Full Changelog: 0.142.1...0.142.2
0.142.1
What's Changed
Fix
-
Fix bug introduced in v0.142.0 where django-components broke
when the{% component_tags %}library was NOT among the built-ins (#1439) -
Fix compatibility between Django's
inclusion_tagand django-components.
(#1390)
Full Changelog: 0.142.0...0.142.1
0.142.0
What's Changed
v0.142.0
Feat
-
New built-in component
ErrorFallbackUse
ErrorFallbackto catch errors and display a fallback content instead.This is similar to React's
ErrorBoundary
component.Either pass the fallback as a kwarg:
{% component "error_fallback" fallback="Oops, something went wrong" %} {% component "table" / %} {% endcomponent %}
Or use the full
fallbackslot:{% component "error_fallback" %} {% fill "content" %} {% component "table" / %} {% endfill %} {% fill "fallback" data="data" %} <p>Oops, something went wrong</p> {% button href="/report-error" %} Report error {% endbutton %} {% endfill %} {% endcomponent %}
-
Wrap the template rendering in
Component.on_render()in a lambda function.When you wrap the rendering call in a lambda function, and the rendering fails,
the error will be yielded back in the(None, Exception)tuple.Before:
class MyTable(Component): def on_render(self, context, template): try: intermediate = template.render(context) html, error = yield intermediate except Exception as e: html, error = None, e
After:
class MyTable(Component): def on_render(self, context, template): html, error = yield lambda: template.render(context)
-
Multiple yields in
Component.on_render()- You can now yield multiple times within the sameon_rendermethod for complex rendering scenarios.class MyTable(Component): def on_render(self, context, template): # First yield with context.push({"mode": "header"}): header_html, header_error = yield lambda: template.render(context) # Second yield with context.push({"mode": "body"}): body_html, body_error = yield lambda: template.render(context) # Third yield footer_html, footer_error = yield "Footer content" # Process all results if header_error or body_error or footer_error: return "Error occurred during rendering" return f"{header_html}\n{body_html}\n{footer_html}"
Each yield operation is independent and returns its own
(html, error)tuple, allowing you to handle each rendering result separately.
Fix
-
Improve formatting when an exception is raised while rendering components. Error messages with newlines should now be properly formatted.
-
Add missing exports for
OnComponentRenderedContext,OnSlotRenderedContext,OnTemplateCompiledContext,OnTemplateLoadedContext.
Refactor
-
Changes to how
get_component_url()handles query parameters:Truevalues are now converted to boolean flags (e.g.?enabledinstead of?enabled=True).FalseandNonevalues are now filtered out.
url = get_component_url( MyComponent, query={"abc": 123, "enabled": True, "debug": False, "none_key": None}, ) # /components/ext/view/components/c1ab2c3?abc=123&enabled
Docs
- New people page to celebrate the contributors and authors!
Full Changelog: 0.141.6...0.142.0
0.141.6
What's Changed
Fix
-
Fix error that occured when calling
Component.inject()inside loops:class MyComponent(Component): def get_template_data(self, args, kwargs, slots, context): data = self.inject("my_provide") return {"data": data}
{% load component_tags %} {% provide "my_provide" key="hi" data=data %} {% for i in range(10) %} {% component "my_component" / %} {% endfor %} {% endprovide %}
-
Allow to call
Component.inject()outside of the rendering:comp = None class MyComponent(Component): def get_template_data(self, args, kwargs, slots, context): nonlocal comp comp = self template_str = """ {% load component_tags %} {% provide "my_provide" key="hi" data=data %} {% component "my_component" / %} {% endprovide %} """ template = Template(template_str) rendered = template.render(Context({})) assert comp is not None injected = comp.inject("my_provide") assert injected.key == "hi" assert injected.data == "data"
Refactor
- Removed circular references to the Component instances. Component instances
are now garbage collected unless you keep a reference to them.
Docs
- fix: fix link to Adding slot page in getting started doc. by @Antoliny0919 in #1374
- docs: add discord link to documentation and README. by @Antoliny0919 in #1386
- docs: document how to serve documentation website by @JuroOravec in #1391
- docs: add first sponsor.🙌 by @Antoliny0919 in #1393
- refactor: move logos and sponsors to assets dir by @JuroOravec in #1405
- docs: fix example by @JuroOravec in #1406
- docs: Add Juro to FUNDING.yml by @JuroOravec in #1407
- docs: document project board and labels by @JuroOravec in #1408
- docs: document publishing by @JuroOravec in #1409
- docs: add Form and Tabs examples by @JuroOravec in #1411
Full Changelog: 0.141.5...0.141.6
0.141.5
What's Changed
Fix
- Tests - Fix bug when using
@djc_testdecorator and theCOMPONENTS
settings are set withComponentsSettingsby @PavelPancocha.
See #1369
Docs
- Add 2-level tabs, add examples and plugins, move community, split release notes by @JuroOravec in #1345
New Contributors
- @PavelPancocha made their first contribution in #1370
Full Changelog: 0.141.4...0.141.5
0.141.4
What's Changed
Fix
- Fix compatibility with Django's
{% include %}and{% extends %}tags (#1325)
Full Changelog: 0.141.3...0.141.4