Skip to content
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

Conditional {% use %} not working #3698

Open
janklan opened this issue May 5, 2022 · 2 comments · May be fixed by #4292
Open

Conditional {% use %} not working #3698

janklan opened this issue May 5, 2022 · 2 comments · May be fixed by #4292

Comments

@janklan
Copy link

janklan commented May 5, 2022

Version context:

Twig version: v3.3.10
Twig Bridge version: 6.0.8
Symfony version: 6.0.8
PHP version: 8.1.5

What's happening:

I just tried to use a different twig trait depending on whether a form variable exists or not.

Below is a simplified version of my template structure.

  1. I'm building a "Create" form that can run inside a modal window driven by Turbo Frames, but the same page also works outside of the modal window, with different surrounding HTML markup, when loaded directly, not via the modal.
  2. My Symfony controller is aware of whether or not the request came from a modal window, and sets a modal variable to either true, or false.

base.html.twig

<html>
<head> ... something shared by a lot of scripts - no markup, just metadata ...</head>
<body>
{%block shell %}{%endblock %}
</body>
</html>

form-base.html.twig

{% extends 'base.html.twig' %}

{% if modal is defined %}
    {% dump('FIRST') %}
    {% use 'standard-form.html.twig' %}
{% else %}
    {% dump('SECOND') %}
    {% use 'modal-form.html.twig' %}
{% endif %}

standard-form.html.twig

{% block shell %}
    {% block body %}
        {{ form(form) }}
    {% endblock body %}
{% endblock shell %}

modal-form.html.twig

{% block shell %}
<turbo-frame id="modal-content">
    {{ form_start(form) }}
    <div class="header">some header contents</div>
    {% block body %}
        By default we just render the form, but people can override the block body, if they need something more specialised
        {{ form_rest(form) }}
    {% endblock body %}
    <button type="submit">
    {{ form_end(form) }}
</turbo-frame>
    ... some additional modal markup ...
{% endblock shell %}

create.html.twig

{% extends 'form-base.html.twig' %}

{% block body %}
 ... some custom form body markup - not important. 
{% endblock %}

Now, let's not get into whether I'm doing it wrong or not - I know I could extend different base templates etc., my point here is that I tried to use the {% use %} trait and it behaved differently than expected:

Consider the base template:

{% if modal is defined %}
    {% dump('FIRST') %}
    {% use 'standard-form.html.twig' %}
{% else %}
    {% dump('SECOND') %}
    {% use 'modal-form.html.twig' %}
{% endif %}

The core of what's wrong: Even though my debug toolbar dumped FIRST and not the SECOND, twig used the {% block shell %} present in modal-form.html.twig, while it should have used standard-form.html.twig.

Docs (https://twig.symfony.com/doc/3.x/tags/use.html) say that if multiple traits define the same block, the last one wins. When I commented the {% use 'modal-form.html.twig' %} out, the correct template showed up.

Conclusion in what I think is wrong:

When Twig parses the templates, it ignores the if/else construct around the use tag.

Is that expected behaviour? I think it should be at least documented?

I also tried {% use (form is defined ? '_stimulus/shell/modal-form.html.twig' : '_stimulus/shell/modal.html.twig') %}, which would work for example with {% include %}, but I got an error The template references in a "use" statement must be a string., suggesting the template path at that point was not resolved, further confirming my suspition.

@stof
Copy link
Member

stof commented May 6, 2022

{% use %} is a compile-time feature, while {% if %} is a runtime one. So it is expected that you cannot wrap a {% use %} tag inside a if to make it conditional.
That's exactly the same than for {% extends %} or {% block %} (a block inside a if is rendered conditionally, but it is always defined).

@janklan
Copy link
Author

janklan commented May 6, 2022

Ok, that makes sense. I have a more specific question, though:

If both use and extends are compile-time features, then if this works:

{% set template = someCondition ? 'extends1.html.twig' : 'extends2.html.twig' %}
{% extends  template %} 

shouldn't this work too?

{% set template = someCondition ? 'use1.html.twig' : 'use2.html.twig' %}
{% use template %}

When I try to use anything else than a plain string, I get that The template references in a "use" statement must be a string. - which is the only reason why I even tried to wrap the use in if/else.

I often extend a template using a variable and I thought there would be no difference in doing that with use? If it can't be done, maybe it's worth mentioning in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

3 participants