在包含的Twig模板中覆盖块

61

有没有一种通常的“好”方法来实现这个功能?我已经阅读了关于“use”标签的内容,它似乎是目前最好的选择,但我仍然不喜欢它不允许我引入任何外部html,只能使用块。

我将在下面的示例中使用“include”标签来说明我试图描述的意图。

#base.html.twig
{% include 'elements/header.html.twig' %}
{% block content %}{% endblock %}
{% include 'elements/footer.html.twig' %}

#header.html.twig
<h1>This is my header</h1>
{% block page_title %} Default Page Title {% endblock %}

#index.html.twig
{% extends 'layouts/base.html.twig' %}
{# I want to be able to do this somehow #}
{% block page_title %} This is my overridden page title {% endblock %}
{% block content %} here is the index page content {% endblock %}

有人在这里提交了一张票据:https://github.com/twigphp/Twig/issues/1360 - mpen
5个回答

71

我找到了一个解决方案。使用block()函数来获取子块的内容,并将其作为变量传递给include语句中的header.html.twig:

#base.html.twig
{% include 'elements/header.html.twig' with {page_title: block('page_title')} %}
{% block content %}{% endblock %}
{% include 'elements/footer.html.twig' %}

#header.html.twig
<h1>This is my header</h1>
{% if page_title is empty %}
Default Page Title
{% else %}
{{ page_title }}
{% endif %}

#index.html.twig
{% extends 'layouts/base.html.twig' %}
{% block page_title %} This is my overridden page title {% endblock %}
{% block content %} here is the index page content {% endblock %}

这就是我如何定义需要在子视图中呈现的块。我认为你不会找到一个真正允许你在包含的视图中使用块的解决方案... - Webberig
5
这仍然是最佳解决方案吗?对我来说,在“include”内部有一个“block”似乎很琐碎。 - Zizaco
1
我将称呼你为布鲁斯·韦伯里·李先生。 - Benjamin Vison
先生,您刚刚让我开心了! :-) - Webberig
5
如果“块”包含HTML,则无法正常工作。例如,如果您想包含标题、正文和页脚,并且在正文中有要替换的“块”,则需要使用 {{ body|raw }} 而不是 {{ body }},以避免HTML被转义。请注意,这样做不会改变原有意思。 - Xavi Montero
显示剩余4条评论

20

我在阅读embed标签的文档时,找到了一个好的而且真实的解决方案。我正在使用Symfony 4中的Twig 2.0。

我的结构

#templates/base.html.twig
{% block navbar %}
    <nav>
    {% block menu %}
        {% include '_menu' %}
    {% endblock menu %}
    </nav>
{% endblock navbar %}
{% block content %}
    {# several blocks defined #}
    {% block footer '' %}
{% endblock content %}

#templates/_menu.html.twig
<ul>
    {% block sub_app_menu_itens %}
        <li>Main App Menu Item</li>
    {% endblock sub_app_menu_itens %}
    <li>Menu item</li>
    <li>Another menu item</li>
    <li>Yet another menu item</li>
</ul>

使用以上代码,当我创建我的index.html.twig时,只需使用以下代码即可显示默认内容:

#templates/index.html.twig
{% extends 'base.html.twig' %}

并覆盖已定义的块。 但是,当我需要创建另一个页面时,使用这些骨架,如果我尝试在另一个包含的_partial模板中覆盖sub_app_menu_itens块,则不起作用。<li>Main App Menu Item</li>总是显示,并且永远不会被覆盖(上面的代码)

#templates/subapp/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}{{ 'agencia.homepage'|trans }}{% endblock %}
{% block menu %}
    {% include 'subapp/_menu.html.twig' %}
{% endblock menu %}
{% block user_content %}Content Here{% endblock %}

#templates/subapp/_menu.html.twig
{% extends '_menu.html.twig' %}
{% block sub_app_menu_itens %}
        <li>SubApp Menu Item</li> 
{% endblock sub_app_menu_itens %}

<li>SubApp菜单项</li>从未显示。尝试了很多东西,如extends甚至带条件语句都没成功。

解决方案

嵌入标签解决了子部分模板块可以被覆盖的问题。

#templates/subapp/index.html.twig
{% block menu %}
    {% embed '_menu.html.twig' %}
        {% block sub_app_menu_itens %}
            {% include 'subapp/_menu.html.twig' %}
        {% endblock sub_app_menu_itens %}
    {% endembed %}
{% endblock menu %}

简化部分模板,仅保留所需的html。

#templates/subapp/_menu.html.twig 
    <li>SubApp Menu Item</li> 

现在,<li>SubApp 菜单项</li> 将会显示在 _menu 模板的正确位置。

从层次思考,include 像是一个子级(被解析的),所有的内容只能在同一级别上覆盖,所以 include 不允许在另一个 include 中进行覆盖。相反,embed 模板则被带到了同一级别(未解析),因此您可以覆盖其中的内容。


3

可以做到。这是我的解决方法,通过将变量传递给视图。

#layout.twig
{% if sidebar is empty %}
    This is the default sidebar text.
{% else %}
    {% block sidebar %}{% endblock %}
{% endif %}
{% block content %}{% endblock %}

#index.twig
{% extends "layout.twig" %}
{% block sidebar %}
    This is the sidebar. It will override the default text.
{% endblock %}

{% block content %}
    This is the content.
{% endblock %}

#index.php (SlimPHP)
$app->render('index.twig', ['sidebar' => true]);

由于我正在使用SlimPHP,因此调用它的方法是通过将sidebar变量传递给视图。通过向视图传递不同的变量,可以进一步扩展此功能,因此您可以选择sidebar1sidebar2等。


0

我用了一个非常简单的hack让它工作了:

基本上,它的行为是这样的,因为没有{% extends "foo.html.twig" %},它不理解块并且只是在原地呈现它们。

所以我们来扩展……什么也不扩展:

{% extends "nothing.html.twig" %}

这个东西其实只是一个块:

# nothing.html.twig
{% block main %}
{% endblock %}

唯一需要注意的是,你需要将所有内容都包裹在一个块中,这个假的“主”块。

0

对@webberig解决方案的改进,修复了在所有扩展模板中未被覆盖的块(@HerrNentu'的评论):

#base.html.twig
{% if block("page_title") is defined %}
     {% include 'elements/header.html.twig' with {page_title: block('page_title')} %}
{% else %}
     {% include 'elements/header.html.twig' with {page_title: null} %}
{% endif %}
{% block content %}{% endblock %}
{% include 'elements/footer.html.twig' %}

#header.html.twig
<h1>This is my header</h1>
{% if page_title is empty %}
Default Page Title
{% else %}
{{ page_title }}
{% endif %}

#index.html.twig
{% extends 'layouts/base.html.twig' %}
{% block page_title %} This is my overridden page title {% endblock %}
{% block content %} here is the index page content {% endblock %}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接