mirror of
https://github.com/django-components/django-components.git
synced 2025-09-26 15:39:08 +00:00
* Add regression test for recursion bug, #68 * Only allow slots to access slot nodelists provided to their immediate parent component to prevent infinite recursions. * Fix import ordering bug in test * Add slot.super to docs Remove unused imports * Bump version Co-authored-by: rbeard0330 <@dul2k3BKW6m>
This commit is contained in:
parent
7f4661922a
commit
e3c9ac76ce
6 changed files with 74 additions and 13 deletions
32
README.md
32
README.md
|
@ -214,7 +214,7 @@ Components support something called slots. They work a lot like Django blocks, b
|
||||||
{% slot "header" %}Calendar header{% endslot %}
|
{% slot "header" %}Calendar header{% endslot %}
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
{% slot "body" %}Calendar body{% endslot %}
|
{% slot "body" %}Today's date is <span>{{ date }}</span>{% endslot %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
@ -222,8 +222,8 @@ Components support something called slots. They work a lot like Django blocks, b
|
||||||
When using the component, you specify what slots you want to fill and where you want to use the defaults from the template. It looks like this:
|
When using the component, you specify what slots you want to fill and where you want to use the defaults from the template. It looks like this:
|
||||||
|
|
||||||
```htmldjango
|
```htmldjango
|
||||||
{% component_block "calendar" %}
|
{% component_block "calendar" date="2020-06-06" %}
|
||||||
{% slot "body" %}Today's date is <span>{{ date }}</span>{% endslot %}
|
{% slot "body" %}Can you belive it's already <span>{{ date }}</span>??{% endslot %}
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ Since the header block is unspecified, it's taken from the base template. If you
|
||||||
Calendar header
|
Calendar header
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
Today's date is <span>2020-06-06</span>
|
Can you believe it's already <span>2020-06-06</span>??
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -243,7 +243,28 @@ Since the header block is unspecified, it's taken from the base template. If you
|
||||||
|
|
||||||
As you can see, component slots lets you write reusable containers, that you fill out when you use a component. This makes for highly reusable components, that can be used in different circumstances.
|
As you can see, component slots lets you write reusable containers, that you fill out when you use a component. This makes for highly reusable components, that can be used in different circumstances.
|
||||||
|
|
||||||
# Component context
|
If you want to include a slot's default content while adding additional content, you can call `slot.super` to insert the base content, which works similarly to `block.super`.
|
||||||
|
|
||||||
|
```htmldjango
|
||||||
|
{% component_block "calendar" date="2020-06-06" %}
|
||||||
|
{% slot "body" %}{ slot.super }. Have a great day!{% endslot %}
|
||||||
|
{% endcomponent_block %}
|
||||||
|
```
|
||||||
|
|
||||||
|
Produces:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="calendar-component">
|
||||||
|
<div class="header">
|
||||||
|
Calendar header
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
Today's date is <span>2020-06-06</span>. Have a great day!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
# Component context and scope
|
||||||
|
|
||||||
By default, components can access context variables from the parent template, just like templates that are included with the `{% include %}` tag. Just like with `{% include %}`, if you don't want the component template to have access to the parent context, add `only` to the end of the `{% component %}` (or `{% component_block %}` tag):
|
By default, components can access context variables from the parent template, just like templates that are included with the `{% include %}` tag. Just like with `{% include %}`, if you don't want the component template to have access to the parent context, add `only` to the end of the `{% component %}` (or `{% component_block %}` tag):
|
||||||
|
|
||||||
|
@ -255,6 +276,7 @@ NOTE: `{% csrf_token %}` tags need access to the top-level context, and they wil
|
||||||
|
|
||||||
Components can also access the outer context in their context methods by accessing the property `outer_context`.
|
Components can also access the outer context in their context methods by accessing the property `outer_context`.
|
||||||
|
|
||||||
|
|
||||||
# Available settings
|
# Available settings
|
||||||
|
|
||||||
All library settings are handled from a global COMPONENTS variable that is read from settings.py. By default you don't need it set, there are resonable defaults.
|
All library settings are handled from a global COMPONENTS variable that is read from settings.py. By default you don't need it set, there are resonable defaults.
|
||||||
|
|
|
@ -114,8 +114,7 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
template_name = self.template(context)
|
template_name = self.template(context)
|
||||||
instance_template = self.get_processed_template(template_name)
|
instance_template = self.get_processed_template(template_name)
|
||||||
active_slots = {**context.get(ACTIVE_SLOT_CONTEXT_KEY, {}), **self.slots}
|
with context.update({ACTIVE_SLOT_CONTEXT_KEY: self.slots}):
|
||||||
with context.update({ACTIVE_SLOT_CONTEXT_KEY: active_slots}):
|
|
||||||
return instance_template.render(context)
|
return instance_template.render(context)
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import inspect
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
@ -7,7 +6,7 @@ from django.template.base import Node, NodeList, TemplateSyntaxError, TokenType
|
||||||
from django.template.library import parse_bits
|
from django.template.library import parse_bits
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from django_components.component import ACTIVE_SLOT_CONTEXT_KEY, Component, registry
|
from django_components.component import ACTIVE_SLOT_CONTEXT_KEY, registry
|
||||||
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -3,7 +3,7 @@ import os
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
VERSION = '0.14'
|
VERSION = '0.15'
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="django_components",
|
name="django_components",
|
||||||
|
|
|
@ -215,3 +215,45 @@ class ComponentIsolationTests(SimpleTestCase):
|
||||||
</custom-template>
|
</custom-template>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RecursiveSlotNameTest(SimpleTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
@component.register('outer')
|
||||||
|
class OuterComponent(component.Component):
|
||||||
|
def template(self, context):
|
||||||
|
return "slotted_template.html"
|
||||||
|
|
||||||
|
@component.register('inner')
|
||||||
|
class InnerComponent(component.Component):
|
||||||
|
def template(self, context):
|
||||||
|
return "slotted_template.html"
|
||||||
|
|
||||||
|
def test_no_infinite_recursion_when_slot_name_is_reused(self):
|
||||||
|
template = Template(
|
||||||
|
"""
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component_block "outer" %}
|
||||||
|
{% slot "header" %}
|
||||||
|
{% component_block "inner" %}{% endcomponent_block %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endcomponent_block %}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertHTMLEqual(
|
||||||
|
template.render(Context({})),
|
||||||
|
"""
|
||||||
|
<custom-template>
|
||||||
|
<header>
|
||||||
|
<custom-template>
|
||||||
|
<header>Default header</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
|
@ -2,9 +2,8 @@ from textwrap import dedent
|
||||||
|
|
||||||
from django.template import Context, Template, TemplateSyntaxError
|
from django.template import Context, Template, TemplateSyntaxError
|
||||||
|
|
||||||
from django_components import component
|
|
||||||
|
|
||||||
from .django_test_setup import * # NOQA
|
from .django_test_setup import * # NOQA
|
||||||
|
from django_components import component
|
||||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue