Add content to block multiple tweaks

There are several questions on how to add a block with a twig. The answer always uses inheritance and usage, and then calls parent (). Somehow I don't know how this works in my particular case:

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 %} 

I would like both contents from sub1 and sub2 to be displayed within the incremental. How can i achieve this?

+1
source share
4 answers

Let it go. I had the same problem and this solution works for me:

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 %} 

According to my research, this method is called "horizontal reuse", and here's the document:

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

+5
source

To include a template, you need to use the include keyword, not the use keyword:

 {% 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 might look like this:

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

Using inheritance

If you want, you can use the {{ parent() }} keyword to use inheritance. For example, if you want to include sub1.html.twig by default, but add sub2.html.twig to your child template, you can do the following:

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
source

I want to share my solution for this problem. I implemented my own Twig extension, which implements a custom widget tag (I used the Twig embed tag as the source).

Extension

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 } 

Usage example

Views / Blocks / 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 %} 

Views / Control Panel / Widgets / 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 %} 

Summary

So, as you can see, with my extension you can include a template for reusing blocks inside it. If you need multiple widgets, you can have multiple widget tags in your template using the same source template, and the contents of the block will not overlap. In fact, it works like embedding a template using Twig embed (and I used this tag as a source for my extension), but with the only (and big) difference - it displays ONLY a block called a "widget". All other blocks are ignored, but can be used inside the widget block.

0
source

If you define only blocks in subpatterns, you can use the block function and explicitly replace:

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 %} 
0
source

All Articles