-
-
Notifications
You must be signed in to change notification settings - Fork 329
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
[TwigComponent] Impossible to declare higher level component content block #844
Comments
Humm but it can be also that |
So I should use |
Works only this way: {# A.html.twig #}
<{{ element }} class="{{ classes }}"{{ attributes }}>
{%- block content -%}{%- endblock -%}
</{{ element }}> {# B.html.twig #}
{% set passthrough %}{% block content %}{% endblock %}{% endset %}
<twig:A>
{{ passthrough }}
</twig:A> |
I've created a more realistic example to help me not get confused :). {# anywhere #}
<twig:DivComponent>
Hello!
</twig:DivComponent>
{# GenericElement.html.twig #}
<{{ element }} class="{{ classes }}"{{ attributes }}>
{%- block content -%}{%- endblock -%}
</{{ element }}> Is that accurate? @WebMamba In my
If I'm correct, before opening the default block, we need to check to make sure that the content doesn't begin with |
Also found another problem. For form I override {% block button_widget %}
<twig:Button ...>
{{- block('button_label_content') -}}
</twig:Button>
{% endblock %}
But with this syntax it works: {% block button_widget %}
{{ component('Button', {
children: block('button_label_content'),
}) }}
{% endblock %} |
Does it work with this syntax?
|
Will check it tomorrow. |
yes it works |
perfect! Then @WebMamba we just have a pre-lexing problem here of some sort. |
Ok Great! I have a better understanding of the problem now! I am checking that! 😁 |
So if I have a good understanding of the problem: <twig:foo><p>My content</p>{% block bar %}<p>Inside bar</p>{% endblock %}</twig:foo> should turn into: {% component 'foo' %}{% block content %}<p>My content</p>{% endblock %}{% block bar %}<p>Inside bar</p>{% endblock %}{% endcomponent %} |
Ok, I checked into this a bit to help with #852. @WebMamba I think it's a bit more complicated... and we actually don't have a syntax yet that makes sense for this. Here is the setup, using a realistic example: <twig:DangerButton>
Danger will Robinson!
</twig:DangerButton> So, I'm passing a Let's forget about the HTML syntax for a moment, and just think about how this should look using the
So, we create the Unfortunately, as @norkunas mentioned, this creates recursion and the page times out. However, I think that this SHOULD be allowed. It IS allowed if you do something similar in normal Twig - i.e. outside of a
So, I think the fix is 2 parts: A) Fix the B) Fix the
This currently becomes:
But it should just be:
|
A bit more thinking. I believe we need to change our
It's obvious what should happen here: the text
Probably we should deprecate our |
Ok, I see what you mean! Thanks for sharing your thought @weaverryan! When I was working on the fix, I feel two things:
I like this library as well https://github.com/giorgiopogliani/twig-components, but why deprecated the entire component tag, and why not just add a slot tag instead? |
Yes, but that will take some time and thought. So... we should do it later ;).
I believe that the entire way that Also, see #863 for another example where I think our |
@weaverryan As I also commented on #863 (comment), I think you should always frame components within the "all components exist in their own isolated universe" paradigm (cfr. ux-live docs). Given that each component renders separately, it's absolutely normal that the foo block in your example does not exist within the "Alert-universe". Moreover, even if you would alter the compiler for the ComponentNode so that it passes the blocks from the parent template to the Alert component, they would still be rendered twice, as there is no parent-child relation with embedded components. It's a separate render. That's also why the code of @norkunas in #844 (comment) works. {% set passthrough %}{% block content %}{% endblock %}{% endset %}
<twig:A>
{{ passthrough }}
</twig:A> What $context["passthrough "] = <the block content>;
//...
$props = $this->extensions["Symfony\\UX\\TwigComponent\\Twig\\ComponentExtension"]->embeddedContext("A", twig_to_array([]), $context);
$this->loadTemplate("a.html.twig", ...)->display($props);
/ Without that passing along of that information As for the very first example in this issue: <twig:A>
{%- block content -%}{%- endblock -%}
</twig:A> That doesn't make sense to me since this effectively is: "use a block named content as the (by default also named) content block of A". So this inevitably renders to {# anywhere #}
<twig:DivComponent>
<twig:block name="genericContent">Hello!</twig:block>
</twig:DivComponent> {# DivComponent.html.twig #}
<twig:GenericElement element="div">
{% block genericContent %}{% endblock %}
</twig:GenericElement> {# GenericElement.html.twig #}
<{{ element }} class="{{ classes }}"{{ attributes }}>
{%- block genericContent -%}{%- endblock -%}
</{{ element }}> Also not ideal, since you lose the HTML-feel a bit here, but I see no other way, since using the content block inside a |
@sneakyvv I think there's a philosophical decision to be made here about the "universe" aspect. The docs state:
This was meant to refer to how the components behave and re-render on the page. Inside the templates, if we're inside of AlertGreeting.html.twig
{% block greeting %}Hello!{% endblock %}
{{ dump(this) }} Dumps Foo
{% component Alert %}
{{ dump(this) }} Dumps Foo
{{ block('greeting') }} Prints "Hello!"
{% endcomponent %} This is, I believe, most similar to frontend frameworks. For example, in React, the equivalent might be: // AlertGreeting.jsx
export default class extends Component {
greeting = "Hello!";
render() {
return (
<Alert>{this.greeting}</Alert>
);
}
} In this case, |
@weaverryan I know the universe-rule actually applies to the live components, but, how the current rendering of a component is working, you can think of the (current) behavior in the same way. Because components are embedded, and their blocks are living inside the universe of the component they're defined in, not in the universe of the component they're being embedded in. I don't think it is even possible to override a block from outside a component, without completely reworking the syntax/compiler, since they are not being used in an extend-flow, but an embed-flow. Or am I overlooking something? Note the "without completely reworking the syntax/compiler", which is obviously also an option 😄 |
But not in a general, because how would you tell if a block is supposed to extend/override a parent block, or whether it should be used as value for a block in a component? {# base.twig #}
...
{% block base_block %}base version{% endblock %}
... {# someFile.twig #}
{% extends base.twig %}
{% block base_block %}override base{% endblock %}
{% block foo_block %}I am the foo block{% endblock %}
<twig:componentA>
<div>{{ block('foo_block') }}</div>
</twig> Should |
That's why I think we should stop using "blocks" for passing content into components - it's doing 2 jobs at once and is confusing and has weird questions like this #844 (comment)
Exactly - so let's move away from using blocks for this! And let blocks be blocks - use a new "slot" idea like in https://github.com/giorgiopogliani/twig-components#usage |
Completely agree, but just to be clear: even with slots it won't be possible to pass outside-slots in, am I right? |
Are you thinking something like this? (Syntax is up for debate - but just focus on the idea): SomeTemplate.html.twig
{% component AlterGreeting %}
{% slot:title %}Hello!{% endslot %}
{% endcomponent %} AlertGreeting.html.twig
{% component Alert %}
{% slot:content %}{{ title }}{% endslot %}
{% endcomponent %} Or are you thinking something else? I wasn't totally sure what you meant by "I mean slots outside of the components tags". With the above, it should be easy to grab the slot from one component and "forward" it into the next component. |
…andled correctly (weaverryan) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Fixing bug where traditional blocks aren't handled correctly | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | Fixes part of #844 (comment) | License | MIT Using `{% block traditional_block %}` inside of a `<twig:Component` syntax should now work. However, even though I tried to make the TwigPreLexer a bit more intelligent than just a regex parser, it's reaching its limits of complexity. Even this fix will break down if the user adds extra whitespace - e.g. `{% block traditional_block %}`. It's likely that `TwigPreLexer` will need to be converted to an actual Lexer -> token stream -> parser type of a system. On the bright side, that would make it easier to integrate into Twig core if we ever chose to do that (the parser wouldn't convert over directly, but the lexer & tokens in theory would). Cheers! Commits ------- 2ca9430 [TwigComponent] Fixing bug where traditional blocks aren't handled correctly
Yes, that syntax is perfectly fine! |
@weaverryan see #863 (comment) as to why I believe a new slot syntax (which equals to an include flow instead of embed) is not the solution for this PR, as it would let forgo a much bigger advantage of using embed. |
…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
…andled correctly (weaverryan) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Fixing bug where traditional blocks aren't handled correctly | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | Fixes part of symfony/ux#844 (comment) | License | MIT Using `{% block traditional_block %}` inside of a `<twig:Component` syntax should now work. However, even though I tried to make the TwigPreLexer a bit more intelligent than just a regex parser, it's reaching its limits of complexity. Even this fix will break down if the user adds extra whitespace - e.g. `{% block traditional_block %}`. It's likely that `TwigPreLexer` will need to be converted to an actual Lexer -> token stream -> parser type of a system. On the bright side, that would make it easier to integrate into Twig core if we ever chose to do that (the parser wouldn't convert over directly, but the lexer & tokens in theory would). Cheers! Commits ------- 2ca9430a [TwigComponent] Fixing bug where traditional blocks aren't handled correctly
So I have a component A which defines it's content block. Then I have a higher level component B which also defines it and to forward to the A component, but Twig says:
The block 'content' has already been defined line 6.
The text was updated successfully, but these errors were encountered: