mirror of
https://github.com/django-components/django-components.git
synced 2025-08-28 01:44:05 +00:00
refecator: move defaults applying back to ext, raise on passing Slot to Slot, and docs cleanup (#1214)
* refecator: move defaults applying back to ext, raise on passing Slot to Slot, and docs cleanup * docs: fix typo
This commit is contained in:
parent
bae0f28813
commit
55b1c8bc62
14 changed files with 317 additions and 147 deletions
|
@ -12,8 +12,10 @@ Summary:
|
||||||
`get_context_data()` is now deprecated but will remain until v2.
|
`get_context_data()` is now deprecated but will remain until v2.
|
||||||
- Slots API polished and prepared for v1.
|
- Slots API polished and prepared for v1.
|
||||||
- Merged `Component.Url` with `Component.View`
|
- Merged `Component.Url` with `Component.View`
|
||||||
- Added `Component.args`, `Component.kwargs`, `Component.slots`
|
- Added `Component.args`, `Component.kwargs`, `Component.slots`, `Component.context`
|
||||||
- Added `{{ component_vars.args }}`, `{{ component_vars.kwargs }}`, `{{ component_vars.slots }}`
|
- Added `{{ component_vars.args }}`, `{{ component_vars.kwargs }}`, `{{ component_vars.slots }}`
|
||||||
|
- You should no longer instantiate `Component` instances. Instead, call `Component.render()` or `Component.render_to_response()` directly.
|
||||||
|
- Component caching can now consider slots (opt-in)
|
||||||
- And lot more...
|
- And lot more...
|
||||||
|
|
||||||
#### 🚨📢 BREAKING CHANGES
|
#### 🚨📢 BREAKING CHANGES
|
||||||
|
@ -867,6 +869,8 @@ Summary:
|
||||||
can now be accessed also outside of the render call. So now its possible to take the component
|
can now be accessed also outside of the render call. So now its possible to take the component
|
||||||
instance out of `get_template_data()` (although this is not recommended).
|
instance out of `get_template_data()` (although this is not recommended).
|
||||||
|
|
||||||
|
- Passing `Slot` instance to `Slot` constructor raises an error.
|
||||||
|
|
||||||
#### Fix
|
#### Fix
|
||||||
|
|
||||||
- Fix bug: Context processors data was being generated anew for each component. Now the data is correctly created once and reused across components with the same request ([#1165](https://github.com/django-components/django-components/issues/1165)).
|
- Fix bug: Context processors data was being generated anew for each component. Now the data is correctly created once and reused across components with the same request ([#1165](https://github.com/django-components/django-components/issues/1165)).
|
||||||
|
|
|
@ -640,15 +640,25 @@ Table.render(
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Slot class can be instantiated with a function, a string, or from another
|
Slot class can be instantiated with a function or a string:
|
||||||
[`Slot`](../../../reference/api#django_components.Slot) instance:
|
|
||||||
|
|
||||||
```py
|
```py
|
||||||
slot1 = Slot(lambda ctx: f"Hello, {ctx.data['name']}!")
|
slot1 = Slot(lambda ctx: f"Hello, {ctx.data['name']}!")
|
||||||
slot2 = Slot("Hello, world!")
|
slot2 = Slot("Hello, world!")
|
||||||
slot3 = Slot(slot1)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
Passing a [`Slot`](../../../reference/api#django_components.Slot) instance to the `Slot`
|
||||||
|
constructor results in an error:
|
||||||
|
|
||||||
|
```py
|
||||||
|
slot = Slot("Hello")
|
||||||
|
|
||||||
|
# Raises an error
|
||||||
|
slot2 = Slot(slot)
|
||||||
|
```
|
||||||
|
|
||||||
### Rendering slots
|
### Rendering slots
|
||||||
|
|
||||||
**Python**
|
**Python**
|
||||||
|
@ -713,12 +723,13 @@ html = slot()
|
||||||
|
|
||||||
When accessing slots from within [`Component`](../../../reference/api#django_components.Component) methods,
|
When accessing slots from within [`Component`](../../../reference/api#django_components.Component) methods,
|
||||||
the [`Slot`](../../../reference/api#django_components.Slot) instances are populated
|
the [`Slot`](../../../reference/api#django_components.Slot) instances are populated
|
||||||
with extra metadata [`component_name`](../../../reference/api#django_components.Slot.component_name)
|
with extra metadata [`component_name`](../../../reference/api#django_components.Slot.component_name),
|
||||||
and [`slot_name`](../../../reference/api#django_components.Slot.slot_name).
|
[`slot_name`](../../../reference/api#django_components.Slot.slot_name), and
|
||||||
|
[`nodelist`](../../../reference/api#django_components.Slot.nodelist).
|
||||||
|
|
||||||
These are used solely for debugging.
|
These are used for debugging, such as printing the path to the slot in the component tree.
|
||||||
|
|
||||||
In fact, you can set these fields too when creating new slots:
|
When you create a slot, you can set these fields too:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# Either at slot creation
|
# Either at slot creation
|
||||||
|
@ -753,19 +764,6 @@ slot = Slot("Hello!")
|
||||||
print(slot.nodelist) # <django.template.Nodelist: ['Hello!']>
|
print(slot.nodelist) # <django.template.Nodelist: ['Hello!']>
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info
|
|
||||||
|
|
||||||
If you pass a [`Slot`](../../../reference/api#django_components.Slot) instance to the constructor,
|
|
||||||
the inner slot will be "unwrapped" and its `Slot.contents` will be used instead.
|
|
||||||
|
|
||||||
```py
|
|
||||||
slot = Slot("Hello")
|
|
||||||
print(slot.contents) # "Hello"
|
|
||||||
|
|
||||||
slot2 = Slot(slot)
|
|
||||||
print(slot2.contents) # "Hello"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Escaping slots content
|
### Escaping slots content
|
||||||
|
|
||||||
Slots content are automatically escaped by default to prevent XSS attacks.
|
Slots content are automatically escaped by default to prevent XSS attacks.
|
||||||
|
|
|
@ -87,6 +87,14 @@
|
||||||
options:
|
options:
|
||||||
show_if_no_docstring: true
|
show_if_no_docstring: true
|
||||||
|
|
||||||
|
::: django_components.SlotContext
|
||||||
|
options:
|
||||||
|
show_if_no_docstring: true
|
||||||
|
|
||||||
|
::: django_components.SlotFallback
|
||||||
|
options:
|
||||||
|
show_if_no_docstring: true
|
||||||
|
|
||||||
::: django_components.SlotFunc
|
::: django_components.SlotFunc
|
||||||
options:
|
options:
|
||||||
show_if_no_docstring: true
|
show_if_no_docstring: true
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Commands
|
# Commands
|
||||||
|
|
||||||
These are all the [Django management commands](https://docs.djangoproject.com/en/5.1/ref/django-admin)
|
These are all the [Django management commands](https://docs.djangoproject.com/en/5.2/ref/django-admin)
|
||||||
that will be added by installing `django_components`:
|
that will be added by installing `django_components`:
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,9 +54,7 @@ python manage.py components ext run <extension> <command>
|
||||||
## `components create`
|
## `components create`
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
usage: python manage.py components create [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose]
|
usage: python manage.py components create [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose] [--dry-run] name
|
||||||
[--dry-run]
|
|
||||||
name
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -238,7 +236,7 @@ List all extensions.
|
||||||
- `--columns COLUMNS`
|
- `--columns COLUMNS`
|
||||||
- Comma-separated list of columns to show. Available columns: name. Defaults to `--columns name`.
|
- Comma-separated list of columns to show. Available columns: name. Defaults to `--columns name`.
|
||||||
- `-s`, `--simple`
|
- `-s`, `--simple`
|
||||||
- Only show table data, without headers. Use this option for generating machine- readable output.
|
- Only show table data, without headers. Use this option for generating machine-readable output.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -386,7 +384,7 @@ usage: python manage.py components list [-h] [--all] [--columns COLUMNS] [-s]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/commands/list.py#L141" target="_blank">See source code</a>
|
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/commands/list.py#L89" target="_blank">See source code</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -401,7 +399,7 @@ List all components created in this project.
|
||||||
- `--columns COLUMNS`
|
- `--columns COLUMNS`
|
||||||
- Comma-separated list of columns to show. Available columns: name, full_name, path. Defaults to `--columns full_name,path`.
|
- Comma-separated list of columns to show. Available columns: name, full_name, path. Defaults to `--columns full_name,path`.
|
||||||
- `-s`, `--simple`
|
- `-s`, `--simple`
|
||||||
- Only show table data, without headers. Use this option for generating machine- readable output.
|
- Only show table data, without headers. Use this option for generating machine-readable output.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -463,9 +461,8 @@ ProjectDashboardAction project.components.dashboard_action.ProjectDashboardAc
|
||||||
## `upgradecomponent`
|
## `upgradecomponent`
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
usage: upgradecomponent [-h] [--path PATH] [--version] [-v {0,1,2,3}] [--settings SETTINGS]
|
usage: upgradecomponent [-h] [--path PATH] [--version] [-v {0,1,2,3}] [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color]
|
||||||
[--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color]
|
[--force-color] [--skip-checks]
|
||||||
[--skip-checks]
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -509,10 +506,8 @@ Deprecated. Use `components upgrade` instead.
|
||||||
## `startcomponent`
|
## `startcomponent`
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
usage: startcomponent [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force]
|
usage: startcomponent [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose] [--dry-run] [--version] [-v {0,1,2,3}]
|
||||||
[--verbose] [--dry-run] [--version] [-v {0,1,2,3}] [--settings SETTINGS]
|
[--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color] [--skip-checks]
|
||||||
[--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color]
|
|
||||||
[--skip-checks]
|
|
||||||
name
|
name
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -85,9 +85,9 @@ name | type | description
|
||||||
`component` | [`Component`](../api#django_components.Component) | The Component instance that received the input and is being rendered
|
`component` | [`Component`](../api#django_components.Component) | The Component instance that received the input and is being rendered
|
||||||
`component_cls` | [`Type[Component]`](../api#django_components.Component) | The Component class
|
`component_cls` | [`Type[Component]`](../api#django_components.Component) | The Component class
|
||||||
`component_id` | `str` | The unique identifier for this component instance
|
`component_id` | `str` | The unique identifier for this component instance
|
||||||
`context` | [`Context`](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.Context) | The Django template Context object
|
`context` | [`Context`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.Context) | The Django template Context object
|
||||||
`kwargs` | `Dict` | Dictionary of keyword arguments passed to the component
|
`kwargs` | `Dict` | Dictionary of keyword arguments passed to the component
|
||||||
`slots` | `Dict` | Dictionary of slot definitions
|
`slots` | `Dict[str, Slot]` | Dictionary of slot definitions
|
||||||
|
|
||||||
::: django_components.extension.ComponentExtension.on_component_registered
|
::: django_components.extension.ComponentExtension.on_component_registered
|
||||||
options:
|
options:
|
||||||
|
@ -181,6 +181,30 @@ name | type | description
|
||||||
--|--|--
|
--|--|--
|
||||||
`registry` | [`ComponentRegistry`](../api#django_components.ComponentRegistry) | The to-be-deleted ComponentRegistry instance
|
`registry` | [`ComponentRegistry`](../api#django_components.ComponentRegistry) | The to-be-deleted ComponentRegistry instance
|
||||||
|
|
||||||
|
::: django_components.extension.ComponentExtension.on_slot_rendered
|
||||||
|
options:
|
||||||
|
heading_level: 3
|
||||||
|
show_root_heading: true
|
||||||
|
show_signature: true
|
||||||
|
separate_signature: true
|
||||||
|
show_symbol_type_heading: false
|
||||||
|
show_symbol_type_toc: false
|
||||||
|
show_if_no_docstring: true
|
||||||
|
show_labels: false
|
||||||
|
|
||||||
|
**Available data:**
|
||||||
|
|
||||||
|
name | type | description
|
||||||
|
--|--|--
|
||||||
|
`component` | [`Component`](../api#django_components.Component) | The Component instance that contains the `{% slot %}` tag
|
||||||
|
`component_cls` | [`Type[Component]`](../api#django_components.Component) | The Component class that contains the `{% slot %}` tag
|
||||||
|
`component_id` | `str` | The unique identifier for this component instance
|
||||||
|
`result` | `SlotResult` | The rendered result of the slot
|
||||||
|
`slot` | `Slot` | The Slot instance that was rendered
|
||||||
|
`slot_is_default` | `bool` | Whether the slot is default
|
||||||
|
`slot_is_required` | `bool` | Whether the slot is required
|
||||||
|
`slot_name` | `str` | The name of the `{% slot %}` tag
|
||||||
|
|
||||||
## Objects
|
## Objects
|
||||||
|
|
||||||
::: django_components.extension.OnComponentClassCreatedContext
|
::: django_components.extension.OnComponentClassCreatedContext
|
||||||
|
|
|
@ -6,7 +6,7 @@ Below are the signals that are sent by or during the use of django-components.
|
||||||
|
|
||||||
## template_rendered
|
## template_rendered
|
||||||
|
|
||||||
Django's [`template_rendered`](https://docs.djangoproject.com/en/5.1/ref/signals/#template-rendered) signal.
|
Django's [`template_rendered`](https://docs.djangoproject.com/en/5.2/ref/signals/#template-rendered) signal.
|
||||||
This signal is sent when a template is rendered.
|
This signal is sent when a template is rendered.
|
||||||
|
|
||||||
Django-components triggers this signal when a component is rendered. If there are nested components,
|
Django-components triggers this signal when a component is rendered. If there are nested components,
|
||||||
|
|
|
@ -20,7 +20,7 @@ Import as
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1024" target="_blank">See source code</a>
|
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1022" 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#L1046" target="_blank">See source code</a>
|
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1044" 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#L2784" target="_blank">See source code</a>
|
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L3164" target="_blank">See source code</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ and other tags:
|
||||||
### Isolating components
|
### Isolating components
|
||||||
|
|
||||||
By default, components behave similarly to Django's
|
By default, components behave similarly to Django's
|
||||||
[`{% include %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#include),
|
[`{% include %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#include),
|
||||||
and the template inside the component has access to the variables defined in the outer template.
|
and the template inside the component has access to the variables defined in the outer template.
|
||||||
|
|
||||||
You can selectively isolate a component, using the `only` flag, so that the inner template
|
You can selectively isolate a component, using the `only` flag, so that the inner template
|
||||||
|
@ -163,34 +163,32 @@ COMPONENTS = {
|
||||||
## fill
|
## fill
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{% fill name: str, *, data: Optional[str] = None, default: Optional[str] = None %}
|
{% fill name: str, *, data: Optional[str] = None, fallback: Optional[str] = None, body: Union[str, django.utils.safestring.SafeString, django_components.slots.SlotFunc[~TSlotData], django_components.slots.Slot[~TSlotData], NoneType] = None, default: Optional[str] = None %}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L648" target="_blank">See source code</a>
|
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L949" target="_blank">See source code</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Use this tag to insert content into component's slots.
|
Use this tag to insert content into component's slots.
|
||||||
|
|
||||||
`{% fill %}` tag may be used only within a `{% component %}..{% endcomponent %}` block.
|
`{% fill %}` tag may be used only within a `{% component %}..{% endcomponent %}` block,
|
||||||
Runtime checks should prohibit other usages.
|
and raises a `TemplateSyntaxError` if used outside of a component.
|
||||||
|
|
||||||
**Args:**
|
**Args:**
|
||||||
|
|
||||||
- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
|
- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
|
||||||
the default slot.
|
the default slot.
|
||||||
- `default` (str, optional): This argument allows you to access the original content of the slot
|
|
||||||
under the specified variable name. See
|
|
||||||
[Accessing original content of slots](../../concepts/fundamentals/slots#accessing-original-content-of-slots)
|
|
||||||
- `data` (str, optional): This argument allows you to access the data passed to the slot
|
- `data` (str, optional): This argument allows you to access the data passed to the slot
|
||||||
under the specified variable name. See [Scoped slots](../../concepts/fundamentals/slots#scoped-slots)
|
under the specified variable name. See [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||||
|
- `fallback` (str, optional): This argument allows you to access the original content of the slot
|
||||||
|
under the specified variable name. See [Slot fallback](../../concepts/fundamentals/slots#slot-fallback).
|
||||||
|
|
||||||
**Examples:**
|
**Example:**
|
||||||
|
|
||||||
Basic usage:
|
|
||||||
```django
|
```django
|
||||||
{% component "my_table" %}
|
{% component "my_table" %}
|
||||||
{% fill "pagination" %}
|
{% fill "pagination" %}
|
||||||
|
@ -199,7 +197,15 @@ Basic usage:
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing slot's default content with the `default` kwarg
|
### Access slot fallback
|
||||||
|
|
||||||
|
Use the `fallback` kwarg to access the original content of the slot.
|
||||||
|
|
||||||
|
The `fallback` kwarg defines the name of the variable that will contain the slot's fallback content.
|
||||||
|
|
||||||
|
Read more about [Slot fallback](../../concepts/fundamentals/slots#slot-fallback).
|
||||||
|
|
||||||
|
Component template:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{# my_table.html #}
|
{# my_table.html #}
|
||||||
|
@ -211,17 +217,27 @@ Basic usage:
|
||||||
</table>
|
</table>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Fill:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{% component "my_table" %}
|
{% component "my_table" %}
|
||||||
{% fill "pagination" default="default_pag" %}
|
{% fill "pagination" fallback="fallback" %}
|
||||||
<div class="my-class">
|
<div class="my-class">
|
||||||
{{ default_pag }}
|
{{ fallback }}
|
||||||
</div>
|
</div>
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing slot's data with the `data` kwarg
|
### Access slot data
|
||||||
|
|
||||||
|
Use the `data` kwarg to access the data passed to the slot.
|
||||||
|
|
||||||
|
The `data` kwarg defines the name of the variable that will contain the slot's data.
|
||||||
|
|
||||||
|
Read more about [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||||
|
|
||||||
|
Component template:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{# my_table.html #}
|
{# my_table.html #}
|
||||||
|
@ -233,6 +249,8 @@ Basic usage:
|
||||||
</table>
|
</table>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Fill:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{% component "my_table" %}
|
{% component "my_table" %}
|
||||||
{% fill "pagination" data="slot_data" %}
|
{% fill "pagination" data="slot_data" %}
|
||||||
|
@ -245,20 +263,60 @@ Basic usage:
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing slot data and default content on the default slot
|
### Using default slot
|
||||||
|
|
||||||
To access slot data and the default slot content on the default slot,
|
To access slot data and the fallback slot content on the default slot,
|
||||||
use `{% fill %}` with `name` set to `"default"`:
|
use `{% fill %}` with `name` set to `"default"`:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{% component "button" %}
|
{% component "button" %}
|
||||||
{% fill name="default" data="slot_data" default="default_slot" %}
|
{% fill name="default" data="slot_data" fallback="slot_fallback" %}
|
||||||
You clicked me {{ slot_data.count }} times!
|
You clicked me {{ slot_data.count }} times!
|
||||||
{{ default_slot }}
|
{{ slot_fallback }}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Slot fills from Python
|
||||||
|
|
||||||
|
You can pass a slot fill from Python to a component by setting the `body` kwarg
|
||||||
|
on the `{% fill %}` tag.
|
||||||
|
|
||||||
|
First pass a [`Slot`](../api#django_components.Slot) instance to the template
|
||||||
|
with the [`get_template_data()`](../api#django_components.Component.get_template_data)
|
||||||
|
method:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django_components import component, Slot
|
||||||
|
|
||||||
|
class Table(Component):
|
||||||
|
def get_template_data(self, args, kwargs, slots, context):
|
||||||
|
return {
|
||||||
|
"my_slot": Slot(lambda ctx: "Hello, world!"),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then pass the slot to the `{% fill %}` tag:
|
||||||
|
|
||||||
|
```django
|
||||||
|
{% component "table" %}
|
||||||
|
{% fill "pagination" body=my_slot / %}
|
||||||
|
{% endcomponent %}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
If you define both the `body` kwarg and the `{% fill %}` tag's body,
|
||||||
|
an error will be raised.
|
||||||
|
|
||||||
|
```django
|
||||||
|
{% component "table" %}
|
||||||
|
{% fill "pagination" body=my_slot %}
|
||||||
|
...
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
```
|
||||||
|
|
||||||
## html_attrs
|
## html_attrs
|
||||||
|
|
||||||
```django
|
```django
|
||||||
|
@ -274,8 +332,7 @@ use `{% fill %}` with `name` set to `"default"`:
|
||||||
Generate HTML attributes (`key="value"`), combining data from multiple sources,
|
Generate HTML attributes (`key="value"`), combining data from multiple sources,
|
||||||
whether its template variables or static text.
|
whether its template variables or static text.
|
||||||
|
|
||||||
It is designed to easily merge HTML attributes passed from outside with the internal.
|
It is designed to easily merge HTML attributes passed from outside as well as inside the component.
|
||||||
See how to in [Passing HTML attributes to components](../../guides/howto/passing_html_attrs/).
|
|
||||||
|
|
||||||
**Args:**
|
**Args:**
|
||||||
|
|
||||||
|
@ -318,8 +375,8 @@ renders
|
||||||
<div class="my-class extra-class" data-id="123">
|
<div class="my-class extra-class" data-id="123">
|
||||||
```
|
```
|
||||||
|
|
||||||
**See more usage examples in
|
See more usage examples in
|
||||||
[HTML attributes](../../concepts/fundamentals/html_attributes#examples-for-html_attrs).**
|
[HTML attributes](../../concepts/fundamentals/html_attributes#examples-for-html_attrs).
|
||||||
|
|
||||||
## provide
|
## provide
|
||||||
|
|
||||||
|
@ -352,7 +409,7 @@ or Vue's [`provide()`](https://vuejs.org/guide/components/provide-inject).
|
||||||
|
|
||||||
Provide the "user_data" in parent component:
|
Provide the "user_data" in parent component:
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = """
|
template = """
|
||||||
|
@ -372,7 +429,7 @@ class Parent(Component):
|
||||||
Since the "child" component is used within the `{% provide %} / {% endprovide %}` tags,
|
Since the "child" component is used within the `{% provide %} / {% endprovide %}` tags,
|
||||||
we can request the "user_data" using `Component.inject("user_data")`:
|
we can request the "user_data" using `Component.inject("user_data")`:
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = """
|
template = """
|
||||||
|
@ -410,7 +467,7 @@ user = self.inject("user_data")["user"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L189" target="_blank">See source code</a>
|
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L475" target="_blank">See source code</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -435,7 +492,7 @@ or [React's `children`](https://react.dev/learn/passing-props-to-a-component#pas
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = """
|
template = """
|
||||||
|
@ -450,7 +507,7 @@ class Child(Component):
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = """
|
template = """
|
||||||
|
@ -468,12 +525,14 @@ class Parent(Component):
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
### Passing data to slots
|
### Slot data
|
||||||
|
|
||||||
Any extra kwargs will be considered as slot data, and will be accessible in the [`{% fill %}`](#fill)
|
Any extra kwargs will be considered as slot data, and will be accessible in the [`{% fill %}`](#fill)
|
||||||
tag via fill's `data` kwarg:
|
tag via fill's `data` kwarg:
|
||||||
|
|
||||||
```python
|
Read more about [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||||
|
|
||||||
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = """
|
template = """
|
||||||
|
@ -486,7 +545,7 @@ class Child(Component):
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = """
|
template = """
|
||||||
|
@ -501,35 +560,35 @@ class Parent(Component):
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing default slot content
|
### Slot fallback
|
||||||
|
|
||||||
The content between the `{% slot %}..{% endslot %}` tags is the default content that
|
The content between the `{% slot %}..{% endslot %}` tags is the fallback content that
|
||||||
will be rendered if no fill is given for the slot.
|
will be rendered if no fill is given for the slot.
|
||||||
|
|
||||||
This default content can then be accessed from within the [`{% fill %}`](#fill) tag using
|
This fallback content can then be accessed from within the [`{% fill %}`](#fill) tag using
|
||||||
the fill's `default` kwarg.
|
the fill's `fallback` kwarg.
|
||||||
This is useful if you need to wrap / prepend / append the original slot's content.
|
This is useful if you need to wrap / prepend / append the original slot's content.
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = """
|
template = """
|
||||||
<div>
|
<div>
|
||||||
{% slot "content" %}
|
{% slot "content" %}
|
||||||
This is default content!
|
This is fallback content!
|
||||||
{% endslot %}
|
{% endslot %}
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = """
|
template = """
|
||||||
{# Parent can access the slot's default content #}
|
{# Parent can access the slot's fallback content #}
|
||||||
{% component "child" %}
|
{% component "child" %}
|
||||||
{% fill "content" default="default" %}
|
{% fill "content" fallback="fallback" %}
|
||||||
{{ default }}
|
{{ fallback }}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,5 +7,11 @@ template and in [`on_render_before` / `on_render_after`](../concepts/advanced/ho
|
||||||
hooks.
|
hooks.
|
||||||
|
|
||||||
|
|
||||||
|
::: django_components.component.ComponentVars.args
|
||||||
|
|
||||||
|
::: django_components.component.ComponentVars.kwargs
|
||||||
|
|
||||||
|
::: django_components.component.ComponentVars.slots
|
||||||
|
|
||||||
::: django_components.component.ComponentVars.is_filled
|
::: django_components.component.ComponentVars.is_filled
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ from django_components.extension import (
|
||||||
extensions,
|
extensions,
|
||||||
)
|
)
|
||||||
from django_components.extensions.cache import ComponentCache
|
from django_components.extensions.cache import ComponentCache
|
||||||
from django_components.extensions.defaults import ComponentDefaults, apply_defaults, defaults_by_component
|
from django_components.extensions.defaults import ComponentDefaults
|
||||||
from django_components.extensions.view import ComponentView, ViewFn
|
from django_components.extensions.view import ComponentView, ViewFn
|
||||||
from django_components.node import BaseNode
|
from django_components.node import BaseNode
|
||||||
from django_components.perfutil.component import ComponentRenderer, component_context_cache, component_post_render
|
from django_components.perfutil.component import ComponentRenderer, component_context_cache, component_post_render
|
||||||
|
@ -2798,22 +2798,11 @@ class Component(metaclass=ComponentMeta):
|
||||||
|
|
||||||
render_id = _gen_component_id()
|
render_id = _gen_component_id()
|
||||||
|
|
||||||
# Apply defaults to missing or `None` values in `kwargs`
|
|
||||||
defaults = defaults_by_component.get(comp_cls, None)
|
|
||||||
if defaults is not None:
|
|
||||||
apply_defaults(kwargs_dict, defaults)
|
|
||||||
|
|
||||||
# If user doesn't specify `Args`, `Kwargs`, `Slots` types, then we pass them in as plain
|
|
||||||
# dicts / lists.
|
|
||||||
args_inst = comp_cls.Args(*args_list) if comp_cls.Args is not None else args_list
|
|
||||||
kwargs_inst = comp_cls.Kwargs(**kwargs_dict) if comp_cls.Kwargs is not None else kwargs_dict
|
|
||||||
slots_inst = comp_cls.Slots(**slots_dict) if comp_cls.Slots is not None else slots_dict
|
|
||||||
|
|
||||||
component = comp_cls(
|
component = comp_cls(
|
||||||
id=render_id,
|
id=render_id,
|
||||||
args=args_inst,
|
args=args_list,
|
||||||
kwargs=kwargs_inst,
|
kwargs=kwargs_dict,
|
||||||
slots=slots_inst,
|
slots=slots_dict,
|
||||||
context=context,
|
context=context,
|
||||||
request=request,
|
request=request,
|
||||||
deps_strategy=deps_strategy,
|
deps_strategy=deps_strategy,
|
||||||
|
@ -2841,6 +2830,12 @@ class Component(metaclass=ComponentMeta):
|
||||||
if result_override is not None:
|
if result_override is not None:
|
||||||
return result_override
|
return result_override
|
||||||
|
|
||||||
|
# If user doesn't specify `Args`, `Kwargs`, `Slots` types, then we pass them in as plain
|
||||||
|
# dicts / lists.
|
||||||
|
component.args = comp_cls.Args(*args_list) if comp_cls.Args is not None else args_list
|
||||||
|
component.kwargs = comp_cls.Kwargs(**kwargs_dict) if comp_cls.Kwargs is not None else kwargs_dict
|
||||||
|
component.slots = comp_cls.Slots(**slots_dict) if comp_cls.Slots is not None else slots_dict
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
# 2. Prepare component state
|
# 2. Prepare component state
|
||||||
######################################
|
######################################
|
||||||
|
|
|
@ -92,7 +92,7 @@ class OnComponentInputContext(NamedTuple):
|
||||||
"""List of positional arguments passed to the component"""
|
"""List of positional arguments passed to the component"""
|
||||||
kwargs: Dict
|
kwargs: Dict
|
||||||
"""Dictionary of keyword arguments passed to the component"""
|
"""Dictionary of keyword arguments passed to the component"""
|
||||||
slots: Dict
|
slots: Dict[str, "Slot"]
|
||||||
"""Dictionary of slot definitions"""
|
"""Dictionary of slot definitions"""
|
||||||
context: Context
|
context: Context
|
||||||
"""The Django template Context object"""
|
"""The Django template Context object"""
|
||||||
|
@ -498,6 +498,19 @@ class ComponentExtension:
|
||||||
# Add extra kwarg to all components when they are rendered
|
# Add extra kwarg to all components when they are rendered
|
||||||
ctx.kwargs["my_input"] = "my_value"
|
ctx.kwargs["my_input"] = "my_value"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
In this hook, the components' inputs are still mutable.
|
||||||
|
|
||||||
|
As such, if a component defines [`Args`](../api#django_components.Component.Args),
|
||||||
|
[`Kwargs`](../api#django_components.Component.Kwargs),
|
||||||
|
[`Slots`](../api#django_components.Component.Slots) types, these types are NOT yet instantiated.
|
||||||
|
|
||||||
|
Instead, component fields like [`Component.args`](../api#django_components.Component.args),
|
||||||
|
[`Component.kwargs`](../api#django_components.Component.kwargs),
|
||||||
|
[`Component.slots`](../api#django_components.Component.slots)
|
||||||
|
are plain `list` / `dict` objects.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from dataclasses import MISSING, Field, dataclass
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, NamedTuple, Optional, Type
|
from typing import TYPE_CHECKING, Any, Callable, Dict, List, NamedTuple, Optional, Type
|
||||||
from weakref import WeakKeyDictionary
|
from weakref import WeakKeyDictionary
|
||||||
|
|
||||||
from django_components.extension import ComponentExtension, OnComponentClassCreatedContext
|
from django_components.extension import ComponentExtension, OnComponentClassCreatedContext, OnComponentInputContext
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from django_components.component import Component
|
from django_components.component import Component
|
||||||
|
@ -99,7 +99,7 @@ def _extract_defaults(defaults: Optional[Type]) -> List[ComponentDefaultField]:
|
||||||
return defaults_fields
|
return defaults_fields
|
||||||
|
|
||||||
|
|
||||||
def apply_defaults(kwargs: Dict, defaults: List[ComponentDefaultField]) -> None:
|
def _apply_defaults(kwargs: Dict, defaults: List[ComponentDefaultField]) -> None:
|
||||||
"""
|
"""
|
||||||
Apply the defaults from `Component.Defaults` to the given `kwargs`.
|
Apply the defaults from `Component.Defaults` to the given `kwargs`.
|
||||||
|
|
||||||
|
@ -171,3 +171,11 @@ class DefaultsExtension(ComponentExtension):
|
||||||
def on_component_class_created(self, ctx: OnComponentClassCreatedContext) -> None:
|
def on_component_class_created(self, ctx: OnComponentClassCreatedContext) -> None:
|
||||||
defaults_cls = getattr(ctx.component_cls, "Defaults", None)
|
defaults_cls = getattr(ctx.component_cls, "Defaults", None)
|
||||||
defaults_by_component[ctx.component_cls] = _extract_defaults(defaults_cls)
|
defaults_by_component[ctx.component_cls] = _extract_defaults(defaults_cls)
|
||||||
|
|
||||||
|
# Apply defaults to missing or `None` values in `kwargs`
|
||||||
|
def on_component_input(self, ctx: OnComponentInputContext) -> None:
|
||||||
|
defaults = defaults_by_component.get(ctx.component_cls, None)
|
||||||
|
if defaults is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
_apply_defaults(ctx.kwargs, defaults)
|
||||||
|
|
|
@ -30,7 +30,7 @@ class ProvideNode(BaseNode):
|
||||||
|
|
||||||
Provide the "user_data" in parent component:
|
Provide the "user_data" in parent component:
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -50,7 +50,7 @@ class ProvideNode(BaseNode):
|
||||||
Since the "child" component is used within the `{% provide %} / {% endprovide %}` tags,
|
Since the "child" component is used within the `{% provide %} / {% endprovide %}` tags,
|
||||||
we can request the "user_data" using `Component.inject("user_data")`:
|
we can request the "user_data" using `Component.inject("user_data")`:
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
|
|
@ -52,7 +52,23 @@ FILL_BODY_KWARG = "body"
|
||||||
|
|
||||||
# Public types
|
# Public types
|
||||||
SlotResult = Union[str, SafeString]
|
SlotResult = Union[str, SafeString]
|
||||||
"""Type representing the result of a slot render function."""
|
"""
|
||||||
|
Type representing the result of a slot render function.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django_components import SlotContext, SlotResult
|
||||||
|
|
||||||
|
def my_slot_fn(ctx: SlotContext) -> SlotResult:
|
||||||
|
return "Hello, world!"
|
||||||
|
|
||||||
|
my_slot = Slot(my_slot_fn)
|
||||||
|
html = my_slot() # Output: Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
Read more about [Slot functions](../../concepts/fundamentals/slots#slot-functions).
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
@ -65,9 +81,9 @@ class SlotContext(Generic[TSlotData]):
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from django_components import SlotContext
|
from django_components import SlotContext, SlotResult
|
||||||
|
|
||||||
def my_slot(ctx: SlotContext):
|
def my_slot(ctx: SlotContext) -> SlotResult:
|
||||||
return f"Hello, {ctx.data['name']}!"
|
return f"Hello, {ctx.data['name']}!"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -216,8 +232,6 @@ class Slot(Generic[TSlotData]):
|
||||||
the body (string) of that `{% fill %}` tag.
|
the body (string) of that `{% fill %}` tag.
|
||||||
- If Slot was created from string as `Slot("...")`, `Slot.contents` will contain that string.
|
- If Slot was created from string as `Slot("...")`, `Slot.contents` will contain that string.
|
||||||
- If Slot was created from a function, `Slot.contents` will contain that function.
|
- If Slot was created from a function, `Slot.contents` will contain that function.
|
||||||
- If Slot was created from another `Slot` as `Slot(slot)`, `Slot.contents` will contain the inner
|
|
||||||
slot's `Slot.contents`.
|
|
||||||
|
|
||||||
Read more about [Slot contents](../../concepts/fundamentals/slots#slot-contents).
|
Read more about [Slot contents](../../concepts/fundamentals/slots#slot-contents).
|
||||||
"""
|
"""
|
||||||
|
@ -232,28 +246,30 @@ class Slot(Generic[TSlotData]):
|
||||||
|
|
||||||
# Following fields are only for debugging
|
# Following fields are only for debugging
|
||||||
component_name: Optional[str] = None
|
component_name: Optional[str] = None
|
||||||
"""Name of the component that originally received this slot fill."""
|
"""
|
||||||
|
Name of the component that originally received this slot fill.
|
||||||
|
|
||||||
|
See [Slot metadata](../../concepts/fundamentals/slots#slot-metadata).
|
||||||
|
"""
|
||||||
slot_name: Optional[str] = None
|
slot_name: Optional[str] = None
|
||||||
"""Slot name to which this Slot was initially assigned."""
|
"""
|
||||||
|
Slot name to which this Slot was initially assigned.
|
||||||
|
|
||||||
|
See [Slot metadata](../../concepts/fundamentals/slots#slot-metadata).
|
||||||
|
"""
|
||||||
nodelist: Optional[NodeList] = None
|
nodelist: Optional[NodeList] = None
|
||||||
"""
|
"""
|
||||||
If the slot was defined with [`{% fill %}`](../template_tags#fill) tag,
|
If the slot was defined with [`{% fill %}`](../template_tags#fill) tag,
|
||||||
this will be the Nodelist of the fill's content.
|
this will be the Nodelist of the fill's content.
|
||||||
|
|
||||||
|
See [Slot metadata](../../concepts/fundamentals/slots#slot-metadata).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
# Since the `Slot` instance is treated as a function, it may be passed as `contents`
|
# Raise if Slot received another Slot instance as `contents`,
|
||||||
# to the `Slot()` constructor. In that case we need to unwrap to the original value
|
# because this leads to ambiguity about how to handle the metadata.
|
||||||
# if `Slot()` constructor got another Slot instance.
|
if isinstance(self.contents, Slot):
|
||||||
# NOTE: If `Slot` was passed as `contents`, we do NOT take the metadata from the inner Slot instance.
|
raise ValueError("Slot received another Slot instance as `contents`")
|
||||||
# Instead we treat is simply as a function.
|
|
||||||
# NOTE: Try to avoid infinite loop if `Slot.contents` points to itself.
|
|
||||||
seen_contents = set()
|
|
||||||
while isinstance(self.contents, Slot) and self.contents not in seen_contents:
|
|
||||||
seen_contents.add(id(self.contents))
|
|
||||||
self.contents = self.contents.contents
|
|
||||||
if id(self.contents) in seen_contents:
|
|
||||||
raise ValueError("Detected infinite loop in `Slot.contents` pointing to itself")
|
|
||||||
|
|
||||||
if self.content_func is None:
|
if self.content_func is None:
|
||||||
self.contents, new_nodelist, self.content_func = self._resolve_contents(self.contents)
|
self.contents, new_nodelist, self.content_func = self._resolve_contents(self.contents)
|
||||||
|
@ -261,7 +277,7 @@ class Slot(Generic[TSlotData]):
|
||||||
self.nodelist = new_nodelist
|
self.nodelist = new_nodelist
|
||||||
|
|
||||||
if not callable(self.content_func):
|
if not callable(self.content_func):
|
||||||
raise ValueError(f"Slot content must be a callable, got: {self.content_func}")
|
raise ValueError(f"Slot 'content_func' must be a callable, got: {self.content_func}")
|
||||||
|
|
||||||
# Allow to treat the instances as functions
|
# Allow to treat the instances as functions
|
||||||
def __call__(
|
def __call__(
|
||||||
|
@ -374,20 +390,30 @@ SlotName = str
|
||||||
|
|
||||||
class SlotFallback:
|
class SlotFallback:
|
||||||
"""
|
"""
|
||||||
SlotFallback allows to treat a slot fallback as a variable. The slot is rendered only once
|
The content between the `{% slot %}..{% endslot %}` tags is the *fallback* content that
|
||||||
the instance is coerced to string.
|
will be rendered if no fill is given for the slot.
|
||||||
|
|
||||||
This is used to access slots as variables inside the templates. When a `SlotFallback`
|
```django
|
||||||
is rendered in the template with `{{ my_lazy_slot }}`, it will output the contents
|
{% slot "name" %}
|
||||||
of the slot.
|
Hello, my name is {{ name }} <!-- Fallback content -->
|
||||||
|
{% endslot %}
|
||||||
|
```
|
||||||
|
|
||||||
Usage in slot functions:
|
Because the fallback is defined as a piece of the template
|
||||||
|
([`NodeList`](https://github.com/django/django/blob/ddb85294159185c5bd5cae34c9ef735ff8409bfe/django/template/base.py#L1017)),
|
||||||
|
we want to lazily render it only when needed.
|
||||||
|
|
||||||
|
`SlotFallback` type allows to pass around the slot fallback as a variable.
|
||||||
|
|
||||||
|
To force the fallback to render, coerce it to string to trigger the `__str__()` method.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def slot_function(self, ctx: SlotContext):
|
def slot_function(self, ctx: SlotContext):
|
||||||
return f"Hello, {ctx.fallback}!"
|
return f"Hello, {ctx.fallback}!"
|
||||||
```
|
```
|
||||||
"""
|
""" # noqa: E501
|
||||||
|
|
||||||
def __init__(self, slot: "SlotNode", context: Context):
|
def __init__(self, slot: "SlotNode", context: Context):
|
||||||
self._slot = slot
|
self._slot = slot
|
||||||
|
@ -400,6 +426,9 @@ class SlotFallback:
|
||||||
|
|
||||||
# TODO_v1 - REMOVE - superseded by SlotFallback
|
# TODO_v1 - REMOVE - superseded by SlotFallback
|
||||||
SlotRef = SlotFallback
|
SlotRef = SlotFallback
|
||||||
|
"""
|
||||||
|
DEPRECATED: Use [`SlotFallback`](../api#django_components.SlotFallback) instead. Will be removed in v1.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
name_escape_re = re.compile(r"[^\w]")
|
name_escape_re = re.compile(r"[^\w]")
|
||||||
|
@ -457,7 +486,7 @@ class SlotNode(BaseNode):
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -472,7 +501,7 @@ class SlotNode(BaseNode):
|
||||||
\"\"\"
|
\"\"\"
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -490,12 +519,14 @@ class SlotNode(BaseNode):
|
||||||
\"\"\"
|
\"\"\"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Passing data to slots
|
### Slot data
|
||||||
|
|
||||||
Any extra kwargs will be considered as slot data, and will be accessible in the [`{% fill %}`](#fill)
|
Any extra kwargs will be considered as slot data, and will be accessible in the [`{% fill %}`](#fill)
|
||||||
tag via fill's `data` kwarg:
|
tag via fill's `data` kwarg:
|
||||||
|
|
||||||
```python
|
Read more about [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||||
|
|
||||||
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -508,7 +539,7 @@ class SlotNode(BaseNode):
|
||||||
\"\"\"
|
\"\"\"
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -523,7 +554,7 @@ class SlotNode(BaseNode):
|
||||||
\"\"\"
|
\"\"\"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing fallback slot content
|
### Slot fallback
|
||||||
|
|
||||||
The content between the `{% slot %}..{% endslot %}` tags is the fallback content that
|
The content between the `{% slot %}..{% endslot %}` tags is the fallback content that
|
||||||
will be rendered if no fill is given for the slot.
|
will be rendered if no fill is given for the slot.
|
||||||
|
@ -532,7 +563,7 @@ class SlotNode(BaseNode):
|
||||||
the fill's `fallback` kwarg.
|
the fill's `fallback` kwarg.
|
||||||
This is useful if you need to wrap / prepend / append the original slot's content.
|
This is useful if you need to wrap / prepend / append the original slot's content.
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("child")
|
@register("child")
|
||||||
class Child(Component):
|
class Child(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -544,7 +575,7 @@ class SlotNode(BaseNode):
|
||||||
\"\"\"
|
\"\"\"
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```djc_py
|
||||||
@register("parent")
|
@register("parent")
|
||||||
class Parent(Component):
|
class Parent(Component):
|
||||||
template = \"\"\"
|
template = \"\"\"
|
||||||
|
@ -910,21 +941,20 @@ class FillNode(BaseNode):
|
||||||
"""
|
"""
|
||||||
Use this tag to insert content into component's slots.
|
Use this tag to insert content into component's slots.
|
||||||
|
|
||||||
`{% fill %}` tag may be used only within a `{% component %}..{% endcomponent %}` block.
|
`{% fill %}` tag may be used only within a `{% component %}..{% endcomponent %}` block,
|
||||||
Runtime checks should prohibit other usages.
|
and raises a `TemplateSyntaxError` if used outside of a component.
|
||||||
|
|
||||||
**Args:**
|
**Args:**
|
||||||
|
|
||||||
- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
|
- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
|
||||||
the default slot.
|
the default slot.
|
||||||
- `fallback` (str, optional): This argument allows you to access the original content of the slot
|
|
||||||
under the specified variable name. See [Slot fallback](../../concepts/fundamentals/slots#slot-fallback).
|
|
||||||
- `data` (str, optional): This argument allows you to access the data passed to the slot
|
- `data` (str, optional): This argument allows you to access the data passed to the slot
|
||||||
under the specified variable name. See [Slot data](../../concepts/fundamentals/slots#slot-data).
|
under the specified variable name. See [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||||
|
- `fallback` (str, optional): This argument allows you to access the original content of the slot
|
||||||
|
under the specified variable name. See [Slot fallback](../../concepts/fundamentals/slots#slot-fallback).
|
||||||
|
|
||||||
**Examples:**
|
**Example:**
|
||||||
|
|
||||||
Basic usage:
|
|
||||||
```django
|
```django
|
||||||
{% component "my_table" %}
|
{% component "my_table" %}
|
||||||
{% fill "pagination" %}
|
{% fill "pagination" %}
|
||||||
|
@ -933,7 +963,15 @@ class FillNode(BaseNode):
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing slot's fallback content with the `fallback` kwarg
|
### Access slot fallback
|
||||||
|
|
||||||
|
Use the `fallback` kwarg to access the original content of the slot.
|
||||||
|
|
||||||
|
The `fallback` kwarg defines the name of the variable that will contain the slot's fallback content.
|
||||||
|
|
||||||
|
Read more about [Slot fallback](../../concepts/fundamentals/slots#slot-fallback).
|
||||||
|
|
||||||
|
Component template:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{# my_table.html #}
|
{# my_table.html #}
|
||||||
|
@ -945,6 +983,8 @@ class FillNode(BaseNode):
|
||||||
</table>
|
</table>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Fill:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{% component "my_table" %}
|
{% component "my_table" %}
|
||||||
{% fill "pagination" fallback="fallback" %}
|
{% fill "pagination" fallback="fallback" %}
|
||||||
|
@ -955,7 +995,15 @@ class FillNode(BaseNode):
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing slot's data with the `data` kwarg
|
### Access slot data
|
||||||
|
|
||||||
|
Use the `data` kwarg to access the data passed to the slot.
|
||||||
|
|
||||||
|
The `data` kwarg defines the name of the variable that will contain the slot's data.
|
||||||
|
|
||||||
|
Read more about [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||||
|
|
||||||
|
Component template:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{# my_table.html #}
|
{# my_table.html #}
|
||||||
|
@ -967,6 +1015,8 @@ class FillNode(BaseNode):
|
||||||
</table>
|
</table>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Fill:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
{% component "my_table" %}
|
{% component "my_table" %}
|
||||||
{% fill "pagination" data="slot_data" %}
|
{% fill "pagination" data="slot_data" %}
|
||||||
|
@ -979,7 +1029,7 @@ class FillNode(BaseNode):
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing slot data and fallback content on the default slot
|
### Using default slot
|
||||||
|
|
||||||
To access slot data and the fallback slot content on the default slot,
|
To access slot data and the fallback slot content on the default slot,
|
||||||
use `{% fill %}` with `name` set to `"default"`:
|
use `{% fill %}` with `name` set to `"default"`:
|
||||||
|
@ -993,7 +1043,7 @@ class FillNode(BaseNode):
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Passing slot fill from Python
|
### Slot fills from Python
|
||||||
|
|
||||||
You can pass a slot fill from Python to a component by setting the `body` kwarg
|
You can pass a slot fill from Python to a component by setting the `body` kwarg
|
||||||
on the `{% fill %}` tag.
|
on the `{% fill %}` tag.
|
||||||
|
@ -1189,6 +1239,7 @@ class FillNode(BaseNode):
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# EXTRACTING {% fill %} FROM TEMPLATES
|
# EXTRACTING {% fill %} FROM TEMPLATES
|
||||||
|
# (internal)
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,15 @@ class TestSlot:
|
||||||
slots={"first": "SLOT_FN"},
|
slots={"first": "SLOT_FN"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_render_raises_on_slot_instance_in_slot_constructor(self):
|
||||||
|
slot: Slot = Slot(lambda ctx: "SLOT_FN")
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError,
|
||||||
|
match=re.escape("Slot received another Slot instance as `contents`"),
|
||||||
|
):
|
||||||
|
Slot(slot)
|
||||||
|
|
||||||
def test_render_slot_in_python__minimal(self):
|
def test_render_slot_in_python__minimal(self):
|
||||||
def slot_fn(ctx: SlotContext):
|
def slot_fn(ctx: SlotContext):
|
||||||
assert ctx.context is None
|
assert ctx.context is None
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue