In previous examples you could repeatedly see us using @register() to "register" the components. In this section we dive deeper into what it actually means and how you can manage (add or remove) components.
In previous examples you could repeatedly see us using @register() to "register" the components. In this section we dive deeper into what it actually means and how you can manage (add or remove) components.
Django-components provides a seamless integration with HTML fragments (HTML over the wire), whether you're using HTMX, AlpineJS, or vanilla JavaScript.
When you define a component that has extra JS or CSS, and you use django-components to render the fragment, django-components will:
Automatically load the associated JS and CSS
Ensure that JS is loaded and executed only once even if the fragment is inserted multiple times
Info
What are HTML fragments and "HTML over the wire"?
It is one of the methods for updating the state in the browser UI upon user interaction.
How it works is that:
User makes an action - clicks a button or submits a form
The action causes a request to be made from the client to the server.
Server processes the request (e.g. form submission), and responds with HTML of some part of the UI (e.g. a new entry in a table).
A library like HTMX, AlpineJS, or custom function inserts the new HTML into the correct place.
Django-components provides a seamless integration with HTML fragments (HTML over the wire), whether you're using HTMX, AlpineJS, or vanilla JavaScript.
When you define a component that has extra JS or CSS, and you use django-components to render the fragment, django-components will:
Automatically load the associated JS and CSS
Ensure that JS is loaded and executed only once even if the fragment is inserted multiple times
Info
What are HTML fragments and "HTML over the wire"?
It is one of the methods for updating the state in the browser UI upon user interaction.
How it works is that:
User makes an action - clicks a button or submits a form
The action causes a request to be made from the client to the server.
Server processes the request (e.g. form submission), and responds with HTML of some part of the UI (e.g. a new entry in a table).
A library like HTMX, AlpineJS, or custom function inserts the new HTML into the correct place.
Prop drilling refers to a scenario in UI development where you need to pass data through many layers of a component tree to reach the nested components that actually need the data.
Normally, you'd use props to send data from a parent component to its children. However, this straightforward method becomes cumbersome and inefficient if the data has to travel through many levels or if several components scattered at different depths all need the same piece of information.
This results in a situation where the intermediate components, which don't need the data for their own functioning, end up having to manage and pass along these props. This clutters the component tree and makes the code verbose and harder to manage.
A neat solution to avoid prop drilling is using the "provide and inject" technique.
With provide / inject, a parent component acts like a data hub for all its descendants. This setup allows any component, no matter how deeply nested it is, to access the required data directly from this centralized provider without having to messily pass props down the chain. This approach significantly cleans up the code and makes it easier to maintain.
Prop drilling refers to a scenario in UI development where you need to pass data through many layers of a component tree to reach the nested components that actually need the data.
Normally, you'd use props to send data from a parent component to its children. However, this straightforward method becomes cumbersome and inefficient if the data has to travel through many levels or if several components scattered at different depths all need the same piece of information.
This results in a situation where the intermediate components, which don't need the data for their own functioning, end up having to manage and pass along these props. This clutters the component tree and makes the code verbose and harder to manage.
A neat solution to avoid prop drilling is using the "provide and inject" technique.
With provide / inject, a parent component acts like a data hub for all its descendants. This setup allows any component, no matter how deeply nested it is, to access the required data directly from this centralized provider without having to messily pass props down the chain. This approach significantly cleans up the code and makes it easier to maintain.
However, there is a problem here - When we call render_dependencies() inside CardActions.render(), we extract and REMOVE the info on components' JS and CSS from the HTML. But the template of CardActions contains no {% component_depedencies %} tags, and nor <head> nor <body> HTML tags. So the component's JS and CSS will NOT be inserted, and will be lost.
However, there is a problem here - When we call render_dependencies() inside CardActions.render(), we extract and REMOVE the info on components' JS and CSS from the HTML. But the template of CardActions contains no {% component_depedencies %} tags, and nor <head> nor <body> HTML tags. So the component's JS and CSS will NOT be inserted, and will be lost.
If you pre-render one component to pass it into another, the pre-rendered component must be rendered with render_dependencies=False.
\ No newline at end of file
diff --git a/dev/concepts/advanced/tag_formatter/index.html b/dev/concepts/advanced/tag_formatter/index.html
index 35b0e0f1..f6cffe65 100644
--- a/dev/concepts/advanced/tag_formatter/index.html
+++ b/dev/concepts/advanced/tag_formatter/index.html
@@ -1,4 +1,4 @@
- Tag formatters - Django-Components
Every component that you want to use in the template with the {% component %} tag needs to be registered with the ComponentRegistry. Normally, we use the @register decorator for that:
Every component that you want to use in the template with the {% component %} tag needs to be registered with the ComponentRegistry. Normally, we use the @register decorator for that:
By default, context variables are passed down the template as in regular Django - deeper scopes can access the variables from the outer scopes. So if you have several nested forloops, then inside the deep-most loop you can access variables defined by all previous loops.
With this in mind, the {% component %} tag behaves similarly to {% include %} tag - inside the component tag, you can access all variables that were defined outside of it.
And just like with {% include %}, if you don't want a specific component template to have access to the parent context, add only to the {% component %} tag:
{%component"calendar"date="2015-06-19"only/%}
+ Component context and scope - Django-Components
By default, context variables are passed down the template as in regular Django - deeper scopes can access the variables from the outer scopes. So if you have several nested forloops, then inside the deep-most loop you can access variables defined by all previous loops.
With this in mind, the {% component %} tag behaves similarly to {% include %} tag - inside the component tag, you can access all variables that were defined outside of it.
And just like with {% include %}, if you don't want a specific component template to have access to the parent context, add only to the {% component %} tag:
{%component"calendar"date="2015-06-19"only/%}
NOTE: {% csrf_token %} tags need access to the top-level context, and they will not function properly if they are rendered in a component that is called with the only modifier.
If you find yourself using the only modifier often, you can set the context_behavior option to "isolated", which automatically applies the only modifier. This is useful if you want to make sure that components don't accidentally access the outer context.
Components can also access the outer context in their context methods like get_context_data by accessing the property self.outer_context.
Note: Since 0.92, Component no longer subclasses View. To configure the View class, set the nested Component.View class
Components can now be used as views:
Components define the Component.as_view() class method that can be used the same as View.as_view().
By default, you can define GET, POST or other HTTP handlers directly on the Component, same as you do with View. For example, you can override get and post to handle GET and POST requests, respectively.
In addition, Component now has a render_to_response method that renders the component template based on the provided context and slots' data and returns an HttpResponse object.
Note: Since 0.92, Component no longer subclasses View. To configure the View class, set the nested Component.View class
Components can now be used as views:
Components define the Component.as_view() class method that can be used the same as View.as_view().
By default, you can define GET, POST or other HTTP handlers directly on the Component, same as you do with View. For example, you can override get and post to handle GET and POST requests, respectively.
In addition, Component now has a render_to_response method that renders the component template based on the provided context and slots' data and returns an HttpResponse object.
Here's an example of a calendar component defined as a view:
# In a file called [project root]/components/calendar.pyfromdjango_componentsimportComponent,ComponentView,register@register("calendar")
@@ -80,4 +80,4 @@
super(request,*args,**kwargs)do_something_extra(request,*args,**kwargs)
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/dev/concepts/fundamentals/components_in_python/index.html b/dev/concepts/fundamentals/components_in_python/index.html
index c52cef71..2fc83b19 100644
--- a/dev/concepts/fundamentals/components_in_python/index.html
+++ b/dev/concepts/fundamentals/components_in_python/index.html
@@ -1,4 +1,4 @@
- Components in Python - Django-Components
Components can be rendered outside of Django templates, calling them as regular functions ("React-style").
The component class defines render and render_to_response class methods. These methods accept positional args, kwargs, and slots, offering the same flexibility as the {% component %} tag:
classSimpleComponent(Component):
+ Components in Python - Django-Components
Components can be rendered outside of Django templates, calling them as regular functions ("React-style").
The component class defines render and render_to_response class methods. These methods accept positional args, kwargs, and slots, offering the same flexibility as the {% component %} tag:
You cannot use both inlined code and separate file for a single language type:
You can only either set Component.template or Component.template_file
You can only either set Component.css or Component.css_file
You can only either set Component.js or Component.js_file
However, you can freely mix these for different languages:
classMyTable(Component):template:types.django_html=""" <div class="welcome"> Hi there!
@@ -22,7 +22,7 @@
js_file="calendar/script.js"
Important
File path resolution in-depth
At component class creation, django-components checks all file paths defined on the component (e.g. Component.template_file).
For each file path, it checks if the file path is relative to the component's directory. And such file exists, the component's file path is re-written to be defined relative to a first matching directory in COMPONENTS.dirs or COMPONENTS.app_dirs.
Each component can have only a single template, and single main JS and CSS. However, you can define additional JS or CSS using the nested Component.Media class.
Paths are generally handled as static file paths, and resolved URLs are rendered to HTML with media_class.render_js() or media_class.render_css().
A path that starts with http, https, or / is considered a URL, skipping the static file resolution. This path is still rendered to HTML with media_class.render_js() or media_class.render_css().
A SafeString, or a function (with __html__ method) is considered an already-formatted HTML tag, skipping both static file resolution and rendering with media_class.render_js() or media_class.render_css().
However, there's a few differences from Django's Media class:
Our Media class accepts various formats for the JS and CSS files: either a single file, a list, or (CSS-only) a dictonary (See ComponentMediaInput).
Each component can have only a single template, and single main JS and CSS. However, you can define additional JS or CSS using the nested Component.Media class.
Paths are generally handled as static file paths, and resolved URLs are rendered to HTML with media_class.render_js() or media_class.render_css().
A path that starts with http, https, or / is considered a URL, skipping the static file resolution. This path is still rendered to HTML with media_class.render_js() or media_class.render_css().
A SafeString, or a function (with __html__ method) is considered an already-formatted HTML tag, skipping both static file resolution and rendering with media_class.render_js() or media_class.render_css().
Do NOT modify HTML / CSS / JS after it has been loaded
django-components assumes that the component's media files like js_file or Media.js/css are static.
If you need to dynamically change these media files, consider instead defining multiple Components.
Modifying these files AFTER the component has been loaded at best does nothing. However, this is an untested behavior, which may lead to unexpected errors.
Do NOT modify HTML / CSS / JS after it has been loaded
django-components assumes that the component's media files like js_file or Media.js/css are static.
If you need to dynamically change these media files, consider instead defining multiple Components.
Modifying these files AFTER the component has been loaded at best does nothing. However, this is an untested behavior, which may lead to unexpected errors.
Alternatively, you can specify which components to inherit from. In such case, the media files are inherited ONLY from the specified components, and NOT from the original parent components:
classParentComponent(Component):
+classMedia:
+js=["parent.js"]
+
+classMyComponent(ParentComponent):
+classMedia:
+# Only inherit from these, ignoring the files from the parent
+extend=[OtherComponent1,OtherComponent2]
+
+js=["script.js"]
+
+print(MyComponent.media._js)# ["script.js", "other1.js", "other2.js"]
+
When you set extend to a list, the list is expected to contain Component classes (or other classes that have a nested Media class).
\ No newline at end of file
diff --git a/dev/concepts/fundamentals/html_attributes/index.html b/dev/concepts/fundamentals/html_attributes/index.html
index 20f837eb..75ce5817 100644
--- a/dev/concepts/fundamentals/html_attributes/index.html
+++ b/dev/concepts/fundamentals/html_attributes/index.html
@@ -1,4 +1,4 @@
- HTML attributes - Django-Components
Components can be defined in a single file, which is useful for small components. To do this, you can use the template, js, and css class attributes instead of the template_file, js_file, and css_file.
For example, here's the calendar component from the Getting started tutorial, defined in a single file:
Components can be defined in a single file, which is useful for small components. To do this, you can use the template, js, and css class attributes instead of the template_file, js_file, and css_file.
For example, here's the calendar component from the Getting started tutorial, defined in a single file:
The slot tag now serves only to declare new slots inside the component template.
To override the content of a declared slot, use the newly introduced fill tag instead.
Whereas unfilled slots used to raise a warning, filling a slot is now optional by default.
To indicate that a slot must be filled, the new required option should be added at the end of the slot tag.
Components support something called 'slots'. When a component is used inside another template, slots allow the parent template to override specific parts of the child component by passing in different content. This mechanism makes components more reusable and composable. This behavior is similar to slots in Vue.
In the example below we introduce two block tags that work hand in hand to make this work. These are...
{% slot <name> %}/{% endslot %}: Declares a new slot in the component template.
{% fill <name> %}/{% endfill %}: (Used inside a {% component %} tag pair.) Fills a declared slot with the specified content.
Let's update our calendar component to support more customization. We'll add slot tag pairs to its template, template.html.
The slot tag now serves only to declare new slots inside the component template.
To override the content of a declared slot, use the newly introduced fill tag instead.
Whereas unfilled slots used to raise a warning, filling a slot is now optional by default.
To indicate that a slot must be filled, the new required option should be added at the end of the slot tag.
Components support something called 'slots'. When a component is used inside another template, slots allow the parent template to override specific parts of the child component by passing in different content. This mechanism makes components more reusable and composable. This behavior is similar to slots in Vue.
In the example below we introduce two block tags that work hand in hand to make this work. These are...
{% slot <name> %}/{% endslot %}: Declares a new slot in the component template.
{% fill <name> %}/{% endfill %}: (Used inside a {% component %} tag pair.) Fills a declared slot with the specified content.
Let's update our calendar component to support more customization. We'll add slot tag pairs to its template, template.html.
In larger projects, you might need to write multiple components with similar behavior. In such cases, you can extract shared behavior into a standalone component class to keep things DRY.
When subclassing a component, there's a couple of things to keep in mind:
If a child component class defines either member of a pair (e.g., either template or template_file), it takes precedence and the parent's definition is ignored completely.
The Component.Media nested class follows Django's media inheritance rules:
If both parent and child define a Media class, the child's media will automatically include both its own and the parent's JS and CSS files.
This behavior can be configured using the extend attribute in the Media class, similar to Django's forms. Read more on this in Controlling Media Inheritance.
For example:
classBaseModal(Component):
+template="<div>Modal content</div>"
+
+classMedia:
+css=["base_modal.css"]
+js=["base_modal.js"]# Contains core modal functionality
+
+classFancyModal(BaseModal):
+classMedia:
+# Will include both base_modal.css/js AND fancy_modal.css/js
+css=["fancy_modal.css"]# Additional styling
+js=["fancy_modal.js"]# Additional animations
+
+classSimpleModal(BaseModal):
+classMedia:
+extend=False# Don't inherit parent's media
+css=["simple_modal.css"]# Only this CSS will be included
+js=["simple_modal.js"]# Only this JS will be included
+
\ No newline at end of file
diff --git a/dev/concepts/fundamentals/template_tag_syntax/index.html b/dev/concepts/fundamentals/template_tag_syntax/index.html
index ec7fbc5d..6319d57d 100644
--- a/dev/concepts/fundamentals/template_tag_syntax/index.html
+++ b/dev/concepts/fundamentals/template_tag_syntax/index.html
@@ -1,4 +1,4 @@
- Template tag syntax - Django-Components
All template tags in django_component, like {% component %} or {% slot %}, and so on, support extra syntax that makes it possible to write components like in Vue or React (JSX).
When you have a tag like {% component %} or {% slot %}, but it has no content, you can simply append a forward slash / at the end, instead of writing out the closing tags like {% endcomponent %} or {% endslot %}:
So this:
{%component"button"%}{%endcomponent%}
+ Template tag syntax - Django-Components