Twig use function multiple parameter - symfony

I am looking for using function "{% use %}" with multiple parameter in twig template.
all page template
{% use "common.html.twig" with
container as containerParent,
title as titleParent
%}
{# Some code ... #}
common.html.twig
{% block title %}{{ block(titleParent) }} - Admin{% endblock %}
{% block container %}
{# Some code ... #}
{{ block(containerParent) }}
{# Some code ... #}
{% endblock %}
When I tried that, my block container and title are not modified.
Maybe I didn't understood the doc ? http://twig.sensiolabs.org/doc/tags/use.html
I just want have access to function twig {{ parent() }} in my twig file which is included by {% use ... %}
Any suggestion ?
Sorry for my English, I am learning it...

Related

SonataAdminBundle templates - list and show fields template content duplication

My list and show fields contain the same content, but due the extending of base_list_field and base_show_field templates I have to create two separate templates.
list/location.html.twig:
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
{{ object.getCity }}, {{ object.getCountry.getName }}
{% endblock %}
show/location.html.twig:
{% extends 'SonataAdminBundle:CRUD:base_show_field.html.twig' %}
{% block field %}
{{ object.getCity }}, {{ object.getCountry.getName }}
{% endblock %}
As you can see huge part of code is duplicated. Is there a way to check the page I am currently in in twig and then extend certain base template? In this case I would be able to use one file instead of two for same content.
In twig it's possible to extend/include a dynamic template :
{# do test here on which page you are or pass through controller #}
{% if true %}
{% set template = 'SonataAdminBundle:CRUD:base_show_field.html.twig' %}
{% else %}
{% set template = 'SonataAdminBundle:CRUD:base_list_field.html.twig' %
{% endif %}
{% extends template %}
{% block field %}
{{ object.getCity }}, {{ object.getCountry.getName }}
{% endblock %}
(edit) if you don't want the hardcoded if I would pass the template variable through the controller and change the twig template to something like
{% extends template|default('SonataAdminBundle:CRUD:base_show_field.html.twig') %}

Include Twig template which Extends

I am rendering a Twig template as below:
$this->render('base.html.twig');
The content (simplified) of this Twig template looks as below:
{% block headers %}
...
{% endblock %}
{% block pagecontent %}
...
{# I want to include another template (A) here #}
{# I want to include another template (B) here #}
{% endblock %}
{% block footers %}
...
{% endblock %}
I have another Twig template which I am not rendering, but I want to include in the above template (where I have placed my Twig comment). The content is as follows:
{% extends '::base' %}
{% block headers %}
{{ parent() }}
{% endblock %}
{% block pagecontent %}
{{ parent() }}
...
{% endblock %}
I want to eventually render several Twig templates inside of base.html.twig.
Is what I am attempting to do achievable, and if so, how do I achieve it?
You just need to render child template (the one extending base.html.twig).
Change in your controller:
$this->render('child_template_extending_base.html.twig');
Replace child_template_extending_base with your real template name.
You can also embed another controllers views in your template with this code:
{{ render(controller(
'AppBundle:Article:recentArticles',
{ 'max': 3 }
)) }}
Read more about this feature here: http://symfony.com/doc/current/book/templating.html#embedding-controllers
base.html.twig
{% block headers %}
...
{% endblock %}
{% block pagecontent %}
...
{# I want to include another template (A) here #}
{# I want to include another template (B) here #}
{% endblock %}
{% block footers %}
...
{% endblock %}
Your controller:
$this->render('base.html.twig');
Normally, $this->render('view.html.twig'); accepts only one twig.
If you want to have several templates, you can build it like this:
view.html.twig
{% extends '::base' %}
{% block pagecontent %}
{# Controller function with template 1 #}
{{ render(controller('AppBundle:Article:recentArticles',{ 'max': 3 })) }}
{# Controller with template 2 #}
{{ render(controller('AppBundle:Article:relatedArticles',{ 'max': 4 })) }}
{% endblock %}
ANOTHER POSSIBLE SOLUTION IS :
You can break one block into several blocks.

Toggle html validation globally

I've made a couple of twig extensions but I'm stumped on this one.
I have the following template logic that I want to make into an extension.
I need reuse this logic into many different forms instead of copying and pasting the following code everywhere:
{% if html5validation is not defined %}
{{ form_start(some_form) }}
{% else %}
{% if html5validation %}
{{ form_start(some_form) }}
{% else %}
{{ form_start
(
company, {'attr': {'novalidate': 'novalidate'}}
)
}}
{% endif %}
{% endif %}
With the above code from the controller I can do the following to turn the html5 validator on and off:
$this->render(..., array(html5validation => false));
I want put the template logic into the twig extension below...
I just don't know if it's possible to implement what I've done above in a twig extension.
class HTML5Validation extends \Twig_Extension
{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('html5validation', array($this, 'setValidation')),
);
}
public function setValidation($boolean)
{
//Implement the same logic as the twig template.
}
public function getName()
{
return 'html5validator';
}
}
The short answer is no - you can't do this using a twig extension, it's not what they're meant for.
Looking at your template fragment I'd say you need to customise the form_start block. To do this see Symfony Form Theming and How to customise form rendering.
EDIT: This solution does not work if your customised code requires local twig variables - only global twig variables are available for form theming. You can define your own twig globals in config.yml or in a twig extension.
For example, to override form_start globally, you find the default definition of the form_start block in form_div_layout.html.twig, copy it into your own form theme file e.g. YourBundle/Form/fields.html.twig, modify it as required and and update the twig configuration to apply your form theme file. Something like this:
{# src/YourBundle/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{% block form_start -%}
{% if html5validation is not defined %}
{{ parent() }}
{% else %}
{% if html5validation %}
{{ parent() }}
{% else %}
{{ parent
(
company, {'attr': {'novalidate': 'novalidate'}}
)
}}
{% endif %}
{% endif %}
{%- endblock form_start %}
Config:
# app/config/config.yml
twig:
form:
resources:
- 'YourBundle:Form:fields.html.twig'
I actually found a better way to do what I wanted.
As a plus it works globally instead of having to populate more fields into your controller!
In YourBundle/Resources/views/validation.toggle.html.twig
{% extends 'form_div_layout.html.twig' %}
{% block form_start -%}
{% if html5validation is defined and html5validation == false %}
{% set attr = attr|merge({'novalidate': 'novalidate'}) %}
{% endif %}
{{ parent() }}
{%- endblock form_start %}
Then if you want to turn off html5 validation across the whole website:
# app/config/config.yml
twig:
global:
html5validation: false
Or
Even better just use it in your dev_config.yml if you want validation on by default on production mode but the ability to toggle validation on and off for dev mode.
# app/config/dev_config.yml
twig:
global:
html5validation: false
resources:
- 'YourBundle::validation.toggle.html.twig'
Finally use it in your twig template normally:
{% form_theme your_form 'YourBundle::validation.toggle.html.twig' %}
form_start(your_form)
Reusable and non invasive, exactly like I wanted it. :)
I got the hint from:
https://github.com/symfony/symfony/issues/11409#issuecomment-49358377
In the absence of a more elegant solution, you can always put the twig fragment given in your question into a separate file and use twig include from your various forms. The included fragment has access to the variables from the surrounding context:
{# YourBundle/Resources/views/form_start.html.twig #}
{% if html5validation is not defined %}
{{ form_start(some_form) }}
{% else %}
{% if html5validation %}
{{ form_start(some_form) }}
{% else %}
{{ form_start
(
company, {'attr': {'novalidate': 'novalidate'}}
)
}}
{% endif %}
{% endif %}
Then in the twig file for the form:
{% include 'YourBundle::form_start.html.twig' %}
If you typically pass a 'form' variable into render() in your controller(s) then you can use that in your form_start fragment. Otherwise you can pass the appropriate form in as a variable:
{% include 'YourBundle::form_start.html.twig' with {'form': localForm} %}

Twig Custom Extension - call parent() in extending block

is there any way to call parent() function in extending block in the template of the custom extension?
In my base.html.twig I have block footer_javascript and in my custom extension I would like to add some JS to this block but I need to ensure that original block footer_javascript will be extended not overwritten. I tried this:
custom_extension.html.twig:
{# some html/twig code, not really important #}
{% block footer_javascript %}
{{ parent() }}
{# some javascript for this custom twig extension #}
{% endblock footer_javascript %}
but of course, I get
Calling "parent" on a template that does not extend nor "use" another
template is forbidden
base.html.twig - base template with block structure:
{# base html/twig structure, not really important %}
{# block which will be extended/overwriten in templates which extends this base.html.twig #}
{% block footer_javascript %}
{% endblock footer_javascript %}
extended.html.twig - template which extends base.html.twig; in this template I am using custom twig extension:
{% extends "::base.html.twig" %}
{# some html/twig ... #}
{{ custom_extension(entity) }}
{% block footer_javascript %}
{{ parent() }}
{# javascript used for extend.html.twig #}
{% endblock footer_javascript %}
Is there any way to extend base footer_javascript in my custom extension template?
To use parent(), you have to extend the template.
Your custom_extension.html.twig has to extend base.html.twig like extended.html.twig.
EDIT:
If you're using 2 templates with <body>, you don't have to use {{ parent() }}, but instead put your js in a twig that you will include in your block.

Override block within included template in Twig

Currently using Symfony2 and Twig, I'm trying to override block within an included template. Let me explain :
{# base.html.twig #}
{% block content %}{% endblock content %}
<!--Some html Code -->
{% block javascripts %}
<!--Some scripts included like jQuery-->
{% endblock javascripts %}
In a other file:
{# page.html.twig #}
{% extends 'base.html.twig' %}
{% block content %}
{% include 'form.html.twig' %}
{% endblock content %}
And finally:
{# form.html.twig #}
<form method="post" action="something">
</form>
{# I am trying somehow to override the "javascripts" block here,
unfortunately I didn't figured out how to to that
#}
{% block javascripts %}
{{ parent() }}
<!--Some JS here-->
{% endblock javascripts %}
Is there a way to do what I want ?
What you need here is multiple inheritance. But just like php, twig does not have multiple inheritance. And just like php has traits, twig has a palliative for this called use. Remember that twig is compiled to php. I think a block that is used in a use statement ends up compiled in a trait.
First, create your a "trait" with the blocks you want to reuse in different places:
{% block my_form %}
<form method="post" action="something">
</form>
{% endblock %}
{% block form_specific_javascript %}
<!--Some JS here-->
{% endblock}
Then, in your page template, call the "trait", and reuse the blocks:
{# page.html.twig #}
{% extends 'base.html.twig' %}
{% block content %}
{% use 'form.html.twig' %}
{{ block('my_form') }}
{% endblock content %}
{# override the javascript block #}
{% block javascripts %}
{{parent()}}
{{block('form_specific_javascript')}}
{% endblock %}
So as you see, you can't do it all from the form template, you have to do some
wiring in your page template. Still, calling a block is better than copy / pasting
its contents, isn't it ?
Since i was looking for the same answer and actually found the solution.
It lies within TWIG with the embed tag.
Available in twig version 1.8, embed tag allows us to "include" a template, which has its own defined blocks who can then be overriden.
More information here: http://twig.sensiolabs.org/doc/tags/embed.html
You could just simply include form.html.twig within the javascripts block
base.html.twig
{% block content %}
<!--Some html Code -->
{% endblock content %}
{% block javascripts %}
<!--Some scripts included like jQuery-->
{% endblock javascripts %}
page.html.twig
{% extends 'base.html.twig' %}
{% block javascripts %}
{{ parent() }}
{% include 'form.html.twig' %}
{% endblock javascripts %}
form.html.twig
<!--Some JS here-->
I have exactly the same problem. I was looking for solution, but I didn't find any. Unfortunately include, embed and use does not solve this problem. But I figured two possible workarounds.
Option 1 (simpler, but needs more code)
Separate form into two files _form.html.twig and _form_js.html.twig and import them in the appropriate blocks.
Option 2
Invert hierarchy of templates. Extend form directly from base.
# form.html.twig
{% extends 'layouts/base.html.twig' %}
{% block body %}
{{ block('page_header') }}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
# ...
</script>
{% endblock %}
And then extend form in other templates like new and edit.
# new.twig.html
{% extends 'form.html.twig' %}
{% block page_header %}
# custom content here
{% endblock %}
Using your own files, this is the approach I used and it worked for me:
{# base.html.twig #}
{% block content %}
{% endblock %}
<!--Some html Code -->
{% block javascripts %}
<script>console.log('1st print')</script>
{% endblock %}
In other file:
{# page.html.twig #}
{% extends 'base.html.twig' %}
{% block content %}
<h1>Welcome to page.html.twig.</h1>
<p>Here is a form for you to complete:</p>
{{ block("content", "form.html.twig") }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>console.log('2nd print')</script>
{{ block("formjavascripts", "form.html.twig") }}
{% endblock %}
And finally:
{# form.html.twig #}
{% block content %}
<form method="post" action="something">
</form>
{% endblock %}
{% block formjavascripts %}
<script>console.log('3rd print')</script>
{% endblock %}
Please notice the use of block function, you can find more info here https://twig.symfony.com/doc/3.x/functions/block.html
Also notice I used "formjavascripts" you could have used "javascripts" as well because that is the block name being retrieved from forms.html.twig file.
Note: You could try using Block function again in forms.html.twig and see if you can do a 4th print and call a 4th twig file! (Not sure if this last thing will work though :P)

Resources