mirror of
https://github.com/django-components/django-components.git
synced 2025-08-09 16:57:59 +00:00
fix: do NOT render component tags when parsing {% component %} body for fills (#778)
* refactor: do NOT render component tags when parsing {% component %} body for fills * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor: fix typo --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
4d5fecf3ee
commit
f5fd3c02cd
5 changed files with 80 additions and 6 deletions
|
@ -1311,7 +1311,7 @@ tags:
|
|||
{% endcomponent %}
|
||||
```
|
||||
|
||||
You can also use `{% for %}`, `{% with %}`, or other tags (even `{% include %}`)
|
||||
You can also use `{% for %}`, `{% with %}`, or other non-component tags (even `{% include %}`)
|
||||
to construct the `{% fill %}` tags, **as long as these other tags do not leave any text behind!**
|
||||
|
||||
```django
|
||||
|
|
|
@ -57,6 +57,7 @@ from django_components.slots import (
|
|||
SlotName,
|
||||
SlotRef,
|
||||
SlotResult,
|
||||
_is_extracting_fill,
|
||||
_nodelist_to_slot_render_func,
|
||||
resolve_fills,
|
||||
)
|
||||
|
@ -894,6 +895,11 @@ class ComponentNode(BaseNode):
|
|||
def render(self, context: Context) -> str:
|
||||
trace_msg("RENDR", "COMP", self.name, self.node_id)
|
||||
|
||||
# Do not render nested `{% component %}` tags in other `{% component %}` tags
|
||||
# at the stage when we are determining if the latter has named fills or not.
|
||||
if _is_extracting_fill(context):
|
||||
return ""
|
||||
|
||||
component_cls: Type[Component] = self.registry.get(self.name)
|
||||
|
||||
# Resolve FilterExpressions and Variables that were passed as args to the
|
||||
|
|
|
@ -399,7 +399,7 @@ class FillNode(BaseNode):
|
|||
self.trace_id = trace_id
|
||||
|
||||
def render(self, context: Context) -> str:
|
||||
if self._is_extracting_fill(context):
|
||||
if _is_extracting_fill(context):
|
||||
self._extract_fill(context)
|
||||
return ""
|
||||
|
||||
|
@ -459,9 +459,6 @@ class FillNode(BaseNode):
|
|||
|
||||
return value
|
||||
|
||||
def _is_extracting_fill(self, context: Context) -> bool:
|
||||
return context.get(FILL_GEN_CONTEXT_KEY, None) is not None
|
||||
|
||||
def _extract_fill(self, context: Context) -> None:
|
||||
# `FILL_GEN_CONTEXT_KEY` is only ever set when we are rendering content between the
|
||||
# `{% component %}...{% endcomponent %}` tags. This is done in order to collect all fill tags.
|
||||
|
@ -775,3 +772,7 @@ def _nodelist_to_slot_render_func(
|
|||
return rendered
|
||||
|
||||
return Slot(content_func=cast(SlotFunc, render_func))
|
||||
|
||||
|
||||
def _is_extracting_fill(context: Context) -> bool:
|
||||
return context.get(FILL_GEN_CONTEXT_KEY, None) is not None
|
||||
|
|
|
@ -77,7 +77,7 @@ tags:
|
|||
{% endcomponent %}
|
||||
```
|
||||
|
||||
You can also use `{% for %}`, `{% with %}`, or other tags (even `{% include %}`)
|
||||
You can also use `{% for %}`, `{% with %}`, or other non-component tags (even `{% include %}`)
|
||||
to construct the `{% fill %}` tags, **as long as these other tags do not leave any text behind!**
|
||||
|
||||
```django
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from typing import Any
|
||||
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
||||
from django_components import Component, register, types
|
||||
|
@ -737,3 +739,68 @@ class InjectTest(BaseTestCase):
|
|||
comp = InjectComponent("")
|
||||
with self.assertRaises(RuntimeError):
|
||||
comp.inject("abc", "def")
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_in_fill(self):
|
||||
@register("injectee")
|
||||
class Injectee(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div> injected: {{ data|safe }} </div>
|
||||
<main>
|
||||
{% slot "content" default / %}
|
||||
</main>
|
||||
"""
|
||||
|
||||
def get_context_data(self):
|
||||
data = self.inject("my_provide")
|
||||
return {"data": data}
|
||||
|
||||
@register("provider")
|
||||
class Provider(Component):
|
||||
def get_context_data(self, data: Any) -> Any:
|
||||
return {"data": data}
|
||||
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% provide "my_provide" key="hi" data=data %}
|
||||
{% slot "content" default / %}
|
||||
{% endprovide %}
|
||||
"""
|
||||
|
||||
@register("parent")
|
||||
class Parent(Component):
|
||||
def get_context_data(self, data: Any) -> Any:
|
||||
return {"data": data}
|
||||
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "provider" data=data %}
|
||||
{% component "injectee" %}
|
||||
{% slot "content" default / %}
|
||||
{% endcomponent %}
|
||||
{% endcomponent %}
|
||||
"""
|
||||
|
||||
@register("root")
|
||||
class Root(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "parent" data=123 %}
|
||||
{% fill "content" %}
|
||||
456
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
"""
|
||||
|
||||
rendered = Root.render()
|
||||
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
"""
|
||||
<div>
|
||||
injected: DepInject(key='hi', data=123)
|
||||
</div>
|
||||
<main>456</main>
|
||||
""",
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue