-
-
Notifications
You must be signed in to change notification settings - Fork 364
[Twig] Embedded components #317
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
Some context on what I would like the default slot feature to look like (using the first example above): {# "embed" in another template: #}
{% component 'alert' with {message: 'embedded message', class: 'extra-class', role: 'alert'} %}
{# anything not wrapped in a block is considered the default slot #}
{{ parent() }} {# call parent "slot" #}
Custom stuff...
{{ this.message }} {# access component properties/methods as normal #}
{{ computed.someMethod }} {# all component features available #}
{% block title %}
{# can still override "named slots" #}
Custom Title
{% endblock %}
{% endcomponent %} I certainly don't think not having this feature is a showstopper - just a nice DX improvement. |
I'm not sure about the mixture of default slot and other blocks. But if only the default block is needed, I would also prefer the notation without
100% agree! |
Thanks for your work kbond ! I tried it out, and it's working great ! I was thinking about the default slots with parent(). What do you think about doing it in the other way. We can use a children function in the component: {# templates/components/alert.html.twig #}
<div{{ attributes.defaults({class: 'alert alert-'~type}) }}>
{{ children() }}
{% block default %}<strong>{{ type }}</strong> {{ message }}{% endblock %}
</div>`
{# "embed" in another template: #}
{% component 'alert' with {message: 'embedded message', class: 'extra-class', type: 'alert'} %}
{# anything not wrapped in a block is considered the children slot #}
<p>My custom stuff...</p>
{% endcomponent %} This children function is only accessible inside a component. I think it can be much easier to read, and closer to front-end framework like react. 🤔 |
fyi: found an issue regarding similar functionality for original embed tag: twigphp/Twig#3572 |
I love this! The code already looks clean. Let's finish this.
I agree & also agree that NOT having this is NOT a show stopper. If we merge this, perhaps some brave curious person will come along and add this. My "hunch" is that it the implementation might involve "hacking in" a
Agree 👍. In theory, we could stash the raw Twig contents into a system cache, and re-render using that. A bit crazy, but I actually don't see any issue with it. But, later. Thanks! |
I believe this to be ready for a final review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Minor comments on the docs
5a98633
to
3692519
Compare
3692519
to
b114594
Compare
Thanks Kevin! |
… live components (sneakyvv) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] [LiveComponent] Add support for embedded live components | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | | License | MIT ## Context Using embedded components was introduced in #317, but support for Live embedded components was not added. The issue is that on a re-render of a Live component you lose the blocks defined within an embedded component. This PR solves that issue. ## Example To explain the solution, take this example: ```twig {# someTemplate.html.twig #} {% component Foo %} {% block content %} Override content {% endblock %} {% endcomponent %} ``` ```twig {# Foo.html.twig #} <div {{ attributes }}> {% block content %} Default content {% endblock %} </div> ``` Of course, Foo is a Live component. This obviously also works with the new Twig syntax. ## Background 1. Each `{% component %}` tag is compiled by `ComponentNode`. It adds an embedded Template to the Template class compiled for `someTemplate.html.twig`. This is a second class inside the same php file, with a suffix in the form of `___%d`. That number at the end is normally random, and is called the embedded template index. 2. `ComponentNode` would generate Template code which fetches the `embeddedContext` from the `ComponentRenderer` and passed that to the `loadTemplate('Foo.html.twig', $index)->display()` 3. When a component is re-rendered (via an action callback) it uses the template of the component `(Foo.html.twig`), which does not have the original block content, because that's part of the host Template (`someTemplate.html.twig`). ## Solution We only need to use the embedded Template instead of the component Template to re-render a component. To make this happen, we need to: 1. Use a deterministic index for an embedded template during compilation. 2. Store info on the rendered component's HTML (via the attributes) about the host template and the embedded template's index. 3. Load the embedded Template during re-render using the info passed along with the other attributes/props. ## Remaining 1. I use `loadTemplate` now in the `ComponentRender`, which is marked as internal in the Twig package. Can we ignore that within this package (as both are "Symfony")? 2. The `PreRenderEvent::EMBEDDED` constant and the `isEmbedded` function were introduced to block live embedded components. Should this PR remove those as well? ### Tasks - [ ] Remove `isEmbedded`? - [ ] Remove `PreRenderEvent::EMBEDDED`? - [ ] Add CHANGELOG Commits ------- d9dd3fc [TwigComponent] [LiveComponent] Add support for embedded live components
… live components (sneakyvv) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] [LiveComponent] Add support for embedded live components | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | | License | MIT ## Context Using embedded components was introduced in symfony/ux#317, but support for Live embedded components was not added. The issue is that on a re-render of a Live component you lose the blocks defined within an embedded component. This PR solves that issue. ## Example To explain the solution, take this example: ```twig {# someTemplate.html.twig #} {% component Foo %} {% block content %} Override content {% endblock %} {% endcomponent %} ``` ```twig {# Foo.html.twig #} <div {{ attributes }}> {% block content %} Default content {% endblock %} </div> ``` Of course, Foo is a Live component. This obviously also works with the new Twig syntax. ## Background 1. Each `{% component %}` tag is compiled by `ComponentNode`. It adds an embedded Template to the Template class compiled for `someTemplate.html.twig`. This is a second class inside the same php file, with a suffix in the form of `___%d`. That number at the end is normally random, and is called the embedded template index. 2. `ComponentNode` would generate Template code which fetches the `embeddedContext` from the `ComponentRenderer` and passed that to the `loadTemplate('Foo.html.twig', $index)->display()` 3. When a component is re-rendered (via an action callback) it uses the template of the component `(Foo.html.twig`), which does not have the original block content, because that's part of the host Template (`someTemplate.html.twig`). ## Solution We only need to use the embedded Template instead of the component Template to re-render a component. To make this happen, we need to: 1. Use a deterministic index for an embedded template during compilation. 2. Store info on the rendered component's HTML (via the attributes) about the host template and the embedded template's index. 3. Load the embedded Template during re-render using the info passed along with the other attributes/props. ## Remaining 1. I use `loadTemplate` now in the `ComponentRender`, which is marked as internal in the Twig package. Can we ignore that within this package (as both are "Symfony")? 2. The `PreRenderEvent::EMBEDDED` constant and the `isEmbedded` function were introduced to block live embedded components. Should this PR remove those as well? ### Tasks - [ ] Remove `isEmbedded`? - [ ] Remove `PreRenderEvent::EMBEDDED`? - [ ] Add CHANGELOG Commits ------- d9dd3fc4 [TwigComponent] [LiveComponent] Add support for embedded live components
… live components (sneakyvv) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] [LiveComponent] Add support for embedded live components | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | | License | MIT ## Context Using embedded components was introduced in symfony/ux#317, but support for Live embedded components was not added. The issue is that on a re-render of a Live component you lose the blocks defined within an embedded component. This PR solves that issue. ## Example To explain the solution, take this example: ```twig {# someTemplate.html.twig #} {% component Foo %} {% block content %} Override content {% endblock %} {% endcomponent %} ``` ```twig {# Foo.html.twig #} <div {{ attributes }}> {% block content %} Default content {% endblock %} </div> ``` Of course, Foo is a Live component. This obviously also works with the new Twig syntax. ## Background 1. Each `{% component %}` tag is compiled by `ComponentNode`. It adds an embedded Template to the Template class compiled for `someTemplate.html.twig`. This is a second class inside the same php file, with a suffix in the form of `___%d`. That number at the end is normally random, and is called the embedded template index. 2. `ComponentNode` would generate Template code which fetches the `embeddedContext` from the `ComponentRenderer` and passed that to the `loadTemplate('Foo.html.twig', $index)->display()` 3. When a component is re-rendered (via an action callback) it uses the template of the component `(Foo.html.twig`), which does not have the original block content, because that's part of the host Template (`someTemplate.html.twig`). ## Solution We only need to use the embedded Template instead of the component Template to re-render a component. To make this happen, we need to: 1. Use a deterministic index for an embedded template during compilation. 2. Store info on the rendered component's HTML (via the attributes) about the host template and the embedded template's index. 3. Load the embedded Template during re-render using the info passed along with the other attributes/props. ## Remaining 1. I use `loadTemplate` now in the `ComponentRender`, which is marked as internal in the Twig package. Can we ignore that within this package (as both are "Symfony")? 2. The `PreRenderEvent::EMBEDDED` constant and the `isEmbedded` function were introduced to block live embedded components. Should this PR remove those as well? ### Tasks - [ ] Remove `isEmbedded`? - [ ] Remove `PreRenderEvent::EMBEDDED`? - [ ] Add CHANGELOG Commits ------- d9dd3fc4 [TwigComponent] [LiveComponent] Add support for embedded live components
This adds a
{% component name %}
tag to allow rendering components and overriding their slots (by using twig's native block system).Example:
More Advanced Example:
Some notes:
embed
tagPreRender
event is still dispatched like normal but because of the nature of how this feature works, modifying the template via a listener is not supported.TODO: