mirror of
https://github.com/django-components/django-components.git
synced 2025-08-04 14:28:18 +00:00
refactor: cleanup docs, add docs on Render API, allow get_context_data return None (#1110)
* refactor: cleanup docs, add docs on Render API, allow get_context_data return None * refactor: fix linter and tests
This commit is contained in:
parent
9ede779fa3
commit
613dfea379
15 changed files with 604 additions and 226 deletions
|
@ -6,6 +6,10 @@ Django-components functionality can be extended with "extensions". Extensions al
|
|||
- Add new attributes and methods to the components under an extension-specific nested class.
|
||||
- Define custom commands that can be executed via the Django management command interface.
|
||||
|
||||
## Live examples
|
||||
|
||||
- [djc-ext-pydantic](https://github.com/django-components/djc-ext-pydantic)
|
||||
|
||||
## Setting up extensions
|
||||
|
||||
Extensions are configured in the Django settings under [`COMPONENTS.extensions`](../../../reference/settings#django_components.app_settings.ComponentsSettings.extensions).
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
_New in version 0.80_:
|
||||
|
||||
Django components supports the provide / inject or ContextProvider pattern with the combination of:
|
||||
`django-components` supports the provide / inject pattern, similarly to React's [Context Providers](https://react.dev/learn/passing-data-deeply-with-context) or Vue's [provide / inject](https://vuejs.org/guide/components/provide-inject).
|
||||
|
||||
1. `{% provide %}` tag
|
||||
1. `inject()` method of the `Component` class
|
||||
This is achieved with the combination of:
|
||||
|
||||
## What is "prop drilling"?
|
||||
- [`{% provide %}`](../../../reference/template_tags/#provide) tag
|
||||
- [`Component.inject()`](../../../reference/api/#django_components.Component.inject) method
|
||||
|
||||
## What is "prop drilling"
|
||||
|
||||
Prop drilling refers to a scenario in UI development where you need to pass data through many layers of a component tree to reach the nested components that actually need the data.
|
||||
|
||||
|
@ -19,8 +21,6 @@ With provide / inject, a parent component acts like a data hub for all its desce
|
|||
|
||||
This feature is inspired by Vue's [Provide / Inject](https://vuejs.org/guide/components/provide-inject) and React's [Context / useContext](https://react.dev/learn/passing-data-deeply-with-context).
|
||||
|
||||
## How to use provide / inject
|
||||
|
||||
As the name suggest, using provide / inject consists of 2 steps
|
||||
|
||||
1. Providing data
|
||||
|
@ -28,77 +28,86 @@ As the name suggest, using provide / inject consists of 2 steps
|
|||
|
||||
For examples of advanced uses of provide / inject, [see this discussion](https://github.com/django-components/django-components/pull/506#issuecomment-2132102584).
|
||||
|
||||
## Using `{% provide %}` tag
|
||||
## Providing data
|
||||
|
||||
First we use the `{% provide %}` tag to define the data we want to "provide" (make available).
|
||||
First we use the [`{% provide %}`](../../../reference/template_tags/#provide) tag to define the data we want to "provide" (make available).
|
||||
|
||||
```django
|
||||
{% provide "my_data" key="hi" another=123 %}
|
||||
{% provide "my_data" hello="hi" another=123 %}
|
||||
{% component "child" / %} <--- Can access "my_data"
|
||||
{% endprovide %}
|
||||
|
||||
{% component "child" / %} <--- Cannot access "my_data"
|
||||
```
|
||||
|
||||
Notice that the `provide` tag REQUIRES a name as a first argument. This is the _key_ by which we can then access the data passed to this tag.
|
||||
The first argument to the [`{% provide %}`](../../../reference/template_tags/#provide) tag is the _key_ by which we can later access the data passed to this tag. The key in this case is `"my_data"`.
|
||||
|
||||
`provide` tag name must resolve to a valid identifier (AKA a valid Python variable name).
|
||||
The key must resolve to a valid identifier (AKA a valid Python variable name).
|
||||
|
||||
Once you've set the name, you define the data you want to "provide" by passing it as keyword arguments. This is similar to how you pass data to the `{% with %}` tag.
|
||||
Next you define the data you want to "provide" by passing them as keyword arguments. This is similar to how you pass data to the [`{% with %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#with) tag or the [`{% slot %}`](../../../reference/template_tags/#slot) tag.
|
||||
|
||||
> NOTE: Kwargs passed to `{% provide %}` are NOT added to the context.
|
||||
> In the example below, the `{{ key }}` won't render anything:
|
||||
>
|
||||
> ```django
|
||||
> {% provide "my_data" key="hi" another=123 %}
|
||||
> {{ key }}
|
||||
> {% endprovide %}
|
||||
> ```
|
||||
!!! note
|
||||
|
||||
Similarly to [slots and fills](#dynamic-slots-and-fills), also provide's name argument can be set dynamically via a variable, a template expression, or a spread operator:
|
||||
Kwargs passed to `{% provide %}` are NOT added to the context.
|
||||
In the example below, the `{{ hello }}` won't render anything:
|
||||
|
||||
```django
|
||||
{% provide "my_data" hello="hi" another=123 %}
|
||||
{{ hello }}
|
||||
{% endprovide %}
|
||||
```
|
||||
|
||||
Similarly to [slots and fills](../../fundamentals/slots/#dynamic-slots-and-fills), also provide's name argument can be set dynamically via a variable, a template expression, or a spread operator:
|
||||
|
||||
```django
|
||||
{% provide name=name ... %}
|
||||
...
|
||||
{% provide %}
|
||||
</table>
|
||||
{% with my_name="my_name" %}
|
||||
{% provide name=my_name ... %}
|
||||
...
|
||||
{% endprovide %}
|
||||
{% endwith %}
|
||||
```
|
||||
|
||||
## Using `inject()` method
|
||||
## Injecting data
|
||||
|
||||
To "inject" (access) the data defined on the `provide` tag, you can use the `inject()` method inside of `get_context_data()`.
|
||||
To "inject" (access) the data defined on the [`{% provide %}`](../../../reference/template_tags/#provide) tag,
|
||||
you can use the [`Component.inject()`](../../../reference/api/#django_components.Component.inject) method from within any other component methods.
|
||||
|
||||
For a component to be able to "inject" some data, the component (`{% component %}` tag) must be nested inside the `{% provide %}` tag.
|
||||
For a component to be able to "inject" some data, the component ([`{% component %}`](../../../reference/template_tags/#component) tag) must be nested inside the [`{% provide %}`](../../../reference/template_tags/#provide) tag.
|
||||
|
||||
In the example from previous section, we've defined two kwargs: `key="hi" another=123`. That means that if we now inject `"my_data"`, we get an object with 2 attributes - `key` and `another`.
|
||||
In the example from previous section, we've defined two kwargs: `hello="hi" another=123`. That means that if we now inject `"my_data"`, we get an object with 2 attributes - `hello` and `another`.
|
||||
|
||||
```py
|
||||
class ChildComponent(Component):
|
||||
def get_context_data(self):
|
||||
my_data = self.inject("my_data")
|
||||
print(my_data.key) # hi
|
||||
print(my_data.another) # 123
|
||||
print(my_data.hello) # hi
|
||||
print(my_data.another) # 123
|
||||
return {}
|
||||
```
|
||||
|
||||
First argument to `inject` is the _key_ (or _name_) of the provided data. This
|
||||
must match the string that you used in the `provide` tag. If no provider
|
||||
with given key is found, `inject` raises a `KeyError`.
|
||||
First argument to [`Component.inject()`](../../../reference/api/#django_components.Component.inject) is the _key_ (or _name_) of the provided data. This
|
||||
must match the string that you used in the [`{% provide %}`](../../../reference/template_tags/#provide) tag.
|
||||
|
||||
To avoid the error, you can pass a second argument to `inject` to which will act as a default value, similar to `dict.get(key, default)`:
|
||||
If no provider with given key is found, [`inject()`](../../../reference/api/#django_components.Component.inject) raises a `KeyError`.
|
||||
|
||||
To avoid the error, you can pass a second argument to [`inject()`](../../../reference/api/#django_components.Component.inject). This will act as a default value similar to `dict.get(key, default)`:
|
||||
|
||||
```py
|
||||
class ChildComponent(Component):
|
||||
def get_context_data(self):
|
||||
my_data = self.inject("invalid_key", DEFAULT_DATA)
|
||||
assert my_data == DEFAUKT_DATA
|
||||
assert my_data == DEFAULT_DATA
|
||||
return {}
|
||||
```
|
||||
|
||||
The instance returned from `inject()` is a subclass of `NamedTuple`, so the instance is immutable. This ensures that the data returned from `inject` will always
|
||||
have all the keys that were passed to the `provide` tag.
|
||||
!!! note
|
||||
|
||||
> NOTE: `inject()` works strictly only in `get_context_data`. If you try to call it from elsewhere, it will raise an error.
|
||||
The instance returned from [`inject()`](../../../reference/api/#django_components.Component.inject) is immutable (subclass of [`NamedTuple`](https://docs.python.org/3/library/typing.html#typing.NamedTuple)). This ensures that the data returned from [`inject()`](../../../reference/api/#django_components.Component.inject) will always
|
||||
have all the keys that were passed to the [`{% provide %}`](../../../reference/template_tags/#provide) tag.
|
||||
|
||||
!!! warning
|
||||
|
||||
[`inject()`](../../../reference/api/#django_components.Component.inject) works strictly only during render execution. If you try to call `inject()` from outside, it will raise an error.
|
||||
|
||||
## Full example
|
||||
|
||||
|
@ -106,7 +115,7 @@ have all the keys that were passed to the `provide` tag.
|
|||
@register("child")
|
||||
class ChildComponent(Component):
|
||||
template = """
|
||||
<div> {{ my_data.key }} </div>
|
||||
<div> {{ my_data.hello }} </div>
|
||||
<div> {{ my_data.another }} </div>
|
||||
"""
|
||||
|
||||
|
@ -116,7 +125,7 @@ class ChildComponent(Component):
|
|||
|
||||
template_str = """
|
||||
{% load component_tags %}
|
||||
{% provide "my_data" key="hi" another=123 %}
|
||||
{% provide "my_data" hello="hi" another=123 %}
|
||||
{% component "child" / %}
|
||||
{% endprovide %}
|
||||
"""
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
nav:
|
||||
- Single-file components: single_file_components.md
|
||||
- Components in Python: components_in_python.md
|
||||
- Accessing component inputs: access_component_input.md
|
||||
- Render API: render_api.md
|
||||
- Component defaults: component_defaults.md
|
||||
- Component context and scope: component_context_scope.md
|
||||
- Template tag syntax: template_tag_syntax.md
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
When you call `Component.render` or `Component.render_to_response`, the inputs to these methods can be accessed from within the instance under `self.input`.
|
||||
|
||||
This means that you can use `self.input` inside:
|
||||
|
||||
- `get_context_data`
|
||||
- `get_template_name`
|
||||
- `get_template`
|
||||
- `on_render_before`
|
||||
- `on_render_after`
|
||||
|
||||
`self.input` is only defined during the execution of `Component.render`, and raises a `RuntimeError` when called outside of this context.
|
||||
|
||||
`self.input` has the same fields as the input to `Component.render`:
|
||||
|
||||
```python
|
||||
class TestComponent(Component):
|
||||
def get_context_data(self, var1, var2, variable, another, **attrs):
|
||||
assert self.input.args == (123, "str")
|
||||
assert self.input.kwargs == {"variable": "test", "another": 1}
|
||||
assert self.input.slots == {"my_slot": "MY_SLOT"}
|
||||
assert isinstance(self.input.context, Context)
|
||||
|
||||
return {
|
||||
"variable": variable,
|
||||
}
|
||||
|
||||
rendered = TestComponent.render(
|
||||
kwargs={"variable": "test", "another": 1},
|
||||
args=(123, "str"),
|
||||
slots={"my_slot": "MY_SLOT"},
|
||||
)
|
||||
```
|
||||
|
||||
NOTE: The slots in `self.input.slots` are normalized to slot functions.
|
|
@ -1,6 +1,13 @@
|
|||
Every component that you want to use in the template with the [`{% component %}`](django_components.templateags.component_tags)
|
||||
tag needs to be registered with the [`ComponentRegistry`](django_components.component_registry.ComponentRegistry).
|
||||
Normally, we use the [`@register`](django_components.component_registry.register) decorator for that:
|
||||
django-components automatically register all components found in the
|
||||
[`COMPONENTS.dirs`](../../../reference/settings#django_components.app_settings.ComponentsSettings.dirs) and
|
||||
[`COMPONENTS.app_dirs`](../../../reference/settings#django_components.app_settings.ComponentsSettings.app_dirs)
|
||||
directories by loading all Python files in these directories.
|
||||
|
||||
### Manually register components
|
||||
Every component that you want to use in the template with the
|
||||
[`{% component %}`](../../../reference/template_tags#component)
|
||||
tag needs to be registered with the [`ComponentRegistry`](../../../reference/api#django_components.ComponentRegistry).
|
||||
Normally, we use the [`@register`](../../../reference/api#django_components.register) decorator for that:
|
||||
|
||||
```python
|
||||
from django_components import Component, register
|
||||
|
@ -12,6 +19,8 @@ class Calendar(Component):
|
|||
|
||||
But for the component to be registered, the code needs to be executed - and for that, the file needs to be imported as a module.
|
||||
|
||||
This is the "discovery" part of the process.
|
||||
|
||||
One way to do that is by importing all your components in `apps.py`:
|
||||
|
||||
```python
|
||||
|
@ -30,23 +39,28 @@ class MyAppConfig(AppConfig):
|
|||
|
||||
However, there's a simpler way!
|
||||
|
||||
By default, the Python files in the [`COMPONENTS.dirs`](django_components.app_settings.ComponentsSettings.dirs) directories (and app-level [`[app]/components/`](django_components.app_settings.ComponentsSettings.app_dirs)) are auto-imported in order to auto-register the components.
|
||||
### Autodiscovery
|
||||
|
||||
Autodiscovery occurs when Django is loaded, during the [`AppConfig.ready`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready)
|
||||
By default, the Python files found in the
|
||||
[`COMPONENTS.dirs`](../../../reference/settings#django_components.app_settings.ComponentsSettings.dirs) and
|
||||
[`COMPONENTS.app_dirs`](../../../reference/settings#django_components.app_settings.ComponentsSettings.app_dirs)
|
||||
are auto-imported in order to register the components.
|
||||
|
||||
Autodiscovery occurs when Django is loaded, during the [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready)
|
||||
hook of the `apps.py` file.
|
||||
|
||||
If you are using autodiscovery, keep a few points in mind:
|
||||
|
||||
- Avoid defining any logic on the module-level inside the `components` dir, that you would not want to run anyway.
|
||||
- Components inside the auto-imported files still need to be registered with [`@register`](django_components.component_registry.register)
|
||||
- Avoid defining any logic on the module-level inside the components directories, that you would not want to run anyway.
|
||||
- Components inside the auto-imported files still need to be registered with [`@register`](../../../reference/api#django_components.register)
|
||||
- Auto-imported component files must be valid Python modules, they must use suffix `.py`, and module name should follow [PEP-8](https://peps.python.org/pep-0008/#package-and-module-names).
|
||||
- Subdirectories and files starting with an underscore `_` (except `__init__.py`) are ignored.
|
||||
|
||||
Autodiscovery can be disabled in the [settings](django_components.app_settings.ComponentsSettings.autodiscovery).
|
||||
Autodiscovery can be disabled in the settings with [`autodiscover=False`](../../../reference/settings#django_components.app_settings.ComponentsSettings.autodiscover).
|
||||
|
||||
### Manually trigger autodiscovery
|
||||
|
||||
Autodiscovery can be also triggered manually, using the [`autodiscover`](django_components.autodiscovery.autodiscover) function. This is useful if you want to run autodiscovery at a custom point of the lifecycle:
|
||||
Autodiscovery can be also triggered manually, using the [`autodiscover()`](../../../reference/api#django_components.autodiscover) function. This is useful if you want to run autodiscovery at a custom point of the lifecycle:
|
||||
|
||||
```python
|
||||
from django_components import autodiscover
|
||||
|
|
|
@ -17,9 +17,7 @@ django-components has a suite of features that help you write and manage views a
|
|||
|
||||
- In addition, [`Component`](../../../reference/api#django_components.Component) has a [`render_to_response()`](../../../reference/api#django_components.Component.render_to_response) method that renders the component template based on the provided input and returns an `HttpResponse` object.
|
||||
|
||||
## Component as view example
|
||||
|
||||
### Define handlers
|
||||
## Define handlers
|
||||
|
||||
Here's an example of a calendar component defined as a view. Simply define a `View` class with your custom `get()` method to handle GET requests:
|
||||
|
||||
|
@ -83,7 +81,7 @@ class Calendar(Component):
|
|||
|
||||
This is deprecated from v0.137 onwards, and will be removed in v1.0.
|
||||
|
||||
### Register the URLs manually
|
||||
## Register URLs manually
|
||||
|
||||
To register the component as a route / endpoint in Django, add an entry to your
|
||||
[`urlpatterns`](https://docs.djangoproject.com/en/5.1/topics/http/urls/).
|
||||
|
@ -102,7 +100,7 @@ urlpatterns = [
|
|||
internally calls [`View.as_view()`](https://docs.djangoproject.com/en/5.1/ref/class-based-views/base/#django.views.generic.base.View.as_view), passing the component
|
||||
instance as one of the arguments.
|
||||
|
||||
### Register the URLs automatically
|
||||
## Register URLs automatically
|
||||
|
||||
If you don't care about the exact URL of the component, you can let django-components manage the URLs for you by setting the [`Component.Url.public`](../../../reference/api#django_components.ComponentUrl.public) attribute to `True`:
|
||||
|
||||
|
|
|
@ -329,7 +329,7 @@ Renders:
|
|||
<div class="my-class extra-class"></div>
|
||||
```
|
||||
|
||||
### Merging `style` Attributes
|
||||
### Merging `style` attributes
|
||||
|
||||
The `style` attribute can be specified as a string of style properties as usual.
|
||||
|
||||
|
@ -364,8 +364,8 @@ If you want granular control over individual style properties, you can use a dic
|
|||
|
||||
If a style property is specified multiple times, the last value is used.
|
||||
|
||||
- If the last time the property is set is `False`, the property is removed.
|
||||
- Properties set to `None` are ignored.
|
||||
- If the last non-`None` instance of the property is set to `False`, the property is removed.
|
||||
|
||||
**Example:**
|
||||
|
||||
|
|
121
docs/concepts/fundamentals/render_api.md
Normal file
121
docs/concepts/fundamentals/render_api.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
When a component is being rendered, whether with [`Component.render()`](../../../reference/api#django_components.Component.render)
|
||||
or [`{% component %}`](../../../reference/template_tags#component), a component instance is populated with the current inputs and context. This allows you to access things like component inputs - methods and attributes on the component instance which would otherwise not be available.
|
||||
|
||||
We refer to these render-only methods and attributes as the "Render API".
|
||||
|
||||
Render API is available inside these [`Component`](../../../reference/api#django_components.Component) methods:
|
||||
|
||||
- [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
|
||||
- [`on_render_before()`](../../../reference/api#django_components.Component.on_render_before)
|
||||
- [`on_render_after()`](../../../reference/api#django_components.Component.on_render_after)
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **attrs):
|
||||
# Access component's ID
|
||||
assert self.id == "djc1A2b3c"
|
||||
|
||||
# Access component's inputs, slots and context
|
||||
assert self.input.args == (123, "str")
|
||||
assert self.input.kwargs == {"variable": "test", "another": 1}
|
||||
footer_slot = self.input.slots["footer"]
|
||||
some_var = self.input.context["some_var"]
|
||||
|
||||
# Access the request object and Django's context processors, if available
|
||||
assert self.request.GET == {"query": "something"}
|
||||
assert self.context_processors_data['user'].username == "admin"
|
||||
|
||||
return {
|
||||
"variable": variable,
|
||||
}
|
||||
|
||||
rendered = Table.render(
|
||||
kwargs={"variable": "test", "another": 1},
|
||||
args=(123, "str"),
|
||||
slots={"footer": "MY_SLOT"},
|
||||
)
|
||||
```
|
||||
|
||||
## Accessing Render API
|
||||
|
||||
If you try to access the Render API outside of a render call, you will get a `RuntimeError`.
|
||||
|
||||
## Component ID
|
||||
|
||||
Component ID (or render ID) is a unique identifier for the current render call.
|
||||
|
||||
That means that if you call [`Component.render()`](../../../reference/api#django_components.Component.render)
|
||||
multiple times, the ID will be different for each call.
|
||||
|
||||
It is available as [`self.id`](../../../reference/api#django_components.Component.id).
|
||||
|
||||
## Component inputs
|
||||
|
||||
All the component inputs are captured and available as [`self.input`](../../../reference/api/#django_components.Component.input).
|
||||
|
||||
[`self.input`](../../../reference/api/#django_components.Component.input) ([`ComponentInput`](../../../reference/api/#django_components.ComponentInput)) has the mostly the same fields as the input to [`Component.render()`](../../../reference/api/#django_components.Component.render). This includes:
|
||||
|
||||
- `args` - List of positional arguments
|
||||
- `kwargs` - Dictionary of keyword arguments
|
||||
- `slots` - Dictionary of slots. Values are normalized to [`Slot`](../../../reference/api/#django_components.Slot) instances
|
||||
- `context` - [`Context`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.Context) object that should be used to render the component
|
||||
- And other kwargs passed to [`Component.render()`](../../../reference/api/#django_components.Component.render) like `type` and `render_dependencies`
|
||||
|
||||
Thus, use can use [`self.input.args`](../../../reference/api/#django_components.ComponentInput.args)
|
||||
and [`self.input.kwargs`](../../../reference/api/#django_components.ComponentInput.kwargs)
|
||||
to access the positional and keyword arguments passed to [`Component.render()`](../../../reference/api/#django_components.Component.render).
|
||||
|
||||
```python
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
# Access component's inputs, slots and context
|
||||
assert self.input.args == [123, "str"]
|
||||
assert self.input.kwargs == {"variable": "test", "another": 1}
|
||||
footer_slot = self.input.slots["footer"]
|
||||
some_var = self.input.context["some_var"]
|
||||
|
||||
return {}
|
||||
|
||||
rendered = TestComponent.render(
|
||||
kwargs={"variable": "test", "another": 1},
|
||||
args=(123, "str"),
|
||||
slots={"footer": "MY_SLOT"},
|
||||
)
|
||||
```
|
||||
|
||||
## Request object and context processors
|
||||
|
||||
If the component was either:
|
||||
|
||||
- Given a [`request`](../../../reference/api/#django_components.Component.render) kwarg
|
||||
- Rendered with [`RenderContext`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.RequestContext)
|
||||
- Nested in another component for which any of these conditions is true
|
||||
|
||||
Then the request object will be available in [`self.request`](../../../reference/api/#django_components.Component.request).
|
||||
|
||||
If the request object is available, you will also be able to access the [`context processors`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#configuring-an-engine) data in [`self.context_processors_data`](../../../reference/api/#django_components.Component.context_processors_data).
|
||||
|
||||
This is a dictionary with the context processors data.
|
||||
|
||||
If the request object is not available, then [`self.context_processors_data`](../../../reference/api/#django_components.Component.context_processors_data) will be an empty dictionary.
|
||||
|
||||
```python
|
||||
from django.http import HttpRequest
|
||||
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **attrs):
|
||||
# Access the request object and Django's context processors
|
||||
assert self.request.GET == {"query": "something"}
|
||||
assert self.context_processors_data['user'].username == "admin"
|
||||
|
||||
return {}
|
||||
|
||||
rendered = Table.render(
|
||||
request=HttpRequest(),
|
||||
)
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
The [`self.context_processors_data`](../../../reference/api/#django_components.Component.context_processors_data) object is generated dynamically, so changes to it are not persisted.
|
|
@ -85,7 +85,7 @@ Other than that, you can use spread operators multiple times, and even put keywo
|
|||
|
||||
In a case of conflicts, the values added later (right-most) overwrite previous values.
|
||||
|
||||
## Use template tags inside component inputs
|
||||
## Template tags inside literal strings
|
||||
|
||||
_New in version 0.93_
|
||||
|
||||
|
@ -119,26 +119,22 @@ Instead, template tags in django_components (`{% component %}`, `{% slot %}`, `{
|
|||
/ %}
|
||||
```
|
||||
|
||||
In the example above:
|
||||
In the example above, the component receives:
|
||||
|
||||
- Component `test` receives a positional argument with value `"As positional arg "`. The comment is omitted.
|
||||
- Kwarg `title` is passed as a string, e.g. `John Doe`
|
||||
- Kwarg `id` is passed as `int`, e.g. `15`
|
||||
- Kwarg `readonly` is passed as `bool`, e.g. `False`
|
||||
- Kwarg `author` is passed as a string, e.g. `John Wick ` (Comment omitted)
|
||||
- Positional argument `"As positional arg "` (Comment omitted)
|
||||
- `title` - passed as `str`, e.g. `John Doe`
|
||||
- `id` - passed as `int`, e.g. `15`
|
||||
- `readonly` - passed as `bool`, e.g. `False`
|
||||
- `author` - passed as `str`, e.g. `John Wick ` (Comment omitted)
|
||||
|
||||
This is inspired by [django-cotton](https://github.com/wrabit/django-cotton#template-expressions-in-attributes).
|
||||
|
||||
### Passing data as string vs original values
|
||||
|
||||
Sometimes you may want to use the template tags to transform
|
||||
or generate the data that is then passed to the component.
|
||||
In the example above, the kwarg `id` was passed as an integer, NOT a string.
|
||||
|
||||
The data doesn't necessarily have to be strings. In the example above, the kwarg `id` was passed as an integer, NOT a string.
|
||||
|
||||
Although the string literals for components inputs are treated as regular Django templates, there is one special case:
|
||||
|
||||
When the string literal contains only a single template tag, with no extra text, then the value is passed as the original type instead of a string.
|
||||
When the string literal contains only a single template tag, with no extra text (and no extra whitespace),
|
||||
then the value is passed as the original type instead of a string.
|
||||
|
||||
Here, `page` is an integer:
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue