How to check if a block is overridden in Twig? - php

I would like to check whether a block is overriden in a child template.
template.html.twig:
<html>
...
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
...
<div class="contents">
{% block contents %}
{% endblock %}
</div>
...
</html>
page1.html.twig -- contains a menu:
{% extends '::template.html.twig' %}
{% block menu %}
<ul>
<li>...</li>
</ul>
{% endblock %}
{% block contents %}
Hello World!
{% endblock %}
page2.html.twig -- does not contain a menu:
{% extends '::template.html.twig' %}
{% block contents %}
Hello World!
{% endblock %}
I would like to show the nav element in the template, only if it is overriden in the child template (the goal is to avoid an empty <nav /> element).
The Twig syntax has a is empty test, but it only applies to variables, not blocks.
What am I doing wrong?

You can set a variable in your child template check for that:
{# template.html.twig #}
{% if show_menu is not empty %}
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
{% endif %}
{# page1.html.twig #}
{% set show_menu = true %}
Maybe you should also consider putting this markup in the block definition for a more straight-forward approach.

You could add a wrapper to your nav element and then override it in page2 to make it blank when you don't want a menu. This doesn't require an extra variable.
template.html.twig
... snip ...
{% block nav %}
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
{% endblock %}
... snip ...
page2.html.twig
{% extends '::template.html.twig' %}
{% block nav '' %} {# here's the kicker ... #}
{% block contents %}
Hello World!
{% endblock %}
In any other page that has a menu you would continue to override the 'menu' block normally.

Related

How to overwrite a html-element but keep the rest of a block?

I am extending a twig-template where i want to replace the line
<div class="container">
with
<div class="container-fluid">
Twig-template
{% block layout_main_navigation %}
<div class="main-navigation"
id="mainNavigation"
data-flyout-menu="true">
{% block layout_main_navigation_navbar %}
<div class="container"> <!-- This line should be replaced -->
{% block layout_main_navigation_menu %}
...
{% endblock %}
...
{% endblock %}
...
{% endblock %}
I tried to overwrite the layout_main_navigation by copying everything and changing the class of the div. But i am not happy with that solution as i have to copy and overwrite a lot blocks.
How can i achieve replacing the class of the div and overwrite as less block as possible?
You can override the block layout_main_navigation_navbar, add the div element with the desired class container-fluid and then include the original content of the block layout_main_navigation_menu using the parent() function:
{% block layout_main_navigation_navbar %}
<div class="container-fluid">
{% block layout_main_navigation_menu %}
{{ parent() }}
{% endblock %}
</div>
{% endblock %}

Replace only one nested block in twig

I have the following twig structure:
base.twig
<html>
<head>
</head>
<body class="fade-in {{body_class}}">
<main>
{% block menu %}
{% include 'menu.twig' %}
{% endblock %}
</main>
</body>
</html>
menu.twig
<header>
<div>
{% block menu_main %}
{% include 'menu-main.twig' %}
{% endblock %}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: dropdownland } %}
{% endblock %}
</div>
</header>
child.twig
{% extends "base.twig" %}
{% block menu %}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: menu_ap_countries } %}
{% endblock %}
{% endblock %}
What i want to achieve is, just replace the block menu_country inside child.twig. If i use the approach above, the whole block menu gets replaced by only menu_country which means that the block menu_main is missing.
I tried also
{% extends "base.twig" %}
{% block menu %}
{{ parent() }}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: menu_ap_countries } %}
{% endblock %}
{% endblock %}
but this renders the block menu_country twice. Is there any way to achieve this?
Thanks a lot.
After further investigation due to #DarkBees answer, i came accross the embed function which does exactly what i need in this case. So the extending template becomes this:
{% block menu %}
{% embed 'menu.twig'%}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: menu_ap_countries } %}
{% endblock %}
{% endembed %}
{% endblock %}
By embedding, I am able to overwrite the block inside menu.twig
Including templates does not mean u are importing said blocks inside the template.
This mean only the block menu will exists inside child.twig.
In your first example you are actually just overwriting the block menu and creating a new block menu_country inside of it.
In your second example, you are telling twig to output the default content of the block menu and append a new block menu_country to it.
A possible solution would be to change set-up to this e.g.
menu.twig
<header>
<div>
{% block menu_main %}
{% include 'menu-main.twig' %}
{% endblock %}
{% block menu_country %}
{% include 'menu-country.twig' %}
{% endblock %}
</div>
</header>
menu-country.twig
<ul class="country">
{% for country in menu_country|default(dropdownland) %}
<li>{{ country }}</li>
{% endfor %}
</ul>
child.twig
{% extends "base.twig" %}
{% block menu %}
{% include 'menu.twig' with { menu_country: menu_ap_countries, } %}
{% endblock %}
demo

extend or include multiple instances TWIG

I am using Twig to use as templating engine and i have a common right sidebar that shall be included on all pages.
_base.twig
/* Page HTML header */
{% block content %} {% endblock %}
/* Page HTML Footer */
index.twig
{% extends "_base.twig" %}
{% block content %}
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
{% endblock %}
about.twig
{% block content %}
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
{% endblock %}
.... so on for many pages.. I want right-bar to be a external page and shall be included every page as content is same always.
I tried using {% extends %} two times but Twig fails as i read that multiple extends are not allowed. So any solutions please.
With Twig, you have many possibilities for solving your problem :
A/ The parent solution Official doc is here
This solution renders the contents of the parent block in your child Block.
_base.twig
/* Page HTML header */
{% block content %}
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
{% endblock %}
/* Page HTML Footer */
index.twig
{% extends "_base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
about.twig
{% extends "_base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
other.twig
{% extends "_base.twig" %}
{% block content %}
<div> If you don't place the parent twig attribute, your menu can't appear and will be overrided</div>
{% endblock %}
B/ The Include Solution Official doc is here
This solution create another block, and call the block in the child block.
_base.twig
/* Page HTML header */
{% block content %}
{% endblock %}
/* Page HTML Footer */
navBar.twig
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
index.twig
{% extends "_base.twig" %}
{% block content %}
{{ include('navBar.twig') }}\]
{% endblock %}
about.twig
{% extends "_base.twig" %}
{% block content %}
{{ include('navBar.twig') }}
{% endblock %}
If you use the Symfony2 Framework, you can already use the {{ render(controller('')) }} [you can see a sample here] for call a specific controller, himself call a twig view... it's very useful if you want to load a dynamic values in your block, but is less effective than a {{ include('') }}...
For me, the B solution is the best, but both solutions work.

Append inline scripts inside Twig layout block

I have a layout that looks like this:
<html>
<body>
<!-- Omitted -->
<div class="content">
{% block body %}{% endblock %}
</div>
<script src="js/vendor.js"></script>
{% block javascripts %}
<!-- I want to be able to inject inline scripts from sub-templates here -->
{% endblock %}
</body>
</html>
Then I have the register_content.html.twig template overridden from FOSUserBundle and in it I am trying to inject script into the "javascripts" block like so:
{% block javascripts %}
<script>
// Some inline JS
</script>
{% endblock %}
The problem I am having is the inline script seems to get injected at the end of the "body" block and not in the "scripts" block.
This is the output I am getting:
<!-- This should be below js/vendor.js scripts -->
<script>
// Some inline JS
</script>
</div>
</div>
<script src="js/vendor.js"></script>
What am I doing wrong? Does my problem lie with FOSUserBundle and it's own templates it inherits from (it still uses my layout and everything I have in it; not it's own layout). I'm very confused here.
P.S. I'm total newbie to Symfony/Twig so please be easy on me.
EDIT: These are the FOSUserBundle layout and templates I have overriden:
app/Resources/FOSUserBundle/views/layout.html.twig:
{% extends 'AcmeWebBundle::layout.html.twig' %}
{% block title %}Acme Demo Application{% endblock %}
{% block body %}
{% block fos_user_content %}{% endblock %}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{% endblock %}
app/Resources/FOSUserBundle/views/Registration/register.html.twig:
{% extends "FOSUserBundle::layout.html.twig" %}
{% block body %}
{% block fos_user_content %}
{% include "FOSUserBundle:Registration:register_content.html.twig" %}
{% endblock fos_user_content %}
{% endblock %}
{% block javascripts %}
{% parent() %}
{% endblock %}
app/Resources/FOSUserBundle/views/Registration/register_content.html.twig:
<div class="page-header">
<h2>Register</h2>
</div>
<form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register form-horizontal">
<!-- HTML Omitted -->
</form>
{% block javascripts %}
{% javascripts '#AcmeWebBundle/Resources/public/js/app.js' %}
<script>
// jQuery is undefined because it gets defined below the 'javascripts' block in the parent layout
console.dir(window.jQuery);
</script>
{% endjavascripts %}
{% endblock %}
After the EDIT the situation is more clear.
The behaviour you expect works with template inheritance only; it won't work with include, the way you have it in app/Resources/FOSUserBundle/views/Registration/register.html.twig.
To do what you want, do one of the following:
Place the inline JavaScript code from the javascript block of app/Resources/FOSUserBundle/views/Registration/register_content.html.twig inside the javascript block of app/Resources/FOSUserBundle/views/Registration/register.html.twig
Let template app/Resources/FOSUserBundle/views/Registration/register.html.twig inherit from app/Resources/FOSUserBundle/views/Registration/register_content.html.twig and override its blocks as needed (register_content.html.twig does not extend layout.html.twig, so this does not make much sense)

Twig - render block from included or parent template

is there any way to render block from included or parent template?
I have 3 templates
main.html.twig:
{% include 'navbar.html.twig' %}
<div id="main_content">
{% block application_content %}
{% endblock application_content %}
</div>
{% block application_footer %}
footer
{% endblock application_footer %}
navbar.html.twig:
<p>bla bla</p>
{% block navbar_profile_photo %}
<img src="{{ image }}">
{% endblock navbar_profile_photo %}
content.html.twig:
{% extends "main.html.twig" %}
{% block application_content %}
lorem ipsum
{% block foo %}
dolor sit amet
{% endblock foo %}
{% endblock application_content %}
I want to be able call something like this
$twig->loadTemplate('content.html.twig')->renderBlock('navbar_profile_photo', array('image' => 'bar.jpg'))
But I can get only blocks from 'content.html.twig'
$twig->loadTemplate('content.html.twig')->getBlockNames();
returns
['application_content', 'foo']
I could add to content.html.twig
{% use 'navbar.html.twig' %}
but in my case I had to do it in many templates with many different use statements
Is there some way to get the final template with all includes and extends and blocks?
In content.html.twig file you must add:
{% extends "EverlutionApplicationBundle::main.html.twig" %}
{% block navbar_profile_photo %}
{{ parent() }}
{% endblock navbar_profile_photo %}
.....

Categories