Extensions can be set by either as an import string or by passing in a class:
# settings.pyclassMyExtension(ComponentExtension):name="my_extension"
@@ -206,187 +206,210 @@
# All `Component.ColorLogger` classes will inherit from this class.ComponentConfig=ColorLoggerComponentConfig
-
Extensions in django-components can define custom commands that can be executed via the Django management command interface. This allows for powerful automation and customization capabilities.
For example, if you have an extension that defines a command that prints "Hello world", you can run the command with:
pythonmanage.pycomponentsextrunmy_exthello
-
Where:
python manage.py components - is the Django entrypoint
ext run - is the subcommand to run extension commands
Instead, use the Slot.extra attribute, which is copied from the original slot:
# ✅ Do this:
+classLoggerExtension(ComponentExtension):
+name="logger"
+
+# Save component-level logger settings for each slot when a component is rendered.
+defon_component_input(self,ctx:OnComponentInputContext):
+forslotinctx.component.slots.values():
+slot.extra["logger"]=ctx.component.logger
-classMyExt(ComponentExtension):
-name="my_ext"
-commands=[HelloCommand]
-
Commands can accept positional arguments and options (e.g. --foo), which are defined using the arguments attribute of the ComponentCommand class.
The arguments are parsed with argparse into a dictionary of arguments and options. These are then available as keyword arguments to the handle method of the command.
fromdjango_componentsimportCommandArg,ComponentCommand,ComponentExtension
-
-classHelloCommand(ComponentCommand):
-name="hello"
-help="Say hello"
-
-arguments=[
-# Positional argument
-CommandArg(
-name_or_flags="name",
-help="The name to say hello to",
-),
-# Optional argument
-CommandArg(
-name_or_flags=["--shout","-s"],
-action="store_true",
-help="Shout the hello",
-),
-]
-
-defhandle(self,name:str,*args,**kwargs):
-shout=kwargs.get("shout",False)
-msg=f"Hello, {name}!"
-ifshout:
-msg=msg.upper()
-print(msg)
-
You can run the command with arguments and options:
fromdjango_componentsimportCommandArg,CommandArgGroup,ComponentCommand,ComponentExtension
+# Then, when a fill is rendered with `{% slot %}`, we can access the logger settings
+# from the slot's metadata.
+defon_slot_rendered(self,ctx:OnSlotRenderedContext):
+logger=ctx.slot.extra["logger"]
+logger.log(...)
+
Extensions in django-components can define custom commands that can be executed via the Django management command interface. This allows for powerful automation and customization capabilities.
For example, if you have an extension that defines a command that prints "Hello world", you can run the command with:
pythonmanage.pycomponentsextrunmy_exthello
+
Where:
python manage.py components - is the Django entrypoint
ext run - is the subcommand to run extension commands
Commands can accept positional arguments and options (e.g. --foo), which are defined using the arguments attribute of the ComponentCommand class.
The arguments are parsed with argparse into a dictionary of arguments and options. These are then available as keyword arguments to the handle method of the command.
fromdjango_componentsimportCommandArg,ComponentCommand,ComponentExtensionclassHelloCommand(ComponentCommand):name="hello"help="Say hello"
-# Argument parsing is managed by `argparse`.
-arguments=[
-# Positional argument
-CommandArg(
-name_or_flags="name",
-help="The name to say hello to",
-),
-# Optional argument
-CommandArg(
-name_or_flags=["--shout","-s"],
-action="store_true",
-help="Shout the hello",
-),
-# When printing the command help message, `--bar` and `--baz`
-# will be grouped under "group bar".
-CommandArgGroup(
-title="group bar",
-description="Group description.",
-arguments=[
-CommandArg(
-name_or_flags="--bar",
-help="Bar description.",
-),
-CommandArg(
-name_or_flags="--baz",
-help="Baz description.",
-),
-],
-),
-]
-
-defhandle(self,name:str,*args,**kwargs):
-shout=kwargs.get("shout",False)
-msg=f"Hello, {name}!"
-ifshout:
-msg=msg.upper()
-print(msg)
-
Extensions can define subcommands, allowing for more complex command structures.
Subcommands are defined similarly to root commands, as subclasses of ComponentCommand class.
However, instead of defining the subcommands in the commands attribute of the extension, you define them in the subcommands attribute of the parent command:
Extensions can define custom views and endpoints that can be accessed through the Django application.
To define URLs for an extension, set them in the urls attribute of your ComponentExtension class. Each URL is defined using the URLRoute class, which specifies the path, handler, and optional name for the route.
Here's an example of how to define URLs within an extension:
fromdjango_components.extensionimportComponentExtension,URLRoute
-fromdjango.httpimportHttpResponse
-
-defmy_view(request):
-returnHttpResponse("Hello from my extension!")
-
-classMyExtension(ComponentExtension):
-name="my_extension"
-
-urls=[
-URLRoute(path="my-view/",handler=my_view,name="my_view"),
-URLRoute(path="another-view/<int:id>/",handler=my_view,name="another_view"),
-]
-
Warning
The URLRoute objects are different from objects created with Django's django.urls.path(). Do NOT use URLRoute objects in Django's urlpatterns and vice versa!
django-components uses a custom URLRoute class to define framework-agnostic routing rules.
As of v0.131, URLRoute objects are directly converted to Django's URLPattern and URLResolver objects.
Extensions can define subcommands, allowing for more complex command structures.
Subcommands are defined similarly to root commands, as subclasses of ComponentCommand class.
However, instead of defining the subcommands in the commands attribute of the extension, you define them in the subcommands attribute of the parent command:
Extensions can define custom views and endpoints that can be accessed through the Django application.
To define URLs for an extension, set them in the urls attribute of your ComponentExtension class. Each URL is defined using the URLRoute class, which specifies the path, handler, and optional name for the route.
Here's an example of how to define URLs within an extension:
fromdjango_components.extensionimportComponentExtension,URLRoute
+fromdjango.httpimportHttpResponse
+
+defmy_view(request):
+returnHttpResponse("Hello from my extension!")
+
+classMyExtension(ComponentExtension):
+name="my_extension"
+
+urls=[
+URLRoute(path="my-view/",handler=my_view,name="my_view"),
+URLRoute(path="another-view/<int:id>/",handler=my_view,name="another_view"),
+]
+
Warning
The URLRoute objects are different from objects created with Django's django.urls.path(). Do NOT use URLRoute objects in Django's urlpatterns and vice versa!
django-components uses a custom URLRoute class to define framework-agnostic routing rules.
As of v0.131, URLRoute objects are directly converted to Django's URLPattern and URLResolver objects.
\ No newline at end of file
diff --git a/dev/concepts/fundamentals/slots/index.html b/dev/concepts/fundamentals/slots/index.html
index 63a055e8..9f32c45f 100644
--- a/dev/concepts/fundamentals/slots/index.html
+++ b/dev/concepts/fundamentals/slots/index.html
@@ -311,148 +311,156 @@
slot=Slot(slot_func)html=slot()
-
Warning
While available, try to avoid using the context attribute in slot functions.
Instead, prefer using the data and fallback attributes.
Access to context may be removed in future versions (v2, v3).
These are used for debugging, such as printing the path to the slot in the component tree.
When you create a slot, you can set these fields too:
# Either at slot creation
-slot=Slot(
-lambdactx:f"Hello, {ctx.data['name']}!",
-component_name="table",
-slot_name="name",
-)
-
-# Or later
-slot.component_name="table"
-slot.slot_name="name"
-
Sometimes you may want to generate slots based on the given input. One example of this is Vuetify's table component, which creates a header and an item slots for each user-defined column.
So if you pass columns named name and age to the table component:
These are populated the first time a slot is passed to a component.
So if you pass the same slot through multiple nested components, the metadata will still point to the first component that received the slot.
You can use these for debugging, such as printing out the slot's component name and slot name.
Extensions can use Slot.source to handle slots differently based on whether the slot was defined in the template with {% fill %} tag or in the component's Python code. See an example in Pass slot metadata.
You can also pass any additional data along with the slot by setting it in Slot.extra:
Sometimes you may want to generate slots based on the given input. One example of this is Vuetify's table component, which creates a header and an item slots for each user-defined column.
So if you pass columns named name and age to the table component:
The component_vars.is_filled variable is still available, but will be removed in v1.0.
NOTE: component_vars.slots no longer escapes special characters in slot names.
You can use {{ component_vars.is_filled.<name> }} together with Django's {% if / elif / else / endif %} tags to define a block whose contents will be rendered only if the component slot with the corresponding 'name' is filled.
This is what our example looks like with component_vars.is_filled.
Accessing is_filled of slot names with special characters¤
To be able to access a slot name via component_vars.is_filled, the slot name needs to be composed of only alphanumeric characters and underscores (e.g. this__isvalid_123).
However, you can still define slots with other special characters. In such case, the slot name in component_vars.is_filled is modified to replace all invalid characters into _.
So a slot named "my super-slot :)" will be available as component_vars.is_filled.my_super_slot___.
Same applies when you are accessing is_filled from within the Python, e.g.:
classMyTable(Component):
-defon_render_before(self,context,template)->None:
-# ✅ Works
-ifself.is_filled["my_super_slot___"]:
-# Do something
-
-# ❌ Does not work
-ifself.is_filled["my super-slot :)"]:
-# Do something
-