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 %}
|
||||
</div>
|
||||
<div class="body">
|
||||
{% slot "body" %}Calendar body{% endslot %}
|
||||
{% slot "body" %}Today's date is <span>{{ date }}</span>{% endslot %}
|
||||
</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:
|
||||
|
||||
```htmldjango
|
||||
{% component_block "calendar" %}
|
||||
{% slot "body" %}Today's date is <span>{{ date }}</span>{% endslot %}
|
||||
{% component_block "calendar" date="2020-06-06" %}
|
||||
{% slot "body" %}Can you belive it's already <span>{{ date }}</span>??{% endslot %}
|
||||
{% endcomponent_block %}
|
||||
```
|
||||
|
||||
|
@ -235,7 +235,7 @@ Since the header block is unspecified, it's taken from the base template. If you
|
|||
Calendar header
|
||||
</div>
|
||||
<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>
|
||||
|
||||
|
@ -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.
|
||||
|
||||
# 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):
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
|
||||
# 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.
|
||||
|
|
|
@ -114,8 +114,7 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
def render(self, context):
|
||||
template_name = self.template(context)
|
||||
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: active_slots}):
|
||||
with context.update({ACTIVE_SLOT_CONTEXT_KEY: self.slots}):
|
||||
return instance_template.render(context)
|
||||
|
||||
class Media:
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import inspect
|
||||
from collections import defaultdict
|
||||
|
||||
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.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
|
||||
|
||||
register = template.Library()
|
||||
|
|
2
setup.py
2
setup.py
|
@ -3,7 +3,7 @@ import os
|
|||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
VERSION = '0.14'
|
||||
VERSION = '0.15'
|
||||
|
||||
setup(
|
||||
name="django_components",
|
||||
|
|
|
@ -215,3 +215,45 @@ class ComponentIsolationTests(SimpleTestCase):
|
|||
</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_components import component
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from django_components import component
|
||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue