I am have one problem, my internal blocks on twig won't work.
This is my layout.html.twig file:
<body>
{% block body %}{% endblock %}
//some code here
</body>
This is my index.html.twig
{% extends ':layout:dashboard.html.twig' %}
{% block body %}
{% block header %}{% endblock header %}
//some code here
{% endblock body %}
and this is header.html.twig.
{% extends '#Dashboard/Dashboard/index.html.twig' %}
{% block header %}
//some code here
{% endblock header %}
Related
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
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)
To process my form collections I have a custom form theme for the block collection widget. This block collection widget is rendered as a table and so depends on a block_collection_header and a block_collection_body.
The block collection widget always stays the same, but sometimes I customize the two other blocks, block collection header and block collection body
My working code :
{# From file myview.html.twig #}
{% form_theme form ':Model:prototype_table_collection.html.twig' %}
{% form(form) %}
Ant this form theme is the following :
{# From file ':Model:prototype_table_collection.html.twig' #}
{% block collection_widget %}
{% spaceless %}
<div class="collection">
{% if prototype is defined %}
{% set body = prototype %}
{% set attr = attr|merge({'data-prototype': block('collection_body') }) %}
{% set header = prototype %}
{% set attr = attr|merge({'data-header': block('collection_header') }) %}
{% endif %}
{% if form.vars.allow_delete is defined and form.vars.allow_delete %}
{% set allow_delete = true %}
{% else %}
{% set allow_delete = false %}
{% endif %}
<div {{ block('widget_container_attributes') }} class="protoype">
{{ form_errors(form) }}
<table class="subtable table">
<thead>
<tr class="headers" style="display: none;">
{% if form.children|length > 0 %}
{% if form.children[0]|length > 0 %}
{% set header = form.children[0] %}
{{ block('collection_header') }}
{% endif %}
{% endif %}
</tr>
</thead>
<tbody class="container_rows">
{% for rows in form %}
{% spaceless %}
{% if rows.children|length > 0 %}
{% set body = rows %}
{{ block('collection_body') }}
{% endif %}
{% endspaceless %}
{% endfor %}
</tbody>
</table>
{% if prototype is defined %}
{% if form.vars.attr['data-add_label'] is defined %}
{% set add_label = form.vars.attr['data-add_label'] ~ ' ' %}
{% else %}
{% set add_label = 'Ajouter ' %}
{% endif %}
{{ add_label }}<i class="fa fa-plus"></i>
{% endif %}
<br>
</div>
</div>
{% endspaceless %}
{% endblock collection_widget %}
{% block collection_header %}
{% for field in header %}
<th>
{% if 'checkbox' not in field.vars.block_prefixes %}
{{ form_label(field)|raw }}
{% else %}
{% if field.vars.attr['data-label'] is defined %}
{{ field.vars.attr['data-label'] }}
{% else %}
Options
{% endif %}
{% endif %}
</th>
{% endfor %}
{% if allow_delete %}
<th class="align_center">Supprimer</th>
{% endif %}
{% endblock %}
{% block collection_body %}
{% spaceless %}
{% set fieldNum = 1 %}
<tr class="row_to_delete child_collection">
{{ form_errors(body) }}
{% for field in body %}
<td class="field{{ fieldNum }} data-label">
{{ form_widget(field) }}
{{ form_errors(field) }}
</td>
{% set fieldNum = fieldNum + 1 %}
{% endfor %}
{% if allow_delete %}
<td class="align_center align_middle"><i class="fa fa-times"></i></td>
{% endif %}
</tr>
{% endspaceless %}
{% endblock %}
The code I'd like to use and which is not working:
The view stays the same
{# From file myview.html.twig #}
{% form_theme form ':Model:prototype_table_collection.html.twig' %}
{% form(form) %}
Here I am trying to externalize the code from within the first block
{# From file ':Model:prototype_table_collection.html.twig' #}
{% block collection_widget %}
{{include(':Model:collection_widget.html.twig')}}
{%end block%}
{% block collection_header %}
{#stays the same as the previous code for this block. It is called by the block collection_widget #}
{%end block%}
{% block collection_body %}
{#stays the same as the previous code for this block. It is called by the block collection_widget #}
{%end block%}
The new externalized file :
{#From file ':Model:collection_widget.html.twig' #}
{# Here I put the same exact code as I had before inside the block collection_widget, I'm not changing the code, I'm just trying to externalize this part #}
The include does not work, my collection does not load.
I have tried with extending a layout, it doesn't work either.
Example :
{# From file ':Model:prototype_table_collection.html.twig' #}
{% extends :Model:parent.html.twig' %}
{% block content %}
{% block collection_header %}
{#stays the same as the previous code for this block. It is called by the block collection_widget #}
{%end block%}
{% block collection_body %}
{#stays the same as the previous code for this block. It is called by the block collection_widget #}
{%end block%}
{%end block%}
and the parent :
{# From file ':Model:parent.html.twig' #}
{% block collection_widget %}
{# same code as brefore #}
{%end block%}
{% block content %}
{% endblock %}
How can I avoid repeating this {% block collection_widget %} code in every form template where I use it ?
I believe you are looking for the horizontal reuse functionality:
Horizontal reuse is an advanced Twig feature that is hardly ever needed in regular templates. It is mainly used by projects that need to make template blocks reusable without using inheritance.
Just include the use tag in the main template:
{# :Model:prototype_table_collection.html.twig #}
{% use ':Model:collection_widget.html.twig' %}
{% block collection_header %}
{# code #}
{%end block%}
{% block collection_body %}
{# code #}
{%end block%}
Then define the collection_widget block as if it was inside of the prototype_table_collection.html.twig file in the first place:
{# :Model:collection_widget.html.twig #}
{% block collection_widget %}
{# code #}
{% endblock %}
I'm learning symfony2 and I came across this problem.
I have index.html.twig in my "ShopBundle", which look like this:
{% extends '::base.html.twig' %}
{% block body %}
<div class="container">
<h2>Index template</h2>
<div class="col-md-6">
{% block login %}{% endblock %}
</div>
<div class="col-md-6">
{% block profile%}{% endblock %}
</div>
</div>
{% endblock %}
as you can see I want to display login form and some user info in my index.
Here is my login form twig
{% extends "FOSUserBundle::layout.html.twig" %}
{% trans_default_domain 'FOSUserBundle' %}
{% block login %}
{% block fos_user_content %}
.....
{% endblock fos_user_content %}
{% endblock login %}
and here is my profile info twig
{% trans_default_domain 'FOSUserBundle' %}
{% block profile %}
{% block fos_user_content %}
...
{% endblock fos_user_content %}
{% endblock profile %}
And here is my file structure
So why it's not working? What am I doing wrong? Is it even possible to do it like this?
Thank you very much for all advices.
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.