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
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') %}
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.
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} %}
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.
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)