symfony / ux

Symfony UX initiative: a JavaScript ecosystem for Symfony

Home Page:https://ux.symfony.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[TwigComponent] Parser fails on Twig tags within attributes

ameotoko opened this issue · comments

Normally, in a Twig template you can do something like this:

{% set target = 'target="_blank"' %}
<a
    href="{{ href|default('./') }}"
    title="{{ pageTitle|default(title) }}"
    {% if class|default %} class="{{ class }}"{% endif %}
    {% if tabindex|default %} tabindex="{{ tabindex }}"{% endif %}
    {{ target }}
>{{ link }}</a>

This doesn't seem possible with Twig Components, it breaks at {% if or {{, even if those are commented out:

Twig\Error\SyntaxError:
Expected attribute name when parsing the "<twig:Button" syntax.

It looks like the parser just bypasses the Twig engine completely and parses the code as raw file instead. I searched through the issues, and it seems you guys are already aware of the problem (for example here: #880 (comment)) and it needs much more rewriting than one might think 🙂

Nevertheless, I thought that may be this specific use-case needs to be mentioned here, just to keep track of everything.

What are you trying to do? Something like:

<twig:Link {% if class|default %} class="{{ class }}"{% endif %} />

Or something similar ?

Yes, exactly that.

You cannot, as you cannot either in include or embed component for instance.

Because it would require a loop lexer / parser that does not exist.

In your example you "echo" something in Twig, but you cannot have logical "before" the execution of the component.

Hum yeah, I think we can't solve that easily! To have this TwigComponent syntax we have what we call a Prelexer. It's a little class that goes over your templates and transforms <twig: into {{ component() }}. The thing is in this Prelexer class we don't have access to all the tokens the Twig Lexer has access to. So for the moment, we let this syntax have it, but maybe one day the TwigComponent syntax will be fully integrated into Twig, but you need to convince Fabien first 😉

That would be really hard to implement this syntax, but for the moment you can spread dynamic properties like that :

<twig:Link
    {{ ...class is defined ? { class } : {} }}
    {{ ...tabindex is defined ? { tabindex } : {} }}
/>

{# or #} 
<twig:Link {{ ...{
    class: class|default ? class : false,
    tabindex: tabindex|default ? tabindex : false,
} }}
/>

@WebMamba honestly i'm not sure this is easily feasable (and personaly sure it should be done)

That would be equivalent to something like {% if foo '{% if bar %}' %}

That would be really hard to implement this syntax, but for the moment you can spread dynamic properties like that :

What about

<twig:Link class="{{ class ?? false }}" tabindex="{{ tabindex ?? false }}" /> 

I'm closing here as this limitation is not something we really can work on

What about

<twig:Link class="{{ class ?? false }}" tabindex="{{ tabindex ?? false }}" /> 

This actually does the right thing! It seems that I missed the part from the docs where it says:

Set an attribute's value to false to exclude the attribute:

{# templates/components/Input.html.twig #}
<input{{ attributes }}/>

{# render component #}
<twig:Input type="text" value="" :autofocus="false" />

{# renders as: #}
<input type="text" value=""/>