refactor: deprecate Component.input and add raw_args, raw_kwargs, raw_slots (#1233)

* refactor: deprecate Component.input and add raw_args, raw_kwargs, raw_slots

* docs: update changelog
This commit is contained in:
Juro Oravec 2025-06-04 23:38:50 +02:00 committed by GitHub
parent eceebb9696
commit 04f79a6e6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 295 additions and 101 deletions

View file

@ -586,6 +586,37 @@ Summary:
) )
``` ```
- `Component.input` (and its type `ComponentInput`) is now deprecated. The `input` property will be removed in v1.
Instead, use attributes directly on the Component instance.
Before:
```py
class MyComponent(Component):
def on_render(self, context, template):
assert self.input.args == [1, 2, 3]
assert self.input.kwargs == {"a": 1, "b": 2}
assert self.input.slots == {"my_slot": "CONTENT"}
assert self.input.context == {"my_slot": "CONTENT"}
assert self.input.deps_strategy == "document"
assert self.input.type == "document"
assert self.input.render_dependencies == True
```
After:
```py
class MyComponent(Component):
def on_render(self, context, template):
assert self.args == [1, 2, 3]
assert self.kwargs == {"a": 1, "b": 2}
assert self.slots == {"my_slot": "CONTENT"}
assert self.context == {"my_slot": "CONTENT"}
assert self.deps_strategy == "document"
assert (self.deps_strategy != "ignore") is True
```
- Component method `on_render_after` was updated to receive also `error` field. - Component method `on_render_after` was updated to receive also `error` field.
For backwards compatibility, the `error` field can be omitted until v1. For backwards compatibility, the `error` field can be omitted until v1.
@ -976,6 +1007,22 @@ Summary:
Same as with the parameters in `Component.get_template_data()`, they will be instances of the `Args`, `Kwargs`, `Slots` classes Same as with the parameters in `Component.get_template_data()`, they will be instances of the `Args`, `Kwargs`, `Slots` classes
if defined, or plain lists / dictionaries otherwise. if defined, or plain lists / dictionaries otherwise.
- 4 attributes that were previously available only under the `Component.input` attribute
are now available directly on the Component instance:
- `Component.raw_args`
- `Component.raw_kwargs`
- `Component.raw_slots`
- `Component.deps_strategy`
The first 3 attributes are the same as the deprecated `Component.input.args`, `Component.input.kwargs`, `Component.input.slots` properties.
Compared to the `Component.args` / `Component.kwargs` / `Component.slots` attributes,
these "raw" attributes are not typed and will remain as plain lists / dictionaries
even if you define the `Args`, `Kwargs`, `Slots` classes.
The `Component.deps_strategy` attribute is the same as the deprecated `Component.input.deps_strategy` property.
- New template variables `{{ component_vars.args }}`, `{{ component_vars.kwargs }}`, `{{ component_vars.slots }}` - New template variables `{{ component_vars.args }}`, `{{ component_vars.kwargs }}`, `{{ component_vars.slots }}`
These attributes are the same as the ones available in `Component.get_template_data()`. These attributes are the same as the ones available in `Component.get_template_data()`.

View file

@ -240,7 +240,7 @@ class Table(Component):
assert self.args == [123, "str"] assert self.args == [123, "str"]
assert self.kwargs == {"variable": "test", "another": 1} assert self.kwargs == {"variable": "test", "another": 1}
footer_slot = self.slots["footer"] footer_slot = self.slots["footer"]
some_var = self.input.context["some_var"] some_var = self.context["some_var"]
# Access the request object and Django's context processors, if available # Access the request object and Django's context processors, if available
assert self.request.GET == {"query": "something"} assert self.request.GET == {"query": "something"}

View file

@ -80,7 +80,7 @@ and so `selected_items` will be set to `[1, 2, 3]`.
This is **NOT recommended**, because: This is **NOT recommended**, because:
- The defaults will NOT be applied to inputs when using [`self.input`](../../../reference/api/#django_components.Component.input) property. - The defaults will NOT be applied to inputs when using [`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs) property.
- The defaults will NOT be applied when a field is given but set to `None`. - The defaults will NOT be applied when a field is given but set to `None`.
Instead, define the defaults in the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class. Instead, define the defaults in the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class.

View file

@ -241,8 +241,24 @@ class ProfileCard(Component):
} }
``` ```
<!-- TODO_v1 - Remove -->
### `input` property (low-level) ### `input` property (low-level)
!!! warning
The `input` property is deprecated and will be removed in v1.
Instead, use properties defined on the
[`Component`](../../../reference/api/#django_components.Component) class
directly like
[`self.context`](../../../reference/api/#django_components.Component.context).
To access the unmodified inputs, use
[`self.raw_args`](../../../reference/api/#django_components.Component.raw_args),
[`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs),
and [`self.raw_slots`](../../../reference/api/#django_components.Component.raw_slots) properties.
The previous two approaches allow you to access only the most important inputs. The previous two approaches allow you to access only the most important inputs.
There are additional settings that may be passed to components. There are additional settings that may be passed to components.
@ -260,8 +276,6 @@ This includes:
- [`input.type`](../../../reference/api/#django_components.ComponentInput.type) - The type of the component (document, fragment) - [`input.type`](../../../reference/api/#django_components.ComponentInput.type) - The type of the component (document, fragment)
- [`input.render_dependencies`](../../../reference/api/#django_components.ComponentInput.render_dependencies) - Whether to render dependencies (CSS, JS) - [`input.render_dependencies`](../../../reference/api/#django_components.ComponentInput.render_dependencies) - Whether to render dependencies (CSS, JS)
For more details, see [Component inputs](../render_api/#other-inputs).
```python ```python
class ProfileCard(Component): class ProfileCard(Component):
def get_template_data(self, args, kwargs, slots, context): def get_template_data(self, args, kwargs, slots, context):
@ -336,7 +350,7 @@ class ProfileCard(Component):
This is **NOT recommended**, because: This is **NOT recommended**, because:
- The defaults will NOT be applied to inputs when using [`self.input`](../../../reference/api/#django_components.Component.input) property. - The defaults will NOT be applied to inputs when using [`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs) property.
- The defaults will NOT be applied when a field is given but set to `None`. - The defaults will NOT be applied when a field is given but set to `None`.
Instead, define the defaults in the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class. Instead, define the defaults in the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class.
@ -348,8 +362,10 @@ All three data methods have access to the Component's [Render API](../render_api
- [`self.args`](../render_api/#args) - The positional arguments for the current render call - [`self.args`](../render_api/#args) - The positional arguments for the current render call
- [`self.kwargs`](../render_api/#kwargs) - The keyword arguments for the current render call - [`self.kwargs`](../render_api/#kwargs) - The keyword arguments for the current render call
- [`self.slots`](../render_api/#slots) - The slots for the current render call - [`self.slots`](../render_api/#slots) - The slots for the current render call
- [`self.raw_args`](../render_api/#args) - Unmodified positional arguments for the current render call
- [`self.raw_kwargs`](../render_api/#kwargs) - Unmodified keyword arguments for the current render call
- [`self.raw_slots`](../render_api/#slots) - Unmodified slots for the current render call
- [`self.context`](../render_api/#context) - The context for the current render call - [`self.context`](../render_api/#context) - The context for the current render call
- [`self.input`](../render_api/#other-inputs) - All the component inputs
- [`self.id`](../render_api/#component-id) - The unique ID for the current render call - [`self.id`](../render_api/#component-id) - The unique ID for the current render call
- [`self.request`](../render_api/#request-and-context-processors) - The request object - [`self.request`](../render_api/#request-and-context-processors) - The request object
- [`self.context_processors_data`](../render_api/#request-and-context-processors) - Data from Django's context processors - [`self.context_processors_data`](../render_api/#request-and-context-processors) - Data from Django's context processors
@ -357,6 +373,7 @@ All three data methods have access to the Component's [Render API](../render_api
- [`self.registry`](../render_api/#template-tag-metadata) - The [`ComponentRegistry`](../../../reference/api/#django_components.ComponentRegistry) 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.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 - [`self.outer_context`](../render_api/#template-tag-metadata) - The context outside of the [`{% component %}`](../../../reference/template_tags#component) tag
- `self.deps_strategy` - The strategy for rendering dependencies
## Type hints ## Type hints
@ -399,7 +416,11 @@ class Button(Component):
!!! note !!! note
The data available via [`self.input`](../../../reference/api/#django_components.Component.input) property is NOT typed. To access "untyped" inputs, use [`self.raw_args`](../../../reference/api/#django_components.Component.raw_args),
[`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs),
and [`self.raw_slots`](../../../reference/api/#django_components.Component.raw_slots) properties.
These are plain lists and dictionaries, even when you added typing to your component.
### Typing data ### Typing data

View file

@ -25,7 +25,7 @@ class Table(Component):
assert self.args == [123, "str"] assert self.args == [123, "str"]
assert self.kwargs == {"variable": "test", "another": 1} assert self.kwargs == {"variable": "test", "another": 1}
footer_slot = self.slots["footer"] footer_slot = self.slots["footer"]
some_var = self.input.context["some_var"] some_var = self.context["some_var"]
def get_template_data(self, args, kwargs, slots, context): def get_template_data(self, args, kwargs, slots, context):
# Access the request object and Django's context processors, if available # Access the request object and Django's context processors, if available
@ -47,8 +47,11 @@ The Render API includes:
- [`self.args`](../render_api/#args) - The positional arguments for the current render call - [`self.args`](../render_api/#args) - The positional arguments for the current render call
- [`self.kwargs`](../render_api/#kwargs) - The keyword arguments for the current render call - [`self.kwargs`](../render_api/#kwargs) - The keyword arguments for the current render call
- [`self.slots`](../render_api/#slots) - The slots for the current render call - [`self.slots`](../render_api/#slots) - The slots for the current render call
- [`self.raw_args`](../render_api/#args) - Unmodified positional arguments for the current render call
- [`self.raw_kwargs`](../render_api/#kwargs) - Unmodified keyword arguments for the current render call
- [`self.raw_slots`](../render_api/#slots) - Unmodified slots for the current render call
- [`self.context`](../render_api/#context) - The context for the current render call - [`self.context`](../render_api/#context) - The context for the current render call
- [`self.input`](../render_api/#other-inputs) - All the component inputs - [`self.deps_strategy`](../../advanced/rendering_js_css#dependencies-strategies) - The strategy for rendering dependencies
- Request-related: - Request-related:
- [`self.request`](../render_api/#request-and-context-processors) - The request object (if available) - [`self.request`](../render_api/#request-and-context-processors) - The request object (if available)
@ -78,6 +81,9 @@ then the [`Component.args`](../../../reference/api/#django_components.Component.
Otherwise, `args` will be a plain list. Otherwise, `args` will be a plain list.
Use [`self.raw_args`](../../../reference/api/#django_components.Component.raw_args)
to access the positional arguments as a plain list irrespective of [`Component.Args`](../../../reference/api/#django_components.Component.Args).
**Example:** **Example:**
With `Args` class: With `Args` class:
@ -120,6 +126,9 @@ then the [`Component.kwargs`](../../../reference/api/#django_components.Componen
Otherwise, `kwargs` will be a plain dictionary. Otherwise, `kwargs` will be a plain dictionary.
Use [`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs)
to access the keyword arguments as a plain dictionary irrespective of [`Component.Kwargs`](../../../reference/api/#django_components.Component.Kwargs).
**Example:** **Example:**
With `Kwargs` class: With `Kwargs` class:
@ -162,6 +171,9 @@ then the [`Component.slots`](../../../reference/api/#django_components.Component
Otherwise, `slots` will be a plain dictionary. Otherwise, `slots` will be a plain dictionary.
Use [`self.raw_slots`](../../../reference/api/#django_components.Component.raw_slots)
to access the slots as a plain dictionary irrespective of [`Component.Slots`](../../../reference/api/#django_components.Component.Slots).
**Example:** **Example:**
With `Slots` class: With `Slots` class:
@ -217,44 +229,6 @@ Whether the context variables defined in `context` are available to the template
- In `"isolated"` context behavior mode, the template will NOT have access to this context, - In `"isolated"` context behavior mode, the template will NOT have access to this context,
and data MUST be passed via component's args and kwargs. and data MUST be passed via component's args and kwargs.
### Other inputs
You can access the most important inputs via [`self.args`](../render_api/#args),
[`self.kwargs`](../render_api/#kwargs),
and [`self.slots`](../render_api/#slots) properties.
There are additional settings that may be passed to components.
If you need to access these, you can use [`self.input`](../../../reference/api/#django_components.Component.input) property
for a low-level access to all the inputs passed to the component.
[`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`
For example, you 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
class Table(Component):
def get_template_data(self, args, kwargs, slots, context):
# 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"]
rendered = TestComponent.render(
kwargs={"variable": "test", "another": 1},
args=(123, "str"),
slots={"footer": "MY_SLOT"},
)
```
## Component ID ## Component ID
Component ID (or render ID) is a unique identifier for the current render call. Component ID (or render ID) is a unique identifier for the current render call.

View file

@ -230,7 +230,7 @@ class Table(Component):
assert self.args == [123, "str"] assert self.args == [123, "str"]
assert self.kwargs == {"variable": "test", "another": 1} assert self.kwargs == {"variable": "test", "another": 1}
footer_slot = self.slots["footer"] footer_slot = self.slots["footer"]
some_var = self.input.context["some_var"] some_var = self.context["some_var"]
# Access the request object and Django's context processors, if available # Access the request object and Django's context processors, if available
assert self.request.GET == {"query": "something"} assert self.request.GET == {"query": "something"}

View file

@ -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#L3685" target="_blank">See source code</a> <a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L3796" target="_blank">See source code</a>

View file

@ -209,9 +209,12 @@ def get_component_by_class_id(comp_cls_id: str) -> Type["Component"]:
return comp_cls_id_mapping[comp_cls_id] return comp_cls_id_mapping[comp_cls_id]
# TODO_v1 - Remove with `Component.input`
@dataclass(frozen=True) @dataclass(frozen=True)
class ComponentInput: class ComponentInput:
""" """
Deprecated. Will be removed in v1.
Object holding the inputs that were passed to [`Component.render()`](../api#django_components.Component.render) Object holding the inputs that were passed to [`Component.render()`](../api#django_components.Component.render)
or the [`{% component %}`](../template_tags#component) template tag. or the [`{% component %}`](../template_tags#component) template tag.
@ -2280,9 +2283,13 @@ class Component(metaclass=ComponentMeta):
self.args = default(args, []) self.args = default(args, [])
self.kwargs = default(kwargs, {}) self.kwargs = default(kwargs, {})
self.slots = default(slots, {}) self.slots = default(slots, {})
self.raw_args: List[Any] = self.args if isinstance(self.args, list) else list(self.args)
self.raw_kwargs: Dict[str, Any] = self.kwargs if isinstance(self.kwargs, dict) else to_dict(self.kwargs)
self.raw_slots: Dict[str, Slot] = self.slots if isinstance(self.slots, dict) else to_dict(self.slots)
self.context = default(context, Context()) self.context = default(context, Context())
# TODO_v1 - Remove `is_filled`, superseded by `Component.slots` # TODO_v1 - Remove `is_filled`, superseded by `Component.slots`
self.is_filled = SlotIsFilled(to_dict(self.slots)) self.is_filled = SlotIsFilled(to_dict(self.slots))
# TODO_v1 - Remove `Component.input`
self.input = ComponentInput( self.input = ComponentInput(
context=self.context, context=self.context,
# NOTE: Convert args / kwargs / slots to plain lists / dicts # NOTE: Convert args / kwargs / slots to plain lists / dicts
@ -2295,6 +2302,7 @@ class Component(metaclass=ComponentMeta):
# TODO_v1 - Remove, superseded by `deps_strategy` # TODO_v1 - Remove, superseded by `deps_strategy`
render_dependencies=deps_strategy != "ignore", render_dependencies=deps_strategy != "ignore",
) )
self.deps_strategy = deps_strategy
self.request = request self.request = request
self.outer_context: Optional[Context] = outer_context self.outer_context: Optional[Context] = outer_context
self.registry = default(registry, registry_) self.registry = default(registry, registry_)
@ -2410,8 +2418,11 @@ class Component(metaclass=ComponentMeta):
``` ```
""" """
# TODO_v1 - Remove `Component.input`
input: ComponentInput input: ComponentInput
""" """
Deprecated. Will be removed in v1.
Input holds the data that were passed to the current component at render time. Input holds the data that were passed to the current component at render time.
This includes: This includes:
@ -2426,8 +2437,6 @@ class Component(metaclass=ComponentMeta):
- And other kwargs passed to [`Component.render()`](../api/#django_components.Component.render) - And other kwargs passed to [`Component.render()`](../api/#django_components.Component.render)
like `deps_strategy` like `deps_strategy`
Read more on [Component inputs](../../concepts/fundamentals/render_api/#other-inputs).
**Example:** **Example:**
```python ```python
@ -2449,15 +2458,16 @@ class Component(metaclass=ComponentMeta):
args: Any args: Any
""" """
The `args` argument as passed to Positional arguments passed to the component.
[`Component.get_template_data()`](../api/#django_components.Component.get_template_data).
This is part of the [Render API](../../concepts/fundamentals/render_api). This is part of the [Render API](../../concepts/fundamentals/render_api).
If you defined the [`Component.Args`](../api/#django_components.Component.Args) class, `args` has the same behavior as the `args` argument of
then the `args` property will return an instance of that class. [`Component.get_template_data()`](../api/#django_components.Component.get_template_data):
Otherwise, `args` will be a plain list. - If you defined the [`Component.Args`](../api/#django_components.Component.Args) class,
then the `args` property will return an instance of that `Args` class.
- Otherwise, `args` will be a plain list.
**Example:** **Example:**
@ -2492,17 +2502,40 @@ class Component(metaclass=ComponentMeta):
``` ```
""" """
kwargs: Any raw_args: List[Any]
""" """
The `kwargs` argument as passed to Positional arguments passed to the component.
[`Component.get_template_data()`](../api/#django_components.Component.get_template_data).
This is part of the [Render API](../../concepts/fundamentals/render_api). This is part of the [Render API](../../concepts/fundamentals/render_api).
If you defined the [`Component.Kwargs`](../api/#django_components.Component.Kwargs) class, Unlike [`Component.args`](../api/#django_components.Component.args), this attribute
then the `kwargs` property will return an instance of that class. is not typed and will remain as plain list even if you define the
[`Component.Args`](../api/#django_components.Component.Args) class.
Otherwise, `kwargs` will be a plain dict. **Example:**
```python
from django_components import Component
class Table(Component):
def on_render_before(self, context: Context, template: Optional[Template]) -> None:
assert self.raw_args[0] == 123
assert self.raw_args[1] == 10
```
"""
kwargs: Any
"""
Keyword arguments passed to the component.
This is part of the [Render API](../../concepts/fundamentals/render_api).
`kwargs` has the same behavior as the `kwargs` argument of
[`Component.get_template_data()`](../api/#django_components.Component.get_template_data):
- If you defined the [`Component.Kwargs`](../api/#django_components.Component.Kwargs) class,
then the `kwargs` property will return an instance of that `Kwargs` class.
- Otherwise, `kwargs` will be a plain dict.
**Example:** **Example:**
@ -2540,17 +2573,40 @@ class Component(metaclass=ComponentMeta):
``` ```
""" """
slots: Any raw_kwargs: Dict[str, Any]
""" """
The `slots` argument as passed to Keyword arguments passed to the component.
[`Component.get_template_data()`](../api/#django_components.Component.get_template_data).
This is part of the [Render API](../../concepts/fundamentals/render_api). This is part of the [Render API](../../concepts/fundamentals/render_api).
If you defined the [`Component.Slots`](../api/#django_components.Component.Slots) class, Unlike [`Component.kwargs`](../api/#django_components.Component.kwargs), this attribute
then the `slots` property will return an instance of that class. is not typed and will remain as plain dict even if you define the
[`Component.Kwargs`](../api/#django_components.Component.Kwargs) class.
Otherwise, `slots` will be a plain dict. **Example:**
```python
from django_components import Component
class Table(Component):
def on_render_before(self, context: Context, template: Optional[Template]) -> None:
assert self.raw_kwargs["page"] == 123
assert self.raw_kwargs["per_page"] == 10
```
"""
slots: Any
"""
Slots passed to the component.
This is part of the [Render API](../../concepts/fundamentals/render_api).
`slots` has the same behavior as the `slots` argument of
[`Component.get_template_data()`](../api/#django_components.Component.get_template_data):
- If you defined the [`Component.Slots`](../api/#django_components.Component.Slots) class,
then the `slots` property will return an instance of that class.
- Otherwise, `slots` will be a plain dict.
**Example:** **Example:**
@ -2588,6 +2644,28 @@ class Component(metaclass=ComponentMeta):
``` ```
""" """
raw_slots: Dict[str, Slot]
"""
Slots passed to the component.
This is part of the [Render API](../../concepts/fundamentals/render_api).
Unlike [`Component.slots`](../api/#django_components.Component.slots), this attribute
is not typed and will remain as plain dict even if you define the
[`Component.Slots`](../api/#django_components.Component.Slots) class.
**Example:**
```python
from django_components import Component
class Table(Component):
def on_render_before(self, context: Context, template: Optional[Template]) -> None:
assert self.raw_slots["header"] == "MY_HEADER"
assert self.raw_slots["footer"] == "FOOTER: " + ctx.data["user_id"]
```
"""
context: Context context: Context
""" """
The `context` argument as passed to The `context` argument as passed to
@ -2609,6 +2687,39 @@ class Component(metaclass=ComponentMeta):
and data MUST be passed via component's args and kwargs. and data MUST be passed via component's args and kwargs.
""" """
deps_strategy: DependenciesStrategy
"""
Dependencies strategy defines how to handle JS and CSS dependencies of this and child components.
Read more about
[Dependencies rendering](../../concepts/fundamentals/rendering_components#dependencies-rendering).
This is part of the [Render API](../../concepts/fundamentals/render_api).
There are six strategies:
- [`"document"`](../../concepts/advanced/rendering_js_css#document) (default)
- Smartly inserts JS / CSS into placeholders or into `<head>` and `<body>` tags.
- Inserts extra script to allow `fragment` types to work.
- Assumes the HTML will be rendered in a JS-enabled browser.
- [`"fragment"`](../../concepts/advanced/rendering_js_css#fragment)
- A lightweight HTML fragment to be inserted into a document with AJAX.
- No JS / CSS included.
- [`"simple"`](../../concepts/advanced/rendering_js_css#simple)
- Smartly insert JS / CSS into placeholders or into `<head>` and `<body>` tags.
- No extra script loaded.
- [`"prepend"`](../../concepts/advanced/rendering_js_css#prepend)
- Insert JS / CSS before the rendered HTML.
- No extra script loaded.
- [`"append"`](../../concepts/advanced/rendering_js_css#append)
- Insert JS / CSS after the rendered HTML.
- No extra script loaded.
- [`"ignore"`](../../concepts/advanced/rendering_js_css#ignore)
- HTML is left as-is. You can still process it with a different strategy later with
[`render_dependencies()`](../api/#django_components.render_dependencies).
- Used for inserting rendered HTML into other components.
"""
outer_context: Optional[Context] outer_context: Optional[Context]
""" """
When a component is rendered with the [`{% component %}`](../template_tags#component) tag, When a component is rendered with the [`{% component %}`](../template_tags#component) tag,
@ -2826,7 +2937,7 @@ class Component(metaclass=ComponentMeta):
As the `{{ message }}` is taken from the "my_provide" provider. As the `{{ message }}` is taken from the "my_provide" provider.
""" """
return get_injected_context_var(self.name, self.input.context, key, default) return get_injected_context_var(self.name, self.context, key, default)
@classmethod @classmethod
def as_view(cls, **initkwargs: Any) -> ViewFn: def as_view(cls, **initkwargs: Any) -> ViewFn:

View file

@ -110,7 +110,7 @@ class DynamicComponent(Component):
) -> str: ) -> str:
# Make a copy of kwargs so we pass to the child only the kwargs that are # Make a copy of kwargs so we pass to the child only the kwargs that are
# actually used by the child component. # actually used by the child component.
cleared_kwargs = self.input.kwargs.copy() cleared_kwargs = self.raw_kwargs.copy()
registry: Optional[ComponentRegistry] = cleared_kwargs.pop("registry", None) registry: Optional[ComponentRegistry] = cleared_kwargs.pop("registry", None)
comp_name_or_class: Union[str, Type[Component]] = cleared_kwargs.pop("is", None) comp_name_or_class: Union[str, Type[Component]] = cleared_kwargs.pop("is", None)
@ -121,11 +121,11 @@ class DynamicComponent(Component):
comp_class = self._resolve_component(comp_name_or_class, registry) comp_class = self._resolve_component(comp_name_or_class, registry)
output = comp_class.render( output = comp_class.render(
context=self.input.context, context=self.context,
args=self.input.args, args=self.raw_args,
kwargs=cleared_kwargs, kwargs=cleared_kwargs,
slots=self.input.slots, slots=self.raw_slots,
deps_strategy=self.input.deps_strategy, deps_strategy=self.deps_strategy,
registered_name=self.registered_name, registered_name=self.registered_name,
outer_context=self.outer_context, outer_context=self.outer_context,
registry=self.registry, registry=self.registry,

View file

@ -688,7 +688,7 @@ class SlotNode(BaseNode):
outer_context = component_ctx.outer_context outer_context = component_ctx.outer_context
# Slot info # Slot info
slot_fills = component.input.slots slot_fills = component.raw_slots
slot_name = name slot_name = name
is_default = self.flags[SLOT_DEFAULT_FLAG] is_default = self.flags[SLOT_DEFAULT_FLAG]
is_required = self.flags[SLOT_REQUIRED_FLAG] is_required = self.flags[SLOT_REQUIRED_FLAG]

View file

@ -2115,7 +2115,7 @@ class HeroIcon(Component):
viewbox: Optional[str] = None, viewbox: Optional[str] = None,
attrs: Optional[Dict] = None, attrs: Optional[Dict] = None,
) -> Dict: ) -> Dict:
kwargs = IconDefaults(**self.input.kwargs) kwargs = IconDefaults(**self.kwargs)
if kwargs.variant not in ["outline", "solid"]: if kwargs.variant not in ["outline", "solid"]:
raise ValueError(f"Invalid variant: {kwargs.variant}. Must be either 'outline' or 'solid'") raise ValueError(f"Invalid variant: {kwargs.variant}. Must be either 'outline' or 'solid'")

View file

@ -252,6 +252,47 @@ class TestComponentLegacyApi:
template_2 = _get_component_template(comp) template_2 = _get_component_template(comp)
assert template_2._test_id == "123" # type: ignore[union-attr] assert template_2._test_id == "123" # type: ignore[union-attr]
# TODO_v1 - Remove
def test_input(self):
class TestComponent(Component):
template: types.django_html = """
{% load component_tags %}
Variable: <strong>{{ variable }}</strong>
{% slot 'my_slot' / %}
"""
def get_template_data(self, args, kwargs, slots, context):
assert self.input.args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1}
assert isinstance(self.input.context, Context)
assert list(self.input.slots.keys()) == ["my_slot"]
my_slot = self.input.slots["my_slot"]
assert my_slot() == "MY_SLOT"
return {
"variable": kwargs["variable"],
}
def on_render_before(self, context, template):
assert self.input.args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1}
assert isinstance(self.input.context, Context)
assert list(self.input.slots.keys()) == ["my_slot"]
my_slot = self.input.slots["my_slot"]
assert my_slot() == "MY_SLOT"
rendered = TestComponent.render(
kwargs={"variable": "test", "another": 1},
args=(123, "str"),
slots={"my_slot": "MY_SLOT"},
)
assertHTMLEqual(
rendered,
"""
Variable: <strong data-djc-id-ca1bc3e>test</strong> MY_SLOT
""",
)
@djc_test @djc_test
class TestComponent: class TestComponent:
@ -477,7 +518,7 @@ class TestComponentRenderAPI:
rendered = SimpleComponent.render() rendered = SimpleComponent.render()
assert rendered == "render_id: ca1bc3e" assert rendered == "render_id: ca1bc3e"
def test_input(self): def test_raw_input(self):
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -486,11 +527,11 @@ class TestComponentRenderAPI:
""" """
def get_template_data(self, args, kwargs, slots, context): def get_template_data(self, args, kwargs, slots, context):
assert self.input.args == [123, "str"] assert self.raw_args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1} assert self.raw_kwargs == {"variable": "test", "another": 1}
assert isinstance(self.input.context, Context) assert isinstance(self.context, Context)
assert list(self.input.slots.keys()) == ["my_slot"] assert list(self.raw_slots.keys()) == ["my_slot"]
my_slot = self.input.slots["my_slot"] my_slot = self.raw_slots["my_slot"]
assert my_slot() == "MY_SLOT" assert my_slot() == "MY_SLOT"
return { return {
@ -498,11 +539,11 @@ class TestComponentRenderAPI:
} }
def on_render_before(self, context, template): def on_render_before(self, context, template):
assert self.input.args == [123, "str"] assert self.raw_args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1} assert self.raw_kwargs == {"variable": "test", "another": 1}
assert isinstance(self.input.context, Context) assert isinstance(self.context, Context)
assert list(self.input.slots.keys()) == ["my_slot"] assert list(self.raw_slots.keys()) == ["my_slot"]
my_slot = self.input.slots["my_slot"] my_slot = self.raw_slots["my_slot"]
assert my_slot() == "MY_SLOT" assert my_slot() == "MY_SLOT"
rendered = TestComponent.render( rendered = TestComponent.render(

View file

@ -34,7 +34,7 @@ class TestComponentDefaults:
"extra": "extra", # Default because `None` was given "extra": "extra", # Default because `None` was given
"fn": self.Defaults.fn, # Default because missing "fn": self.Defaults.fn, # Default because missing
} }
assert self.input.kwargs == { assert self.raw_kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
"another": 1, # Default because missing "another": 1, # Default because missing
"extra": "extra", # Default because `None` was given "extra": "extra", # Default because `None` was given
@ -46,11 +46,11 @@ class TestComponentDefaults:
assert [*slots.keys()] == ["my_slot"] assert [*slots.keys()] == ["my_slot"]
assert slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type] assert slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type]
assert self.input.args == [123] assert self.raw_args == [123]
assert [*self.input.slots.keys()] == ["my_slot"] assert [*self.raw_slots.keys()] == ["my_slot"]
assert self.input.slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type] assert self.raw_slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type]
assert isinstance(self.input.context, Context) assert isinstance(self.context, Context)
return { return {
"variable": kwargs["variable"], "variable": kwargs["variable"],
@ -82,11 +82,11 @@ class TestComponentDefaults:
"variable": "test", # User-given "variable": "test", # User-given
"fn": "fn_as_factory", # Default because missing "fn": "fn_as_factory", # Default because missing
} }
assert self.input.kwargs == { assert self.raw_kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
"fn": "fn_as_factory", # Default because missing "fn": "fn_as_factory", # Default because missing
} }
assert isinstance(self.input.context, Context) assert isinstance(self.context, Context)
return { return {
"variable": kwargs["variable"], "variable": kwargs["variable"],
@ -117,12 +117,12 @@ class TestComponentDefaults:
# NOTE: NOT a factory, because it was set as `field(default=...)` # NOTE: NOT a factory, because it was set as `field(default=...)`
"fn": self.Defaults.fn.default, # type: ignore[attr-defined] "fn": self.Defaults.fn.default, # type: ignore[attr-defined]
} }
assert self.input.kwargs == { assert self.raw_kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
# NOTE: NOT a factory, because it was set as `field(default=...)` # NOTE: NOT a factory, because it was set as `field(default=...)`
"fn": self.Defaults.fn.default, # type: ignore[attr-defined] "fn": self.Defaults.fn.default, # type: ignore[attr-defined]
} }
assert isinstance(self.input.context, Context) assert isinstance(self.context, Context)
return { return {
"variable": kwargs["variable"], "variable": kwargs["variable"],
@ -153,12 +153,12 @@ class TestComponentDefaults:
# NOTE: IS a factory, because it was set as `field(default_factory=...)` # NOTE: IS a factory, because it was set as `field(default_factory=...)`
"fn": "fn_as_factory", # Default because missing "fn": "fn_as_factory", # Default because missing
} }
assert self.input.kwargs == { assert self.raw_kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
# NOTE: IS a factory, because it was set as `field(default_factory=...)` # NOTE: IS a factory, because it was set as `field(default_factory=...)`
"fn": "fn_as_factory", # Default because missing "fn": "fn_as_factory", # Default because missing
} }
assert isinstance(self.input.context, Context) assert isinstance(self.context, Context)
return { return {
"variable": kwargs["variable"], "variable": kwargs["variable"],

View file

@ -729,7 +729,7 @@ class TestSlot:
template = "CAPTURER" template = "CAPTURER"
def get_template_data(self, args, kwargs, slots, context): def get_template_data(self, args, kwargs, slots, context):
seen_slots.append(self.input.slots["my_slot"]) seen_slots.append(self.slots["my_slot"])
MyTopLevelComponent.render() MyTopLevelComponent.render()

View file

@ -1066,7 +1066,7 @@ class TestPassthroughSlots:
class OuterComp(Component): class OuterComp(Component):
def get_template_data(self, args, kwargs, slots, context): def get_template_data(self, args, kwargs, slots, context):
return { return {
"slots": self.input.slots, "slots": self.slots,
} }
template: types.django_html = """ template: types.django_html = """
@ -1117,7 +1117,7 @@ class TestPassthroughSlots:
class OuterComp(Component): class OuterComp(Component):
def get_template_data(self, args, kwargs, slots, context): def get_template_data(self, args, kwargs, slots, context):
return { return {
"slots": self.input.slots, "slots": self.slots,
} }
template: types.django_html = """ template: types.django_html = """