mirror of
https://github.com/django-components/django-components.git
synced 2025-08-31 03:07:19 +00:00
refactor: Add Node metadata (#1229)
* refactor: `Slot.source` replaced with `Slot.fill_node`, new `Component.node` property, and `slot_node` available in `on_slot_rendered()` hook. * refactor: fix windows path error in tests
This commit is contained in:
parent
abc6be343e
commit
46e524e37d
17 changed files with 728 additions and 103 deletions
|
@ -153,12 +153,14 @@ GreetNode.register(library)
|
|||
|
||||
When using [`BaseNode`](../../../reference/api#django_components.BaseNode), you have access to several useful properties:
|
||||
|
||||
- `node_id`: A unique identifier for this node instance
|
||||
- `flags`: Dictionary of flag values (e.g. `{"required": True}`)
|
||||
- `params`: List of raw parameters passed to the tag
|
||||
- `nodelist`: The template nodes between the start and end tags
|
||||
- `contents`: The raw contents between the start and end tags
|
||||
- `active_flags`: List of flags that are currently set to True
|
||||
- [`node_id`](../../../reference/api#django_components.BaseNode.node_id): A unique identifier for this node instance
|
||||
- [`flags`](../../../reference/api#django_components.BaseNode.flags): Dictionary of flag values (e.g. `{"required": True}`)
|
||||
- [`params`](../../../reference/api#django_components.BaseNode.params): List of raw parameters passed to the tag
|
||||
- [`nodelist`](../../../reference/api#django_components.BaseNode.nodelist): The template nodes between the start and end tags
|
||||
- [`contents`](../../../reference/api#django_components.BaseNode.contents): The raw contents between the start and end tags
|
||||
- [`active_flags`](../../../reference/api#django_components.BaseNode.active_flags): List of flags that are currently set to True
|
||||
- [`template_name`](../../../reference/api#django_components.BaseNode.template_name): The name of the `Template` instance inside which the node was defined
|
||||
- [`template_component`](../../../reference/api#django_components.BaseNode.template_component): The component class that the `Template` belongs to
|
||||
|
||||
This is what the `node` parameter in the [`@template_tag`](../../../reference/api#django_components.template_tag) decorator gives you access to - it's the instance of the node class that was automatically created for your template tag.
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ The Render API includes:
|
|||
- [`self.inject()`](../render_api/#provide-inject) - Inject data into the component
|
||||
|
||||
- Template tag metadata:
|
||||
- [`self.node`](../render_api/#template-tag-metadata) - The [`ComponentNode`](../../../reference/api/#django_components.ComponentNode) instance
|
||||
- [`self.registry`](../render_api/#template-tag-metadata) - The [`ComponentRegistry`](../../../reference/api/#django_components.ComponentRegistry) instance
|
||||
- [`self.registered_name`](../render_api/#template-tag-metadata) - The name under which the component was registered
|
||||
- [`self.outer_context`](../render_api/#template-tag-metadata) - The context outside of the [`{% component %}`](../../../reference/template_tags#component) tag
|
||||
|
@ -337,17 +338,18 @@ class Table(Component):
|
|||
If the component is rendered with [`{% component %}`](../../../reference/template_tags#component) template tag,
|
||||
the following metadata is available:
|
||||
|
||||
- [`self.node`](../../../reference/api/#django_components.Component.node) - The [`ComponentNode`](../../../reference/api/#django_components.ComponentNode) instance
|
||||
- [`self.registry`](../../../reference/api/#django_components.Component.registry) - The [`ComponentRegistry`](../../../reference/api/#django_components.ComponentRegistry) instance
|
||||
that was used to render the component
|
||||
- [`self.registered_name`](../../../reference/api/#django_components.Component.registered_name) - The name under which the component was registered
|
||||
- [`self.outer_context`](../../../reference/api/#django_components.Component.outer_context) - The context outside of the [`{% component %}`](../../../reference/template_tags#component) tag
|
||||
|
||||
```django
|
||||
{% with abc=123 %}
|
||||
{{ abc }} {# <--- This is in outer context #}
|
||||
{% component "my_component" / %}
|
||||
{% endwith %}
|
||||
```
|
||||
```django
|
||||
{% with abc=123 %}
|
||||
{{ abc }} {# <--- This is in outer context #}
|
||||
{% component "my_component" / %}
|
||||
{% endwith %}
|
||||
```
|
||||
|
||||
You can use these to check whether the component was rendered inside a template with [`{% component %}`](../../../reference/template_tags#component) tag
|
||||
or in Python with [`Component.render()`](../../../reference/api/#django_components.Component.render).
|
||||
|
@ -360,3 +362,40 @@ class MyComponent(Component):
|
|||
else:
|
||||
# Do something for the {% component %} template tag
|
||||
```
|
||||
|
||||
You can access the [`ComponentNode`](../../../reference/api/#django_components.ComponentNode) under [`Component.node`](../../../reference/api/#django_components.Component.node):
|
||||
|
||||
```py
|
||||
class MyComponent(Component):
|
||||
def get_template_data(self, context, template):
|
||||
if self.node is not None:
|
||||
assert self.node.name == "my_component"
|
||||
```
|
||||
|
||||
Accessing the [`ComponentNode`](../../../reference/api/#django_components.ComponentNode) is mostly useful for extensions, which can modify their behaviour based on the source of the Component.
|
||||
|
||||
For example, if `MyComponent` was used in another component - that is,
|
||||
with a `{% component "my_component" %}` tag
|
||||
in a template that belongs to another component - then you can use
|
||||
[`self.node.template_component`](../../../reference/api/#django_components.ComponentNode.template_component)
|
||||
to access the owner [`Component`](../../../reference/api/#django_components.Component) class.
|
||||
|
||||
```djc_py
|
||||
class Parent(Component):
|
||||
template: types.django_html = """
|
||||
<div>
|
||||
{% component "my_component" / %}
|
||||
</div>
|
||||
"""
|
||||
|
||||
@register("my_component")
|
||||
class MyComponent(Component):
|
||||
def get_template_data(self, context, template):
|
||||
if self.node is not None:
|
||||
assert self.node.template_component == Parent
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
`Component.node` is `None` if the component is created by [`Component.render()`](../../../reference/api/#django_components.Component.render)
|
||||
(but you can pass in the `node` kwarg yourself).
|
||||
|
|
|
@ -728,7 +728,7 @@ with extra metadata:
|
|||
- [`component_name`](../../../reference/api#django_components.Slot.component_name)
|
||||
- [`slot_name`](../../../reference/api#django_components.Slot.slot_name)
|
||||
- [`nodelist`](../../../reference/api#django_components.Slot.nodelist)
|
||||
- [`source`](../../../reference/api#django_components.Slot.source)
|
||||
- [`fill_node`](../../../reference/api#django_components.Slot.fill_node)
|
||||
- [`extra`](../../../reference/api#django_components.Slot.extra)
|
||||
|
||||
These are populated the first time a slot is passed to a component.
|
||||
|
@ -738,10 +738,33 @@ still point to the first component that received the slot.
|
|||
|
||||
You can use these for debugging, such as printing out the slot's component name and slot name.
|
||||
|
||||
Extensions can use [`Slot.source`](../../../reference/api#django_components.Slot.source)
|
||||
**Fill node**
|
||||
|
||||
Components or extensions can use [`Slot.fill_node`](../../../reference/api#django_components.Slot.fill_node)
|
||||
to handle slots differently based on whether the slot
|
||||
was defined in the template with [`{% fill %}`](../../../reference/template_tags#fill) tag
|
||||
or in the component's Python code. See an example in [Pass slot metadata](../../advanced/extensions#pass-slot-metadata).
|
||||
was defined in the template with [`{% fill %}`](../../../reference/template_tags#fill) and
|
||||
[`{% component %}`](../../../reference/template_tags#component) tags,
|
||||
or in the component's Python code.
|
||||
|
||||
If the slot was created from a [`{% fill %}`](../../../reference/template_tags#fill) tag,
|
||||
this will be the [`FillNode`](../../../reference/api#django_components.FillNode) instance.
|
||||
|
||||
If the slot was a default slot created from a [`{% component %}`](../../../reference/template_tags#component) tag,
|
||||
this will be the [`ComponentNode`](../../../reference/api#django_components.ComponentNode) instance.
|
||||
|
||||
You can use this to find the [`Component`](../../../reference/api#django_components.Component) in whose
|
||||
template the [`{% fill %}`](../../../reference/template_tags#fill) tag was defined:
|
||||
|
||||
```python
|
||||
class MyTable(Component):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
footer_slot = slots.get("footer")
|
||||
if footer_slot is not None and footer_slot.fill_node is not None:
|
||||
owner_component = footer_slot.fill_node.template_component
|
||||
# ...
|
||||
```
|
||||
|
||||
**Extra**
|
||||
|
||||
You can also pass any additional data along with the slot by setting it in [`Slot.extra`](../../../reference/api#django_components.Slot.extra):
|
||||
|
||||
|
@ -761,7 +784,6 @@ slot = Slot(
|
|||
# Optional
|
||||
component_name="table",
|
||||
slot_name="name",
|
||||
source="python",
|
||||
extra={},
|
||||
)
|
||||
|
||||
|
@ -771,6 +793,8 @@ slot.slot_name = "name"
|
|||
slot.extra["foo"] = "bar"
|
||||
```
|
||||
|
||||
Read more in [Pass slot metadata](../../advanced/extensions#pass-slot-metadata).
|
||||
|
||||
### Slot contents
|
||||
|
||||
Whether you create a slot from a function, a string, or from the [`{% fill %}`](../../../reference/template_tags#fill) tags,
|
||||
|
|
|
@ -47,6 +47,10 @@
|
|||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.ComponentNode
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.ComponentRegistry
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
@ -83,6 +87,14 @@
|
|||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.FillNode
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.ProvideNode
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.RegistrySettings
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
@ -111,6 +123,10 @@
|
|||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.SlotNode
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.SlotRef
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
|
|
@ -204,6 +204,7 @@ name | type | description
|
|||
`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
|
||||
`slot_node` | `SlotNode` | The node instance of the `{% slot %}` tag
|
||||
|
||||
## Objects
|
||||
|
||||
|
|
|
@ -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#L3301" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L3350" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
|
@ -75,7 +75,7 @@ Renders one of the components that was previously registered with
|
|||
[`@register()`](./api.md#django_components.register)
|
||||
decorator.
|
||||
|
||||
The `{% component %}` tag takes:
|
||||
The [`{% component %}`](../template_tags#component) tag takes:
|
||||
|
||||
- Component's registered name as the first positional argument,
|
||||
- Followed by any number of positional and keyword arguments.
|
||||
|
@ -92,7 +92,8 @@ The component name must be a string literal.
|
|||
### Inserting slot fills
|
||||
|
||||
If the component defined any [slots](../concepts/fundamentals/slots.md), you can
|
||||
"fill" these slots by placing the [`{% fill %}`](#fill) tags within the `{% component %}` tag:
|
||||
"fill" these slots by placing the [`{% fill %}`](../template_tags#fill) tags
|
||||
within the [`{% component %}`](../template_tags#component) tag:
|
||||
|
||||
```django
|
||||
{% component "my_table" rows=rows headers=headers %}
|
||||
|
@ -102,7 +103,7 @@ If the component defined any [slots](../concepts/fundamentals/slots.md), you can
|
|||
{% endcomponent %}
|
||||
```
|
||||
|
||||
You can even nest [`{% fill %}`](#fill) tags within
|
||||
You can even nest [`{% fill %}`](../template_tags#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:
|
||||
|
@ -141,7 +142,7 @@ COMPONENTS = {
|
|||
}
|
||||
```
|
||||
|
||||
### Omitting the `component` keyword
|
||||
### Omitting the component keyword
|
||||
|
||||
If you would like to omit the `component` keyword, and simply refer to your
|
||||
components by their registered names:
|
||||
|
@ -169,19 +170,20 @@ COMPONENTS = {
|
|||
|
||||
|
||||
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L948" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L988" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
Use this tag to insert content into component's slots.
|
||||
Use [`{% fill %}`](../template_tags#fill) tag to insert content into component's
|
||||
[slots](../../concepts/fundamentals/slots).
|
||||
|
||||
`{% fill %}` tag may be used only within a `{% component %}..{% endcomponent %}` block,
|
||||
[`{% fill %}`](../template_tags#fill) tag may be used only within a `{% component %}..{% endcomponent %}` block,
|
||||
and raises a `TemplateSyntaxError` if used outside of a component.
|
||||
|
||||
**Args:**
|
||||
|
||||
- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
|
||||
the default slot.
|
||||
the [default slot](../../concepts/fundamentals/slots#default-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).
|
||||
- `fallback` (str, optional): This argument allows you to access the original content of the slot
|
||||
|
@ -266,7 +268,7 @@ Fill:
|
|||
### Using default slot
|
||||
|
||||
To access slot data and the fallback slot content on the default slot,
|
||||
use `{% fill %}` with `name` set to `"default"`:
|
||||
use [`{% fill %}`](../template_tags#fill) with `name` set to `"default"`:
|
||||
|
||||
```django
|
||||
{% component "button" %}
|
||||
|
@ -280,7 +282,7 @@ use `{% fill %}` with `name` set to `"default"`:
|
|||
### Slot fills from Python
|
||||
|
||||
You can pass a slot fill from Python to a component by setting the `body` kwarg
|
||||
on the `{% fill %}` tag.
|
||||
on the [`{% fill %}`](../template_tags#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)
|
||||
|
@ -296,7 +298,7 @@ class Table(Component):
|
|||
}
|
||||
```
|
||||
|
||||
Then pass the slot to the `{% fill %}` tag:
|
||||
Then pass the slot to the [`{% fill %}`](../template_tags#fill) tag:
|
||||
|
||||
```django
|
||||
{% component "table" %}
|
||||
|
@ -306,7 +308,7 @@ Then pass the slot to the `{% fill %}` tag:
|
|||
|
||||
!!! warning
|
||||
|
||||
If you define both the `body` kwarg and the `{% fill %}` tag's body,
|
||||
If you define both the `body` kwarg and the [`{% fill %}`](../template_tags#fill) tag's body,
|
||||
an error will be raised.
|
||||
|
||||
```django
|
||||
|
@ -391,8 +393,11 @@ See more usage examples in
|
|||
|
||||
|
||||
|
||||
The "provider" part of the [provide / inject feature](../../concepts/advanced/provide_inject).
|
||||
The [`{% provide %}`](../template_tags#provide) tag is part of the "provider" part of
|
||||
the [provide / inject feature](../../concepts/advanced/provide_inject).
|
||||
|
||||
Pass kwargs to this tag to define the provider's data.
|
||||
|
||||
Any components defined within the `{% provide %}..{% endprovide %}` tags will be able to access this data
|
||||
with [`Component.inject()`](../api#django_components.Component.inject).
|
||||
|
||||
|
@ -445,7 +450,7 @@ class Child(Component):
|
|||
}
|
||||
```
|
||||
|
||||
Notice that the keys defined on the `{% provide %}` tag are then accessed as attributes
|
||||
Notice that the keys defined on the [`{% provide %}`](../template_tags#provide) tag are then accessed as attributes
|
||||
when accessing them with [`Component.inject()`](../api#django_components.Component.inject).
|
||||
|
||||
✅ Do this
|
||||
|
@ -467,11 +472,11 @@ user = self.inject("user_data")["user"]
|
|||
|
||||
|
||||
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L474" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L513" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
Slot tag marks a place inside a component where content can be inserted
|
||||
[`{% slot %}`](../template_tags#slot) tag marks a place inside a component where content can be inserted
|
||||
from outside.
|
||||
|
||||
[Learn more](../../concepts/fundamentals/slots) about using slots.
|
||||
|
@ -485,7 +490,7 @@ or [React's `children`](https://react.dev/learn/passing-props-to-a-component#pas
|
|||
|
||||
- `name` (str, required): Registered name of the component to render
|
||||
- `default`: Optional flag. If there is a default slot, you can pass the component slot content
|
||||
without using the [`{% fill %}`](#fill) tag. See
|
||||
without using the [`{% fill %}`](../template_tags#fill) tag. See
|
||||
[Default slot](../../concepts/fundamentals/slots#default-slot)
|
||||
- `required`: Optional flag. Will raise an error if a slot is required but not given.
|
||||
- `**kwargs`: Any extra kwargs will be passed as the slot data.
|
||||
|
@ -527,8 +532,8 @@ class Parent(Component):
|
|||
|
||||
### Slot data
|
||||
|
||||
Any extra kwargs will be considered as slot data, and will be accessible in the [`{% fill %}`](#fill)
|
||||
tag via fill's `data` kwarg:
|
||||
Any extra kwargs will be considered as slot data, and will be accessible
|
||||
in the [`{% fill %}`](../template_tags#fill) tag via fill's `data` kwarg:
|
||||
|
||||
Read more about [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||
|
||||
|
@ -565,8 +570,8 @@ class Parent(Component):
|
|||
The content between the `{% slot %}..{% endslot %}` tags is the fallback content that
|
||||
will be rendered if no fill is given for the slot.
|
||||
|
||||
This fallback content can then be accessed from within the [`{% fill %}`](#fill) tag using
|
||||
the fill's `fallback` kwarg.
|
||||
This fallback content can then be accessed from within the [`{% fill %}`](../template_tags#fill) tag
|
||||
using the fill's `fallback` kwarg.
|
||||
This is useful if you need to wrap / prepend / append the original slot's content.
|
||||
|
||||
```djc_py
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue