Closed JuroOravec closed 3 weeks ago
I'll write the behavior out, since it also took me some time to wrap my head around it:
First let's clarify how include
and extends
tags work inside components. So when component template includes include
or extends
tags, it's as if the "included" template was inlined. So if the "included" template contains slot
tags, then the component uses those slots.
So if I have a template abc.html
:
<div>
hello
{% slot "body" %}{% endslot %}
</div>
And components that make use of abc.html
via include
or extends
:
@component.register("my_comp_extends")
class MyCompWithExtends(component.Component):
template = """{% extends "abc.html" %}"""
@component.register("my_comp_include")
class MyCompWithInclude(component.Component):
template = """{% include "abc.html" %}"""
Then I can set slot fill for the slot imported via include/extends
:
{% component "my_comp_extends" %}
{% fill "body" %}
123
{% endfill %}
{% endcomponent %}
And it will render:
<div>
hello
123
</div>
Slot and block
So if I have a template abc.html
like so:
<div>
hello
{% block inner %}
1
{% slot "body" %}
2
{% endslot %}
{% endblock %}
</div>
and component my_comp
:
@component.register("my_comp")
class MyComp(component.Component):
template_name = "abc.html"
Then:
Since the block
wasn't overriden, we can use the body
slot:
{% component "my_comp" %}
{% fill "body" %}
XYZ
{% endfill %}
{% endcomponent %}
And we get:
<div>
hello
1
XYZ
</div>
blocks
CANNOT be overriden through the component
tag, so something like this:
{% component "my_comp" %}
{% fill "body" %}
XYZ
{% endfill %}
{% endcomponent %}
{% block "inner" %}
456
{% endblock %}
Will still render the component content just the same:
<div>
hello
1
XYZ
</div>
I CAN override the block
tags of abc.html
if my component template uses extends
. In that case, just as you would expect, the block inner
inside abc.html
will render OVERRIDEN
:
@component.register("my_comp")
class MyComp(component.Component):
template_name = """
{% extends "abc.html" %}
{% block inner %}
OVERRIDEN
{% endblock %}
"""
This is where it gets interesting (but still intuitive). I can insert even new slots
inside these "overriding" blocks:
@component.register("my_comp")
class MyComp(component.Component):
template_name = """
{% extends "abc.html" %}
{% load component_tags %}
{% block "inner" %}
OVERRIDEN
{% slot "new_slot" %}
hello
{% endslot %}
{% endblock %}
"""
And I can then pass fill for this new_slot
when rendering the component:
{% component "my_comp" %}
{% fill "new_slot" %}
XYZ
{% endfill %}
{% endcomponent %}
NOTE: Currently I can supply fills for both new_slot
and body
slots, and I will not get an error for an invalid/unknown slot name. But since body
slot is not rendered, it just won't do anything. So this renders the same as above:
{% component "my_comp" %}
{% fill "new_slot" %}
XYZ
{% endfill %}
{% fill "body" %}
www
{% endfill %}
{% endcomponent %}
I've included the explainer in docs/slots_and_blocks.md
I just wanted to say that this is great work again. Thank you!
Added tests to explore behavior of components with "loader tags" (
extends
,include
,block
). Tested out all combinations I could think of:include
extends
block
slot
Surprisingly, it all mostly worked as expected 😄, and I had to update only the traversal logic, so it could extract nodes from
extends
/include
tags.Closes https://github.com/EmilStenstrom/django-components/issues/134