mirror of
https://github.com/django-components/django-components.git
synced 2025-08-04 06:18:17 +00:00
feat: add "simple", "prepend", and "append" render types (#1156)
* feat: add "simple", "prepend", and "append" render types * refactor: explicitly set strategy for "document" in tests
This commit is contained in:
parent
e74e1241ac
commit
bf7a204e92
16 changed files with 1210 additions and 408 deletions
|
@ -1,5 +1,5 @@
|
|||
Django-components provides a seamless integration with HTML fragments ([HTML over the wire](https://hotwired.dev/)),
|
||||
whether you're using HTMX, AlpineJS, or vanilla JavaScript.
|
||||
Django-components provides a seamless integration with HTML fragments with AJAX ([HTML over the wire](https://hotwired.dev/)),
|
||||
whether you're using jQuery, 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:
|
||||
|
@ -22,15 +22,17 @@ to render the fragment, django-components will:
|
|||
4. A library like HTMX, AlpineJS, or custom function inserts the new HTML into
|
||||
the correct place.
|
||||
|
||||
## Document and fragment types
|
||||
## Document and fragment strategies
|
||||
|
||||
Components support two modes of rendering - As a "document" or as a "fragment".
|
||||
Components support different "strategies" for rendering JS and CSS.
|
||||
|
||||
Two of them are used to enable HTML fragments - "document" and "fragment".
|
||||
|
||||
What's the difference?
|
||||
|
||||
### Document mode
|
||||
### Document strategy
|
||||
|
||||
Document mode assumes that the rendered components will be embedded into the HTML
|
||||
Document strategy assumes that the rendered components will be embedded into the HTML
|
||||
of the initial page load. This means that:
|
||||
|
||||
- The JS and CSS is embedded into the HTML as `<script>` and `<style>` tags
|
||||
|
@ -42,7 +44,7 @@ A component is rendered as a "document" when:
|
|||
- It is embedded inside a template as [`{% component %}`](../../reference/template_tags.md#component)
|
||||
- It is rendered with [`Component.render()`](../../../reference/api#django_components.Component.render)
|
||||
or [`Component.render_to_response()`](../../../reference/api#django_components.Component.render_to_response)
|
||||
with the `type` kwarg set to `"document"` (default)
|
||||
with the `deps_strategy` kwarg set to `"document"` (default)
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -55,13 +57,13 @@ MyTable.render(
|
|||
|
||||
MyTable.render(
|
||||
kwargs={...},
|
||||
type="document",
|
||||
deps_strategy="document",
|
||||
)
|
||||
```
|
||||
|
||||
### Fragment mode
|
||||
### Fragment strategy
|
||||
|
||||
Fragment mode assumes that the main HTML has already been rendered and loaded on the page.
|
||||
Fragment strategy 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.
|
||||
|
@ -75,14 +77,14 @@ A component is rendered as "fragment" when:
|
|||
|
||||
- It is rendered with [`Component.render()`](../../../reference/api#django_components.Component.render)
|
||||
or [`Component.render_to_response()`](../../../reference/api#django_components.Component.render_to_response)
|
||||
with the `type` kwarg set to `"fragment"`
|
||||
with the `deps_strategy` kwarg set to `"fragment"`
|
||||
|
||||
Example:
|
||||
|
||||
```py
|
||||
MyTable.render(
|
||||
kwargs={...},
|
||||
type="fragment",
|
||||
deps_strategy="fragment",
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -143,8 +145,8 @@ class Frag(Component):
|
|||
def get(self, request):
|
||||
return self.component.render_to_response(
|
||||
request=request,
|
||||
# IMPORTANT: Don't forget `type="fragment"`
|
||||
type="fragment",
|
||||
# IMPORTANT: Don't forget `deps_strategy="fragment"`
|
||||
deps_strategy="fragment",
|
||||
)
|
||||
|
||||
template = """
|
||||
|
@ -230,8 +232,8 @@ class Frag(Component):
|
|||
def get(self, request):
|
||||
return self.component.render_to_response(
|
||||
request=request,
|
||||
# IMPORTANT: Don't forget `type="fragment"`
|
||||
type="fragment",
|
||||
# IMPORTANT: Don't forget `deps_strategy="fragment"`
|
||||
deps_strategy="fragment",
|
||||
)
|
||||
|
||||
# NOTE: We wrap the actual fragment in a template tag with x-if="false" to prevent it
|
||||
|
@ -329,8 +331,8 @@ class Frag(Component):
|
|||
def get(self, request):
|
||||
return self.component.render_to_response(
|
||||
request=request,
|
||||
# IMPORTANT: Don't forget `type="fragment"`
|
||||
type="fragment",
|
||||
# IMPORTANT: Don't forget `deps_strategy="fragment"`
|
||||
deps_strategy="fragment",
|
||||
)
|
||||
|
||||
template = """
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
### JS and CSS output locations
|
||||
## JS and CSS output locations
|
||||
|
||||
If:
|
||||
|
||||
|
@ -220,3 +220,203 @@ with [`Component.render()`](#TODO) and inserting them into larger structures.
|
|||
- Directly passing rendered HTML to [`render_dependencies()`](#TODO)
|
||||
3. If you pre-render one component to pass it into another, the pre-rendered component must be rendered with
|
||||
[`render_dependencies=False`](#TODO).
|
||||
|
||||
## Dependencies strategies
|
||||
|
||||
The rendered HTML may be used in different contexts (browser, email, etc).
|
||||
If your components use JS and CSS scripts, you may need to handle them differently.
|
||||
|
||||
[`render()`](../../../reference/api/#django_components.Component.render) and [`render_to_response()`](../../../reference/api/#django_components.Component.render_to_response)
|
||||
accept a `deps_strategy` parameter, which controls where and how the JS / CSS are inserted into the HTML.
|
||||
|
||||
The `deps_strategy` parameter is set at the root of a component render tree, which is why it is not available for
|
||||
the [`{% component %}`](../../../reference/template_tags#component) tag.
|
||||
|
||||
!!! info
|
||||
|
||||
The `deps_strategy` parameter is ultimately passed to [`render_dependencies()`](../../../reference/api/#django_components.render_dependencies).
|
||||
|
||||
There are five dependencies strategies:
|
||||
|
||||
- [`document`](../../advanced/rendering_js_css#document) (default)
|
||||
- Smartly inserts JS / CSS into placeholders or into `<head>` and `<body>` tags.
|
||||
- Inserts extra script to allow `fragment` strategy to work.
|
||||
- Assumes the HTML will be rendered in a JS-enabled browser.
|
||||
- [`fragment`](../../advanced/rendering_js_css#fragment)
|
||||
- A lightweight HTML fragment to be inserted into a document with AJAX.
|
||||
- No JS / CSS included.
|
||||
- [`simple`](../../advanced/rendering_js_css#simple)
|
||||
- Smartly insert JS / CSS into placeholders or into `<head>` and `<body>` tags.
|
||||
- No extra script loaded.
|
||||
- [`prepend`](../../advanced/rendering_js_css#prepend)
|
||||
- Insert JS / CSS before the rendered HTML.
|
||||
- No extra script loaded.
|
||||
- [`append`](../../advanced/rendering_js_css#append)
|
||||
- Insert JS / CSS after the rendered HTML.
|
||||
- No extra script loaded.
|
||||
|
||||
### `document`
|
||||
|
||||
`deps_strategy="document"` is the default. Use this if you are rendering a whole page, or if no other option suits better.
|
||||
|
||||
```python
|
||||
html = Button.render(deps_strategy="document")
|
||||
```
|
||||
|
||||
When you render a component tree with the `"document"` strategy, it is expected that:
|
||||
|
||||
- The HTML will be rendered at page load.
|
||||
- The HTML will be inserted into a page / browser where JS can be executed.
|
||||
|
||||
**Location:**
|
||||
|
||||
JS and CSS is inserted:
|
||||
|
||||
- Preferentially into JS / CSS placeholders like [`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies)
|
||||
- Otherwise, JS into `<body>` element, and CSS into `<head>` element
|
||||
- If neither found, JS / CSS are NOT inserted
|
||||
|
||||
**Included scripts:**
|
||||
|
||||
For the `"document"` strategy, the JS and CSS is set up to avoid any delays when the end user loads
|
||||
the page in the browser:
|
||||
|
||||
- Components' primary JS and CSS scripts ([`Component.js`](../../../reference/api/#django_components.Component.js)
|
||||
and [`Component.css`](../../../reference/api/#django_components.Component.css)) - fully inlined:
|
||||
|
||||
```html
|
||||
<script>
|
||||
console.log("Hello from Button!");
|
||||
</script>
|
||||
<style>
|
||||
.button {
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
- Components' secondary JS and CSS scripts
|
||||
([`Component.Media`](../../../reference/api/#django_components.Component.Media)) - inserted as links:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://example.com/styles.css" />
|
||||
<script src="https://example.com/script.js"></script>
|
||||
```
|
||||
|
||||
- A JS script is injected to manage component dependencies, enabling lazy loading of JS and CSS
|
||||
for HTML fragments.
|
||||
|
||||
!!! info
|
||||
|
||||
This strategy is required for fragments to work properly, as it sets up the dependency manager that fragments rely on.
|
||||
|
||||
!!! note "How the dependency manager works"
|
||||
|
||||
The dependency manager is a JS script that keeps track of all the JS and CSS dependencies that have already been loaded.
|
||||
|
||||
When a fragment is inserted into the page, it will also insert a JSON `<script>` tag with fragment metadata.
|
||||
|
||||
The dependency manager will pick up on that, and check which scripts the fragment needs.
|
||||
|
||||
It will then fetch only the scripts that haven't been loaded yet.
|
||||
|
||||
### `fragment`
|
||||
|
||||
`deps_strategy="fragment"` is used when rendering a piece of HTML that will be inserted into a page
|
||||
that has already been rendered with the [`"document"`](#document) strategy:
|
||||
|
||||
```python
|
||||
fragment = MyComponent.render(deps_strategy="fragment")
|
||||
```
|
||||
|
||||
The HTML of fragments is very lightweight because it doesn't include the JS and CSS scripts
|
||||
of the rendered components.
|
||||
|
||||
With fragments, even if a component has JS and CSS, you can insert the same component into a page
|
||||
hundreds of times, and the JS and CSS will only ever be loaded once.
|
||||
|
||||
This is intended for dynamic content that's loaded with AJAX after the initial page load, such as with [jQuery](https://jquery.com/), [HTMX](https://htmx.org/), [AlpineJS](https://alpinejs.dev/) or similar libraries.
|
||||
|
||||
**Location:**
|
||||
|
||||
None. The fragment's JS and CSS files will be loaded dynamically into the page.
|
||||
|
||||
**Included scripts:**
|
||||
|
||||
- A special JSON `<script>` tag that tells the dependency manager what JS and CSS to load.
|
||||
|
||||
### `simple`
|
||||
|
||||
`deps_strategy="simple"` is used either for non-browser use cases, or when you don't want to use the dependency manager.
|
||||
|
||||
Practically, this is the same as the [`"document"`](#document) strategy, except that the dependency manager is not used.
|
||||
|
||||
```python
|
||||
html = MyComponent.render(deps_strategy="simple")
|
||||
```
|
||||
|
||||
**Location:**
|
||||
|
||||
JS and CSS is inserted:
|
||||
|
||||
- Preferentially into JS / CSS placeholders like [`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies)
|
||||
- Otherwise, JS into `<body>` element, and CSS into `<head>` element
|
||||
- If neither found, JS / CSS are NOT inserted
|
||||
|
||||
**Included scripts:**
|
||||
|
||||
- Components' primary JS and CSS scripts ([`Component.js`](../../../reference/api/#django_components.Component.js)
|
||||
and [`Component.css`](../../../reference/api/#django_components.Component.css)) - fully inlined:
|
||||
|
||||
```html
|
||||
<script>
|
||||
console.log("Hello from Button!");
|
||||
</script>
|
||||
<style>
|
||||
.button {
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
- Components' secondary JS and CSS scripts
|
||||
([`Component.Media`](../../../reference/api/#django_components.Component.Media)) - inserted as links:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://example.com/styles.css" />
|
||||
<script src="https://example.com/script.js"></script>
|
||||
```
|
||||
|
||||
- No extra scripts are inserted.
|
||||
|
||||
### `prepend`
|
||||
|
||||
This is the same as [`"simple"`](#simple), but placeholders like [`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies) and HTML tags `<head>` and `<body>` are all ignored. The JS and CSS are **always** inserted **before** the rendered content.
|
||||
|
||||
```python
|
||||
html = MyComponent.render(deps_strategy="prepend")
|
||||
```
|
||||
|
||||
**Location:**
|
||||
|
||||
JS and CSS is **always** inserted before the rendered content.
|
||||
|
||||
**Included scripts:**
|
||||
|
||||
Same as for the [`"simple"`](#simple) strategy.
|
||||
|
||||
### `append`
|
||||
|
||||
This is the same as [`"simple"`](#simple), but placeholders like [`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies) and HTML tags `<head>` and `<body>` are all ignored. The JS and CSS are **always** inserted **after** the rendered content.
|
||||
|
||||
```python
|
||||
html = MyComponent.render(deps_strategy="append")
|
||||
```
|
||||
|
||||
**Location:**
|
||||
|
||||
JS and CSS is **always** inserted after the rendered content.
|
||||
|
||||
**Included scripts:**
|
||||
|
||||
Same as for the [`"simple"`](#simple) strategy.
|
||||
|
|
|
@ -145,7 +145,7 @@ Here is how the HTML is post-processed:
|
|||
</div>
|
||||
```
|
||||
|
||||
3. **Insert JS and CSS**: After the HTML is rendered, Django Components handles inserting JS and CSS dependencies into the page based on the [render type](../rendering_components/#render-types) (document, fragment, or inline).
|
||||
3. **Insert JS and CSS**: After the HTML is rendered, Django Components handles inserting JS and CSS dependencies into the page based on the [dependencies rendering strategy](../rendering_components/#dependencies-rendering) (document, fragment, or inline).
|
||||
|
||||
For example, if your component contains the
|
||||
[`{% component_js_dependencies %}`](../../reference/template_tags.md#component_js_dependencies)
|
||||
|
|
|
@ -244,7 +244,7 @@ Button.render(
|
|||
- `kwargs` - Keyword arguments to pass to the component (as a dictionary)
|
||||
- `slots` - Slot content to pass to the component (as a dictionary)
|
||||
- `context` - Django context for rendering (can be a dictionary or a `Context` object)
|
||||
- `type` - Type of rendering (default: `"document"`)
|
||||
- `deps_strategy` - Dependencies rendering strategy (default: `"document"`)
|
||||
- `request` - HTTP request object, used for context processors (optional)
|
||||
- `escape_slots_content` - Whether to HTML-escape slot content (default: `True`)
|
||||
- `render_dependencies` - Whether to process JS and CSS dependencies (default: `True`)
|
||||
|
@ -336,102 +336,66 @@ response = MyComponent.render_to_response()
|
|||
assert isinstance(response, MyHttpResponse)
|
||||
```
|
||||
|
||||
## Render types
|
||||
## Dependencies rendering
|
||||
|
||||
The rendered HTML may be used in different contexts (browser, email, etc).
|
||||
If your components use JS and CSS scripts, you need to handle them differently.
|
||||
The rendered HTML may be used in different contexts (browser, email, etc), and each may need different handling of JS and CSS scripts.
|
||||
|
||||
[`render()`](../../../reference/api/#django_components.Component.render) and [`render_to_response()`](../../../reference/api/#django_components.Component.render_to_response)
|
||||
accept a `type` parameter, which controls this behavior.
|
||||
accept a `deps_strategy` parameter, which controls where and how the JS / CSS are inserted into the HTML.
|
||||
|
||||
The `type` parameter is set at the root of a component render tree, which is why it is not available for
|
||||
the [`{% component %}`](../../../reference/template_tags#component) tag.
|
||||
The `deps_strategy` parameter is ultimately passed to [`render_dependencies()`](../../../reference/api/#django_components.render_dependencies).
|
||||
|
||||
Learn more about [Rendering JS / CSS](../../advanced/rendering_js_css).
|
||||
|
||||
There are five dependencies rendering strategies:
|
||||
|
||||
- [`document`](../../advanced/rendering_js_css#document) (default)
|
||||
- Smartly inserts JS / CSS into placeholders ([`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies)) or into `<head>` and `<body>` tags.
|
||||
- Inserts extra script to allow `fragment` components to work.
|
||||
- Assumes the HTML will be rendered in a JS-enabled browser.
|
||||
- [`fragment`](../../advanced/rendering_js_css#fragment)
|
||||
- A lightweight HTML fragment to be inserted into a document with AJAX.
|
||||
- Assumes the page was already rendered with `"document"` strategy.
|
||||
- No JS / CSS included.
|
||||
- [`simple`](../../advanced/rendering_js_css#simple)
|
||||
- Smartly insert JS / CSS into placeholders ([`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies)) or into `<head>` and `<body>` tags.
|
||||
- No extra script loaded.
|
||||
- [`prepend`](../../advanced/rendering_js_css#prepend)
|
||||
- Insert JS / CSS before the rendered HTML.
|
||||
- Ignores the placeholders ([`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies)) and any `<head>`/`<body>` HTML tags.
|
||||
- No extra script loaded.
|
||||
- [`append`](../../advanced/rendering_js_css#append)
|
||||
- Insert JS / CSS after the rendered HTML.
|
||||
- Ignores the placeholders ([`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies)) and any `<head>`/`<body>` HTML tags.
|
||||
- No extra script loaded.
|
||||
|
||||
!!! info
|
||||
|
||||
The `type` parameter is ultimately passed to [`render_dependencies()`](../../../reference/api/#django_components.render_dependencies).
|
||||
Learn more about [Rendering JS / CSS](../../advanced/rendering_js_css).
|
||||
You can use the `"prepend"` and `"append"` strategies to force to output JS / CSS for components
|
||||
that don't have neither the placeholders like [`{% component_js_dependencies %}`](../../../reference/template_tags#component_js_dependencies), nor any `<head>`/`<body>` HTML tags:
|
||||
|
||||
There are three render types:
|
||||
|
||||
### `document`
|
||||
|
||||
`type="document"` is the default. Use this if you are rendering a whole page, or if no other option suits better.
|
||||
|
||||
```python
|
||||
html = Button.render(type="document")
|
||||
```
|
||||
|
||||
When you render a component tree with the `"document"` type, it is expected that:
|
||||
|
||||
- The HTML will be rendered at page load.
|
||||
- The HTML will be inserted into a page / browser where JS can be executed.
|
||||
|
||||
With this setting, the JS and CSS is set up to avoid any delays for end users:
|
||||
|
||||
- Components' primary JS and CSS scripts ([`Component.js`](../../../reference/api/#django_components.Component.js)
|
||||
and [`Component.css`](../../../reference/api/#django_components.Component.css)) are inlined into the rendered HTML.
|
||||
|
||||
```html
|
||||
<script>
|
||||
console.log("Hello from Button!");
|
||||
</script>
|
||||
<style>
|
||||
.button {
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
```py
|
||||
rendered = Calendar.render_to_response(
|
||||
request=request,
|
||||
kwargs={
|
||||
"date": request.GET.get("date", ""),
|
||||
},
|
||||
deps_strategy="append",
|
||||
)
|
||||
```
|
||||
|
||||
- Components' secondary JS and CSS scripts ([`Component.Media`](../../../reference/api/#django_components.Component.Media))
|
||||
are inserted into the rendered HTML as links.
|
||||
Renders something like this:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://example.com/styles.css" />
|
||||
<script src="https://example.com/script.js"></script>
|
||||
<!-- Calendar component -->
|
||||
<div class="calendar">
|
||||
...
|
||||
</div>
|
||||
<!-- Appended JS / CSS -->
|
||||
<script src="..."></script>
|
||||
<link href="..."></link>
|
||||
```
|
||||
|
||||
- A JS script is injected to manage component dependencies, enabling lazy loading of JS and CSS
|
||||
for HTML fragments.
|
||||
|
||||
!!! info
|
||||
|
||||
This render type is required for fragments to work properly, as it sets up the dependency manager that fragments rely on.
|
||||
|
||||
!!! note "How the dependency manager works"
|
||||
|
||||
The dependency manager is a JS script that keeps track of all the JS and CSS dependencies that have already been loaded.
|
||||
|
||||
When a fragment is inserted into the page, it will also insert a JSON `<script>` tag with fragment metadata.
|
||||
|
||||
The dependency manager will pick up on that, and check which scripts the fragment needs.
|
||||
|
||||
It will then fetch only the scripts that haven't been loaded yet.
|
||||
|
||||
### `fragment`
|
||||
|
||||
`type="fragment"` is used when rendering a piece of HTML that will be inserted into a page
|
||||
that has already been rendered with the `"document"` type:
|
||||
|
||||
```python
|
||||
fragment = MyComponent.render(type="fragment")
|
||||
```
|
||||
|
||||
The HTML of fragments is very lightweight because it doesn't include the JS and CSS scripts
|
||||
of the rendered components.
|
||||
|
||||
With fragments, even if a component has JS and CSS, you can insert the same component into a page
|
||||
hundreds of times, and the JS and CSS will only ever be loaded once.
|
||||
|
||||
The fragment type:
|
||||
|
||||
- Does not include the dependency manager script (assumes it's already loaded)
|
||||
- Does not inline JS or CSS directly in the HTML
|
||||
- Includes a special JSON `<script>` tag that tells the dependency manager what JS and CSS to load
|
||||
- The dependency manager will fetch only scripts that haven't been loaded yet
|
||||
|
||||
This is intended for dynamic content that's loaded after the initial page load, such as with [HTMX](https://htmx.org/) or similar.
|
||||
|
||||
## Passing context
|
||||
|
||||
The [`render()`](../../../reference/api/#django_components.Component.render) and [`render_to_response()`](../../../reference/api/#django_components.Component.render_to_response) methods accept an optional `context` argument.
|
||||
|
|
|
@ -67,6 +67,10 @@
|
|||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.DependenciesStrategy
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.Empty
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
|
|
@ -20,7 +20,7 @@ Import as
|
|||
|
||||
|
||||
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1066" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1069" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
|
@ -43,7 +43,7 @@ If you insert this tag multiple times, ALL CSS links will be duplicately inserte
|
|||
|
||||
|
||||
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1088" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1091" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
|
@ -67,7 +67,7 @@ If you insert this tag multiple times, ALL JS scripts will be duplicately insert
|
|||
|
||||
|
||||
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L2619" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L2794" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
|
@ -75,84 +75,48 @@ Renders one of the components that was previously registered with
|
|||
[`@register()`](./api.md#django_components.register)
|
||||
decorator.
|
||||
|
||||
**Args:**
|
||||
The `{% component %}` tag takes:
|
||||
|
||||
- `name` (str, required): Registered name of the component to render
|
||||
- All other args and kwargs are defined based on the component itself.
|
||||
|
||||
If you defined a component `"my_table"`
|
||||
|
||||
```python
|
||||
from django_component import Component, register
|
||||
|
||||
@register("my_table")
|
||||
class MyTable(Component):
|
||||
template = """
|
||||
<table>
|
||||
<thead>
|
||||
{% for header in headers %}
|
||||
<th>{{ header }}</th>
|
||||
{% endfor %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
{% for cell in row %}
|
||||
<td>{{ cell }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
"""
|
||||
|
||||
def get_context_data(self, rows: List, headers: List):
|
||||
return {
|
||||
"rows": rows,
|
||||
"headers": headers,
|
||||
}
|
||||
```
|
||||
|
||||
Then you can render this component by referring to `MyTable` via its
|
||||
registered name `"my_table"`:
|
||||
- Component's registered name as the first positional argument,
|
||||
- Followed by any number of positional and keyword arguments.
|
||||
|
||||
```django
|
||||
{% component "my_table" rows=rows headers=headers ... / %}
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
{% component "button" name="John" job="Developer" / %}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Component input
|
||||
The component name must be a string literal.
|
||||
|
||||
Positional and keyword arguments can be literals or template variables.
|
||||
|
||||
The component name must be a single- or double-quotes string and must
|
||||
be either:
|
||||
|
||||
- The first positional argument after `component`:
|
||||
|
||||
```django
|
||||
{% component "my_table" rows=rows headers=headers ... / %}
|
||||
```
|
||||
|
||||
- Passed as kwarg `name`:
|
||||
|
||||
```django
|
||||
{% component rows=rows headers=headers name="my_table" ... / %}
|
||||
```
|
||||
|
||||
### Inserting into slots
|
||||
### Inserting slot fills
|
||||
|
||||
If the component defined any [slots](../concepts/fundamentals/slots.md), you can
|
||||
pass in the content to be placed inside those slots by inserting [`{% fill %}`](#fill) tags,
|
||||
directly within the `{% component %}` tag:
|
||||
"fill" these slots by placing the [`{% fill %}`](#fill) tags within the `{% component %}` tag:
|
||||
|
||||
```django
|
||||
{% component "my_table" rows=rows headers=headers ... / %}
|
||||
{% component "my_table" rows=rows headers=headers %}
|
||||
{% fill "pagination" %}
|
||||
< 1 | 2 | 3 >
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
```
|
||||
|
||||
You can even nest [`{% fill %}`](#fill) tags within
|
||||
[`{% if %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#if),
|
||||
[`{% for %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#for)
|
||||
and other tags:
|
||||
|
||||
```django
|
||||
{% component "my_table" rows=rows headers=headers %}
|
||||
{% if rows %}
|
||||
{% fill "pagination" %}
|
||||
< 1 | 2 | 3 >
|
||||
{% endfill %}
|
||||
{% endif %}
|
||||
{% endcomponent %}
|
||||
```
|
||||
|
||||
### Isolating components
|
||||
|
||||
By default, components behave similarly to Django's
|
||||
|
@ -166,6 +130,36 @@ can access only the data that was explicitly passed to it:
|
|||
{% component "name" positional_arg keyword_arg=value ... only %}
|
||||
```
|
||||
|
||||
Alternatively, you can set all components to be isolated by default, by setting
|
||||
[`context_behavior`](../settings#django_components.app_settings.ComponentsSettings.context_behavior)
|
||||
to `"isolated"` in your settings:
|
||||
|
||||
```python
|
||||
# settings.py
|
||||
COMPONENTS = {
|
||||
"context_behavior": "isolated",
|
||||
}
|
||||
```
|
||||
|
||||
### Omitting the `component` keyword
|
||||
|
||||
If you would like to omit the `component` keyword, and simply refer to your
|
||||
components by their registered names:
|
||||
|
||||
```django
|
||||
{% button name="John" job="Developer" / %}
|
||||
```
|
||||
|
||||
You can do so by setting the "shorthand" [Tag formatter](../../concepts/advanced/tag_formatters)
|
||||
in the settings:
|
||||
|
||||
```python
|
||||
# settings.py
|
||||
COMPONENTS = {
|
||||
"tag_formatter": "django_components.component_shorthand_formatter",
|
||||
}
|
||||
```
|
||||
|
||||
## fill
|
||||
|
||||
```django
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue