Symfony2 Twig: embedding a template - Where to put JS - symfony

On some of my pages I want to embed a toolbar with some generic functionality like sharing, reporting and bookmarking. Currently I do it like this
{# page-detail.html.twig #}
{% extends 'layout.responsive.html.twig' %}
{% block content %}
{% include "toolbar/index.html.twig" with {'foo':'bar'} %}
{# further content... #}
{% endblock %}
As the features of the toolbar require some information about the context it's embedded in, I pass this information in the with-clause. So far so good.
Now, all the buttons are invoking some JavaScript on clicking. Modal windows are popping up, dialogs are displayed, etc. I would like to put the related JS inside the toolbar template but here is the problem:
All my JS-libraries are loaded at the bottom of the page before the closing body-tag. Means, in my toolbar template I have no access to them.
So I thought of the following solutions which I am not really happy with:
move the inclusion of the JS-libraries to the top (loading js slows down page display, also JS would be scattered)
put the respective JS in the javascripts-block in "page-detail.html.twig" (toolbar code is separated from the template code and mixed with the JS-Code needed there)
use the "embed"-Tag and define a JS-Block in the toolbar-Template (won't help as I can access the blocks in the embedding parent template only within the "embed"-Tag)
use the "use"-Tag instead. Well that sounds best so far, however, Code would look like this (assuming toolbar-template defines the blocks "toolbar_content" and "toolbar_js"):
Code:
{# page-detail.html.twig #}
{% extends 'layout.responsive.html.twig' %}
{% use "toolbar/index.html.twig" %}
{% block content %}
{% block toolbar_content %}
{{ parent() }}
{% endblock %}
{# further content... #}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{% block toolbar_js %}
{{ parent() }}
{% endblock %}
... local JS
{% endblock %}
Also in this solution I cannot pass the environment variables anymore using the "with"-clause as it is now used for resolving name-conflicts of the blocks.
I feel like I am missing something. How is it done right? It seems like such a common task.
Additional information
I think not essential for the question but for more clarity, here is how my layout template looks like (not complete just the relevant blocks).
{# layout.responsive.html.twig #}
{% block stylesheets %}
{# bootstrap.css, custom.css, ... #}
{% endblock %}
{% block content %}
{% endblock %}
{% block javascripts %}
{# jquery, bootstrap, ... #}
{% endblock %}
And my toolbar:
{# toolbar/index.html.twig #}
<div class="toolbar btn-toolbar" role="toolbar">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#report">Report</button>
</div>
<!-- more buttons -->
</div>

Related

Adding page specific js using timber twig

I have a twig file that extends base
{% extends "base.twig" %}
{% block content %}
<div> all articles </div>
{% for blog in blog_articles %}
<div><i> {{blog.post_title }}</i></div>
{% endfor %}
{% endblock %}
base.twig has an include for my global js and in my article.twig above i want to be able to add template specific JS that will appear below the global js that is in an include in base.twig
Can this be done?
To answer my own question i did this
in the base.twig
{% include 'globaljs.twig' %}
{% block javascript %}{% endblock %}
Then in the article.twig where i call the javascript block i just add my page scripts. simple

Rendering a block in extended -> included template

Problem
In navbar.html.twig I have a block that looks like this:
//navbar.html.twig
{% block back_link %}{% endblock %}
This navbar is included by my base.html.twig.
//base.html.twig
{% include navbar.html.twig %}
And then my page template extends the base.
//page.html.twig
{% extends base.html.twig %}
...
{% block back_link %} Things i want in the navbar. {% endblock %}
But the things I want in the navbar don't show up in the navbar, because it's included by the base, so there's no parent/child relationship there.
Question
What's a good (or any) way to let me override a block in an included template in an extended template?
if you understand your idea right, you want to have some reusable content for a navbar, separated to navbar.html.twig that will possible to use in several templates?
So you can do it with "use" http://twig.sensiolabs.org/doc/tags/use.html
In navbar.html.twig
//navbar.html.twig
{% block back_link %}{% endblock %}
In base.html.twig.
//base.html.twig
{% use '::navbar.html.twig' %}
{{ block('back_link') }}
In page.html.twig
{% extends '::base.html.twig' %}
{% block back_link %} Things i want in the navbar. {% endblock %}

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)

Problems with include

i have a lot of time programming in PHP, but im doing my first steps in Symfony.
Im try write Twigg templates, i have a public template in app/Resources/view/public.html.twig.
This file contains: http://pastebin.com/T1KGMfXL.
Now, in CloudBundle, have a base.html.twig:
{% extends '::public.html.twig' %}
{% block main %}
<div class="login_page">
<div class="login_box">
{% block content %} {% endblock %}
</div>
</div>
{% endblock %}
And the content in another twig file. For example, login.html.twig
{% extends 'base.html.twig' %}
{% block content %}
....
{% endblock %}
In the Controller, when an user try http://cloud.man.local/app.php/login:
public function staticAction($sitio)
{
// in this case, $sitio contains "login"
return $this->render("CloudBundle:Default:$sitio.html.twig");
}
So, the problem is that, only shows the footer, not show the content.
Any ideas ?.
You want to override a block which is inside another block, so in your case I think you should try this in the another twig files:
{% block main %}
{{ parent() }}
{% block content %} YOur content {% endblock %}
{% endblock %}
see http://twig.sensiolabs.org/doc/functions/parent.html
Hope it's helpful.
Best regard.

Symfony2 Assetic + Twig Template JavaScript Inheritance

My problem:
I have 3 templates:
main.html.twig (main layout file)
layout.html.twig (a bundle specific layout override which contains some bundle specific JS tags)
create.html.twig (a page specific template file which also contains some page specific JS tags)
I am defining a block called 'javascript' in my base layout (main.html.twig), then overriding it (but calling {{ parent() }} in layout.html.twig. This works fine, and the JS tags from the main template file are still included above those in the layout.html.twig template.
I then do the same in the create.html.twig file, overriding the block as follows:
{% block javascripts %}
{{ parent() }}
{% javascripts '#BundleName/Resources/public/js/application.album.uploader.js'
'#BundleName/Resources/public/js/jquery.uploadify.js'
'#BundleName/Resources/public/js/swfuploadify.js' filter='?yui_js' %}
<script src='{{ asset_url }}' type='text/javascript'></script>
{% endjavascripts %}
{% endblock %}
At this point, instead of just overriding the javascript block in the parent (layout.html.twig) and including all the scripts defined in the templates above it, it does the following:
Dumps the <script> tags in the middle of the output (which causes an error, because in my main.html.twig file I am only including the jQuery library at the end of the HTML markup
Then it also dumps the scripts out along with the rest of the others (as I would expect it to)
I am not sure what is causing the scripts to be dumped in the middle of the create.html.twig template, and I'm also confused as to why they're being dumped to the screen twice (once in the middle of the create and then once at the bottom along with all the rest of my scripts from main.html.twig and layout.html.twig.
Has anyone got any ideas? Let me know if anything is unclear or if I can provide some more information.
EDIT:
File contents are below...
main.html.twig: https://gist.github.com/7f29353eaca0947528ce
layout.html.twig: https://gist.github.com/734947e9118b7765715e
create.html.twig: https://gist.github.com/c60c8d5c61e00ff86912
EDIT 2:
I've been having another look at the issue this morning and it looks as though its doing the same thing for stylesheets. I tried to define a new block called pagescripts in my layout.html.twig and then use the block in my create.html.twig but this had the same outcome, it just seems to dump the scripts and stylesheets wherever I use the
{% block pagescripts %}
(scripts here)
{% endblock}
I found the issue. In create.html.twig I was defining my {% block javascripts %} content inside inside my {% block content %}, so I assume Twig was rendering the output of the javascripts block inside the content block.
Moving the {% block javascripts %} content outside of the {% block content %} block fixed the issue.
Here is an example of main.html.twig:
<body>
{% block stylesheets %}
{% endblock %}
{% block jsscript %}
{% endblock %}
{% block content %}
{% endblock %}
</body>
into your create.html.twig
{% extends '::base.html.twig' %}
{% block jsscript %}
my javascript files...
{% endblock %}
{% block content %}
<h1>Create</h1>
<form action="{{ path('entity_create') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<p>
<button type="submit">Create</button>
</p>
</form>
{% endblock %}
If you still have issue, you can add a document ready function :
$(document).ready(function() {
// put all your jQuery goodness in here.
});

Resources