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.
defon_render_before(self:Component,context:Context,template:Template
@@ -32,4 +32,4 @@
Hello this is tab 2{%endcomponent%}{%endcomponent%}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/dev/concepts/advanced/html_tragments/index.html b/dev/concepts/advanced/html_tragments/index.html
new file mode 100644
index 00000000..fefdfe78
--- /dev/null
+++ b/dev/concepts/advanced/html_tragments/index.html
@@ -0,0 +1,222 @@
+ HTML fragments - Django-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.
Fragment mode assumes that the main HTML has already been rendered and loaded on the page. The component renders HTML that will be inserted into the page as a fragment, at a LATER time:
JS and CSS is not directly embedded to avoid duplicately executing the same JS scripts. So template tags like {% component_js_dependencies %} inside of fragments are ignored.
Instead, django-components appends the fragment's content with a JSON <script> to trigger a call to its asset manager JS script, which will load the JS and CSS smartly.
The asset manager JS script is assumed to be already loaded on the page.
classFrag(Component):
+defget(self,request):
+# IMPORTANT: Don't forget `type="fragment"`
+returnself.render_to_response(
+type="fragment",
+)
+
+# NOTE: We wrap the actual fragment in a template tag with x-if="false" to prevent it
+# from being rendered until we have registered the component with AlpineJS.
+template="""
+ <template x-if="false" data-name="frag">
+ <div class="frag">
+ 123
+ <span x-data="frag" x-text="fragVal">
+ </span>
+ </div>
+ </template>
+ """
+
+js="""
+ Alpine.data('frag', () => ({
+ fragVal: 'xxx',
+ }));
+
+ // Now that the component has been defined in AlpineJS, we can "activate"
+ // all instances where we use the `x-data="frag"` directive.
+ document.querySelectorAll('[data-name="frag"]').forEach((el) => {
+ el.setAttribute('x-if', 'true');
+ });
+ """
+
+css="""
+ .frag {
+ background: blue;
+ }
+ """
+
\ No newline at end of file
diff --git a/dev/concepts/advanced/provide_inject/index.html b/dev/concepts/advanced/provide_inject/index.html
index 14d4c415..5065f30d 100644
--- a/dev/concepts/advanced/provide_inject/index.html
+++ b/dev/concepts/advanced/provide_inject/index.html
@@ -1,4 +1,4 @@
- Prop drilling and provide / inject - Django-Components
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 272eed80..cd1c44aa 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
By default, components are rendered using the pair of {% component %} / {% endcomponent %} template tags:
{%component"button"href="..."disabled%}Click me!{%endcomponent%}
@@ -51,4 +51,4 @@
name,# e.g. 'button'tokens# e.g. ['href="..."', 'disabled'])
-
That's it! And once your TagFormatter is ready, don't forget to update the settings!
\ No newline at end of file
+
That's it! And once your TagFormatter is ready, don't forget to update the settings!
\ No newline at end of file
diff --git a/dev/concepts/advanced/typing_and_validation/index.html b/dev/concepts/advanced/typing_and_validation/index.html
index d5bd8c9f..50395d73 100644
--- a/dev/concepts/advanced/typing_and_validation/index.html
+++ b/dev/concepts/advanced/typing_and_validation/index.html
@@ -1,4 +1,4 @@
- Typing and validation - 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.
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:
Defining file paths relative to component or static dirs¤
As seen in the getting started example, to associate HTML/JS/CSS files with a component, you set them as template_name, Media.js and Media.css respectively:
# In a file [project root]/components/calendar/calendar.py
+ Defining HTML / JS / CSS files - Django-Components
Defining file paths relative to component or static dirs¤
As seen in the getting started example, to associate HTML/JS/CSS files with a component, you set them as template_name, Media.js and Media.css respectively:
# In a file [project root]/components/calendar/calendar.pyfromdjango_componentsimportComponent,register@register("calendar")
diff --git a/dev/concepts/fundamentals/html_attributes/index.html b/dev/concepts/fundamentals/html_attributes/index.html
index 119abfe5..19e03f17 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 also 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_name and Media. For example, here's the calendar component from above, defined in a single file:
[project root]/components/calendar.py
# In a file called [project root]/components/calendar.py
+ Single-file components - Django-Components
Components can also 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_name and Media. For example, here's the calendar component from above, defined in a single file:
[project root]/components/calendar.py
# In a file called [project root]/components/calendar.pyfromdjango_componentsimportComponent,register,types@register("calendar")
diff --git a/dev/concepts/fundamentals/slots/index.html b/dev/concepts/fundamentals/slots/index.html
index bcbd6327..20d2e78a 100644
--- a/dev/concepts/fundamentals/slots/index.html
+++ b/dev/concepts/fundamentals/slots/index.html
@@ -1,4 +1,4 @@
- Slots - Django-Components
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.
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
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 %}:
Next we will add CSS and JavaScript to our template.
Info
In django-components, using JS and CSS is as simple as defining them on the Component class. You don't have to insert the <script> and <link> tags into the HTML manually.
Behind the scenes, django-components keeps track of which components use which JS and CSS files. Thus, when a component is rendered on the page, the page will contain only the JS and CSS used by the components, and nothing more!
Next we will add CSS and JavaScript to our template.
Info
In django-components, using JS and CSS is as simple as defining them on the Component class. You don't have to insert the <script> and <link> tags into the HTML manually.
Behind the scenes, django-components keeps track of which components use which JS and CSS files. Thus, when a component is rendered on the page, the page will contain only the JS and CSS used by the components, and nothing more!
Our calendar component's looking great! But we just got a new assignment from our colleague - The calendar date needs to be shown on 3 different pages:
On one page, it needs to be shown as is
On the second, the date needs to be bold
On the third, the date needs to be in italics
As a reminder, this is what the component's template looks like:
Our calendar component's looking great! But we just got a new assignment from our colleague - The calendar date needs to be shown on 3 different pages:
On one page, it needs to be shown as is
On the second, the date needs to be bold
On the third, the date needs to be in italics
As a reminder, this is what the component's template looks like:
<divclass="calendar"> Today's date is <span>{{date}}</span></div>
There's many ways we could approach this:
Expose the date in a slot
Style .calendar > span differently on different pages
Pass a variable to the component that decides how the date is rendered
Create a new component
First two options are more flexible, because the custom styling is not baked into a component's implementation. And for the sake of demonstration, we'll solve this challenge with 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.