-
-
Notifications
You must be signed in to change notification settings - Fork 364
[TwigComponent] Support passing blocks to nested embedded components #920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Note that I had some tests locally to address the ability to access the host/parent component from within an (nested) embedded component, but since that depends on #863 I didn't add them in this PR. |
|
For the record: as discussed on Slack, this PR currently can cause a recursive loop when a block (for example content) for a component has a(nother) component which also has a content block. The last component will be used in the actual content block for the first component. Then when the latter component is rendered it also will be using that same content definition from the first component. So it will render that first content block which renders the last's content blocks which refers again to the first and so on... I have a fix for this ready, and will update the PR soon after I've updated/extended the tests. |
I pushed an improved mechanism to pass blocks down to nested components. As said in my previous comment, just passing along blocks to the embedded templates will quickly end up in recursive loops, not using the correct blocks etc. Now, blocks are still passed along but their name is being randomized. The example from the description now becomes (I've adjusted it there as well): {# anywhere #}
{% set name = 'Fabien' %}
<twig:DivComponent>
Hello {{ name }}!
</twig:DivComponent> {# DivComponent.html.twig #}
<twig:GenericElement element="div" class="divComponent">
{{ block(outerBlocks.content) }}
</twig:GenericElement> {# GenericElement.html.twig #}
<{{ element }}{{ attributes }}>
{%- block content -%}{%- endblock -%}
</{{ element }}> I added a LOT more test cases, which deal with all kind of problems that would not be workable with just passing blocks down like:
See this test which lists all "rules" that blocks abide to within the context of embedded components. |
This is QUITE impressive and also complex. You've done an excellent job annotating the complex code, but still, this is a BEAST. However, as this is important, the feature is experimental and its well-covered in tests, I think we need to merge it and try to start using it in the wild. Thank you for your hard work on this @sneakyvv! |
…d components (sneakyvv) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Support passing blocks to nested embedded components | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | Fix #844 | License | MIT The fix for #844, and a better solution then a manual passthrough workaround, is literally just passing the blocks from the "host Template" to the embedded Template, ~~so that the blocks are merged with the embedded template's blocks, overriding them~~ but altering their name and using a special `outerBloocks` variable to map the block name to the new name (see symfony/ux#920 (comment)). This means that the example from symfony/ux#844 (comment) (tweaked a bit) ```twig {# anywhere #} {% set name = 'Fabien' %} <twig:DivComponent> Hello {{ name }}! </twig:DivComponent> ``` ```twig {# DivComponent.html.twig #} <twig:GenericElement element="div" class="divComponent"> {{ block(outerBlocks.content) }} </twig:GenericElement> ``` ```twig {# GenericElement.html.twig #} <{{ element }}{{ attributes }}> {%- block content -%}{%- endblock -%} </{{ element }}> ``` Results in ```twig <div class="divComponent">Hello Fabien!</div> ``` See the tests for more elaborate examples showing the access to context variables, multi-level overrides, etc. Commits ------- f4b064ca [TwigComponent] Support passing blocks to nested embedded components
…o embedded components (sneakyvv, weaverryan) This PR was merged into the 2.x branch. Discussion ---------- [TwigComponent] Add documentation about passing blocks to embedded components | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Tickets | Fix #... | License | MIT This adds documentation of the feature added in #920 Commits ------- 0bbb2b8 Fixing rst syntax acaa41a proofing ae5a458 Add simple example of outerBlocks without nested components to ease in 111ac3a Allow referring to outerblocks from non-nested components b0913d4 Fix typos 8e1d94b Moving away from "embedded component" wording in docs 305661c Added suggestions of PR feedback 1e85030 Add version info 444686c Add documentation about passing blocks to embedded components and their context
…endering embedded components (sneakyvv) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Add variable to refer outer scope when rendering embedded components | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | | License | MIT This PR's is an alternative approach to the problem stated in #863. Due to the changes proposed in #920, which deals with passing blocks down to nested embedded components, I realized that just being able to access the host (or parent) component isn't sufficient if blocks can be passed down multiple levels deep (as already mentioned in #863 (comment)). This PR adds a proxy object `Hierarchy` which allows this syntax: `Component.php` ```php final class Component { public function scream(int $screamHowHard = 1): string { return 'sf-ux rules' . str_repeat('!', $screamHowHard); } } ``` ```twig {# component.html.twig #} <twig:Foo :someProp="this.something(1)"> {{ someProp }} {{ outerScope.this.something(3) }} </twig:Foo> ``` ```twig {# Foo.html.twig #} <div>{% block content %}{% endblock %}</div> ``` Resulting in ```twig <div> sf-ux rules! sf-ux rules!!! </div> ``` Adding another layer below Foo, would ask for something like ```twig {# component.html.twig #} <twig:Foo> {{ outerScope.outerScope.this.something(3) }} </twig:Foo> ``` to still point to Foo's function. --- ### Questions There's also a `ComputedPropertiesProxy` object. Should we have a cached version of the `Hierarchy` proxy? --- ### NOTE This PR continues on the changes of #920, because having a hierarchy only makes sense if blocks can be passed down multiple times. So #920 may have to be merged first, but if needed, and the `outerScope.this` syntax is preferred over the syntax of #863, I could push my branch that starts of the main branch instead. Edited: using outerScope variable now instead Hierarchy proxy Commits ------- e618111 [TwigComponent] Add variable to refer outer scope when rendering embedded components
The fix for #844, and a better solution then a manual passthrough workaround, is literally just passing the blocks from the "host Template" to the embedded Template,
so that the blocks are merged with the embedded template's blocks, overriding thembut altering their name and using a specialouterBloocks
variable to map the block name to the new name (see #920 (comment)).This means that the example from #844 (comment) (tweaked a bit)
Results in
See the tests for more elaborate examples showing the access to context variables, multi-level overrides, etc.