附加内容以阻止多个子模板

时间:2013-01-28 14:36:57

标签: symfony twig

关于如何使用树枝追加块有几个问题。答案总是使用继承和使用,然后调用parent()。不知怎的,我不知道在我的具体情况下这是如何工作的:

base.html.twig

{% block content %}{% endblock %}
{% block appendable %}
{% endblock %}
{% block another_appendable %}
{% endblock %}

site.html.twig

{% extends base.html.twig %}
{% block content %}
{# Here use/include/embed, i dont know #}
{% use sub1.html.twig %}
{% use sub2.html.twig %}
{% endblock content %}

sub1.html.twig

Some content that should be directly rendered
{% block appendable %}
some stuff that should be added to appendable
{% endblock %}
{% block another_appendable %}
This content should be added to "another appendable"
{% endblock %}

sub2.html.twig

{% block appendable %}
additional stuff that should be appended
{% endblock %}

我希望sub1和sub2中的两个内容都在appendable中呈现。我怎么能做到这一点?

4 个答案:

答案 0 :(得分:5)

我们走吧。我有同样的问题,这个解决方案对我有用:

base.html.twig

{% block content %}{% endblock %}

site.html.twig

{% extends base.html.twig %}

{% use sub1.html.twig with appendable as appendableContent, another_appendable as another_appendableContent %}

{% block content %}

    {% block appendable -%}
        {{ block('appendableContent') }}
    {% endblock %}

    {% block another_appendable -%}
        {{ block('another_appendableContent') }}
    {% endblock %}

{% endblock %}

sub1.html.twig

{% use sub2.html.twig with appendable as appendableContentAlternative %}

{% block appendable %}
    some stuff that should be added to appendable<br/><br/>

    {{ block('appendableContentAlternative') }}
{% endblock %}

{% block another_appendable %}
    This content should be added to "another appendable"<br/><br/>
{% endblock %}

sub2.html.twig

{% block appendable %}
    additional stuff that should be appended<br/><br/>
{% endblock %}

根据我的研究,这种技术被称为“水平重用”,这里是doc:

http://twig.sensiolabs.org/doc/tags/use.html

答案 1 :(得分:2)

要包含模板,您需要使用include关键字,而不是use关键字:

{% block appendable %}

    {# Assuming your sub1 template is in AcmeDemoBundle/Resources/views/MySub/sub1.html.twig #}
    {% include "AcmeDemoBundle:MySub:sub1.html.twig" %}

{% endblock appendable %}

AcmeDemoBundle:MySub:sub1.html.twig 可能如下所示:

<b>Put directly your code there, no need to use the blocks.</b>

使用继承

如果您愿意,可以使用{{ parent() }}关键字来继承。例如,如果您希望默认包含sub1.html.twig,但在您的子模板中附加sub2.html.twig,则可以执行以下操作:

base.html.twig

{% block content %}

    {% include "AcmeDemoBundle:MySub:sub1.html.twig" %}

{% endblock %}

site.html.twig

{% extends base.html.twig %}

{% block content %}

    {# render what happens in the parent content block #}
    {{ parent() }}

    {# and append sub2.html.twig as well #}
    {% include "AcmeDemoBundle:MySub:sub2.html.twig" %}

{% endblock content %}

答案 2 :(得分:0)

我想分享我自己的解决方案。我实现了自己的Twig扩展,它实现了自定义标记widget(我使用了Twig标记embed作为源代码。)

扩展

WidgetNode.php:

namespace Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension;

/**
 * Class WidgetNode
 *
 * @author Denis V
 *
 * @package Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension
 */
class WidgetNode extends \Twig_Node_Include
{
    // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
    public function __construct($filename, $index, \Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
    {
        parent::__construct(new \Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);

        $this->setAttribute('filename', $filename);
        $this->setAttribute('index', $index);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param $compiler \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        if ($this->getAttribute('ignore_missing')) {
            $compiler
                ->write("try {\n")
                ->indent()
            ;
        }

        $this->addGetTemplate($compiler);

        $compiler->raw('->displayBlock(');

        $compiler->string('widget');

        $compiler->raw(', ');

        $this->addTemplateArguments($compiler);

        $compiler->raw(");\n");

        if ($this->getAttribute('ignore_missing')) {
            $compiler
                ->outdent()
                ->write("} catch (Twig_Error_Loader \$e) {\n")
                ->indent()
                ->write("// ignore missing template\n")
                ->outdent()
                ->write("}\n\n")
            ;
        }
    }

    protected function addGetTemplate(\Twig_Compiler $compiler)
    {
        $compiler
            ->write("\$this->env->loadTemplate(")
            ->string($this->getAttribute('filename'))
            ->raw(', ')
            ->string($this->getAttribute('index'))
            ->raw(")")
        ;
    }
}

WidgetTokenParser.php:

namespace Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension;

/**
 * Class WidgetTokenParser
 *
 * @author Denis V
 *
 * @package Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension
 */
class WidgetTokenParser extends \Twig_TokenParser_Include
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_NodeInterface A Twig_NodeInterface instance
     */
    public function parse(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();

        $parent = $this->parser->getExpressionParser()->parseExpression();

        list($variables, $only, $ignoreMissing) = $this->parseArguments();

        // inject a fake parent to make the parent() function work
        $stream->injectTokens(array(
            new \Twig_Token(\Twig_Token::BLOCK_START_TYPE, '', $token->getLine()),
            new \Twig_Token(\Twig_Token::NAME_TYPE, 'extends', $token->getLine()),
            new \Twig_Token(\Twig_Token::STRING_TYPE, '__parent__', $token->getLine()),
            new \Twig_Token(\Twig_Token::BLOCK_END_TYPE, '', $token->getLine()),
        ));

        $module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true);

        // override the parent with the correct one
        $module->setNode('parent', $parent);

        $this->parser->embedTemplate($module);

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new WidgetNode($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endwidget');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'widget';
    }

}

TemplateTagsExtension.php:

namespace Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension;

/**
 * Class TemplateTagsExtension
 *
 * @author Denis V
 *
 * @package Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension
 */
class TemplateTagsExtension extends \Twig_Extension
{
    /**
     * @inheritdoc
     */
    public function getTokenParsers()
    {
        return array(
            new WidgetTokenParser(),
        );
    }

    /**
     * Returns the name of the extension.
     *
     * @return string The extension name
     */
    public function getName()
    {
        return 'template_tags';
    }
}

services.yml:

parameters:
    artprima.twig.extension.template_tags.class: Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension\TemplateTagsExtension

services:
    artprima.twig.extension.template_tags:
        class: %artprima.twig.extension.template_tags.class%
        tags:
            - { name: twig.extension }

用法示例

视图/块/ widget.html.twig:

{# please note, that only "widget" block is rendered, all other blocks can be used inside the "widget" block #}
{# if you don't define the "widget" block, nothing will be rendered #}
{% block widget %}
    <div class="{{ block('widget_box_class') }}">
        {{ block('widget_header') }}
        {{ block('widget_body') }}
    </div>
{% endblock %}

{% block widget_header %}
    <div class="{{ block('widget_header_class') }}">
        {{ block('widget_title') }}
        {% if display_toolbar is defined and display_toolbar %}{{ block('widget_toolbar') }}{% endif %}
    </div>
{% endblock %}

{% block widget_body %}
    <div class="{{ block('widget_main_class') }}">
        {{ block('widget_main') }}
    </div>
{% endblock %}

{% block widget_title %}
    <h5 class="widget-title">{{ block('widget_title_text') }}</h5>
{% endblock %}

{% block widget_title_text %}(undefined){% endblock %}

{% block widget_toolbar %}
    <div class="widget-toolbar">
        {{ block('widget_toolbar_inner') }}
    </div>
{% endblock %}

{% block widget_toolbar_inner %}{% endblock %}

{% block widget_box_class %}{% spaceless %}widget-box{% endspaceless %}{% endblock %}

{% block widget_main_class %}{% spaceless %}widget-main{% endspaceless %}{% endblock %}

{% block widget_main %}{% endblock %}

{% block widget_header_class %}{% spaceless %}widget-header{% endspaceless %}{% endblock %}

视图/仪表板/部件/ sample.html.twig

{% widget "ArtprimaSampleBundle:Blocks:widgets.html.twig" %}
    {% block widget_title_text %}{{ "widget.records_creation_history"|trans }}{% endblock %}
    {% block widget_main_class %}{% spaceless %}no-padding {{ parent() }}{% endspaceless %}{% endblock %}
    {% block widget_main %}
        <table class="table table-striped table-bordered table-hover no-margin-bottom">
            <thead>
            <tr>
                <th>Description</th>
                <th>Amount</th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td>{{ "widget.number_of_countries.created"|trans }}</td>
                <td>{{ dashboard.countries.created }}</td>
            </tr>
            <tr>
                <td>{{ "widget.number_of_users.created"|trans }}</td>
                <td>{{ dashboard.users.created }}</td>
            </tr>
            </tbody>
        </table>
    {% endblock %}
{% endwidget %}

摘要

因此,正如您所看到的,通过我的扩展,可以包含一个模板,重新使用其中的块。如果您需要多个小部件,则可以使用相同的源模板在模板中包含多个widget标记,并且块内容不会重叠。从本质上讲,它就像使用Twig的embed嵌入模板(我使用此标记作为我的扩展的来源),但只有(和主要)差异 - 它呈现 ONLY < / em>名为&#34; widget&#34;的块。所有其他块都被忽略,但可以在&#34;小部件内使用&#34;块。

答案 3 :(得分:0)

如果您只在子模板中定义块,则可以使用block函数并明确替换:

base.html.twig

{% block content %}{% endblock %}
{% block appendable %}{% endblock %}
{% block another_appendable %}{% endblock %}

site.html.twig

{% extends base.html.twig %}

{% block appendable %}
{{ block('appendable', 'sub1.html.twig') }}
{{ block('appendable', 'sub2.html.twig') }}
{% endblock %}

{% block another_appendable %}
This content should be added to "another appendable"
{{ block('another_appendable', 'sub1.html.twig') }}
{% endblock %}

sub1.html.twig

{% block appendable %}
some stuff that should be added to appendable
{% endblock %}

{% block another_appendable %}
This content should be added to "another appendable"
{% endblock %}

sub2.html.twig

{% block appendable %}
additional stuff that should be appended
{% endblock %}