Use array variable to load javascripts at bottom of the page - symfony

I would like to use a kind of global array to define what javascripts should be loaded at the end of the page. This way, I can dynamically add javascript files.
The problem is that the base template is being rendered first. Imagine this base html:
{% set javascriptList = [ 'js/vendor/jquery-1.10.1.min.js', 'js/vendor/bootstrap.min.js', 'js/main.js' ] %}
<!DOCTYPE html>
<head>
<title>my project</title>
</head>
<body>
{% block container %}
content goes here
{% endblock %}
{% block javascripts %}
{% for js in javascriptList %}
<script src="{{ asset(js) }}"></script>
{% endfor %}
{% endblock %}
</body>
</html>
Then I would have a page, something like this:
{% extends base.html.twig %}
{% block container %}
<h1>Demo</h1>
Bla bla
Code I want to reuse:
{% include 'code-with-js.html.twig' %}
{% endblock %}
Then my code-with-js.html.twig would be:
<div id="DemoContent">
Some content, with a tooltip thingy maybe.
</div>
{% set javascriptList = javascriptList|merge(['js/tooltip.js']) %}
So, using this setup, I can make sure that the correct javascript is being added, when the piece of html is being included.
But, this doesn't work of course. The base html is rendered first, so the element will be added to the javascriptList array after it has been rendered. My approach must be wrong.
In my project this reusable code is actually a form with some extra buttons that insert content to the textarea's (so a tinyMCE, but much much much more simplistic). I would like to reuse this code on several pages (create, update).
Any thoughts are welcome!

First of all I recommend you to add a javascript block in your base.html.twig :
You can add a block in your base.html.twig after your script load :
{% block javascripts %}
{% for js in javascriptList %}
<script src="{{ asset(js) }}"></script>
{% endfor %}
{% endblock %}
<script type="text/javascript">
{% block afterJavascriptLoad %}
{% endblock %}
</script>
With inheritance you'll be able to execute javascript after all script load in nested template :
{% extends base.html.twig %}
{% block afterJavascriptLoad %}
//Your code to be executed after base.html script load
{% endblock %}
Having html code and javascript code is not a very good practice. For exemple if you have 3 tinyMCE redactor in your page, you'll load 3 times tinyMCE.
For me the best way is to have your template like this :
base.html.twig:
{% block container %}
content goes here
{% endblock %}
{% block javascripts %}
{% for js in javascriptList %}
<script src="{{ asset(js) }}"></script>
{% endfor %}
{% endblock %}
{% block afterJavascriptLoad %}
//Your code to be executed after base.html script load
{% endblock %}
pageWithTinyMCE.html.twig :
{% extends base.html.twig %}
{% block container %}
<h1>Demo</h1>
Bla bla
Code I want to reuse:
{% include 'code-without-js.html.twig' %}
{% set javascriptList = javascriptList|merge(['js/tooltip.js']) %}
{% endblock %}
{% block afterJavascriptLoad %}
//Custom javascript for the page
{% endblock %}

Related

How to exclude a twig partial from a base extend in Symfony on some pages only?

Is there a way to specify an "extend" in Twig to exclude one of its included partials ?
To better explain myself, here is my base.html.twig
<body>
{% include '/main/_navbar.html.twig' %}
{% block body %}
{% for flashError in app.flashes('success') %}
<div class="alert alert-success" role="alert">{{ message }}</div>
{% endfor %}
{% endblock %}
{% include '/main/_footer.html.twig' %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="{{ asset('script/app.js') }}"></script>
</body>
On my login page, I do not need my _navbar.html.twig partial. Is there a way to not include (exclude) it knowing my view extends from this base template ? Are there any "options" I could pass behind that extends ?
This is the code I use to extend my base template on my login page :
{% extends 'base.html.twig' %}
Just wrap the include you don't want to include in a seperate block, then override the block with empty content, e.g.
base.html.twig
<body>
{% block nav %}
{% include '/main/_navbar.html.twig' %}
{% endblock %}
{% block body %}
{% for flashError in app.flashes('success') %}
<div class="alert alert-success" role="alert">{{ message }}</div>
{% endfor %}
{% endblock %}
{% include '/main/_footer.html.twig' %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="{{ asset('script/app.js') }}"></script>
</body>
login.html.twig
{% extends "base.html.twig" %}
{% block nav %}{% endblock %}
demo

Twig javascript in included child

I have a question regarding adding javascript to child elements. How do you do that?
I have this setup:
base.html.twig:
{% block content %}
{% endblock content %}
{% block script %}
{% endblock script %}
index.html.twig:
{% extends base.html.twig %}
{% include intro.html.twig %}
{% block content %}
<html></html>
{% endblock content %}
{% block script %}
<script></script>
{% endblock script %}
intro.html.twig:
{% block script %}
<script></script>
{% endblock script %}
I want to add more javascript files into the intro.html.twig file, but it doesn't append it to the script block in index.html.twig.
All help appreciated!
UPDATE
I want to send some parameters with intro.html.twig:
{% extends 'intro.html.twig' with {
'title': 'title',
'type': 'test',
} %}
is this possible using extends, or can I only use with with include?
index.html.twig
{% extends intro.html.twig %}
{% block content %}
<html></html>
{% endblock content %}
{% block script %}
{{ parent() }}
<script></script>
{% endblock script %}
intro.html.twig
{% extends base.html.twig %}
{% block script %}
<script></script>
{% endblock script %}

Symfony2 - How to reference files from the web directory with {% javascripts %} in twig?

I'm using libraries like amcharts that have many js files.
I put amcharts in web/js/amcharts as suggested julesbou and then
{% block javascripts %}
{{ parent() }}
{% javascripts '/js/amcharts/amcharts.js' %}
<script src="{{ asset_url }}" type="text/javascript"> </script>
{% endjavascripts %}
{% endblock %}
But I get [exception] 500 | Internal Server Error | RuntimeException
[message] The source file "/amcharts/amcharts.js" does not exist.
Edit
To include a file under web try removing the forward slash from your path i.e.
{% javascripts 'js/amcharts/amcharts.js' %}
^ remove / here
Suppose you have put your javascripts sources named xyz.js in 'js/' folder is source then you will have to fetch this javascripts via your "app/resources/base.html" file loke this :
{% block javascripts %}
<script src="{{ asset('js/xyz.js') }}" type="text/javascript"> </script>
{% endblock %}
and when in remaining twig file you extends base.html.twig like below :
{% extends '::base.html.twig' %}
{% block display %}
{% endblockdisplay %}
it automatically fetch your javascripts . Even if you want to modify the javascripts code for that particular child twig then you will have to write like as :
{% extends '::base.html.twig' %}
{% block display %}
// Your display content here
{% endblockdisplay %}
{% block javascripts %}
{{ parent() }}
// Your changing code here
{% endblockjavascripts %}

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)

Twig: head blocks control from different controllers

I use symfony 2.0.9.
Code of base.html.twig:
<html>
<head>
<title>title</title>
{% block stylesheets %}
<link href="{{ asset('css/main.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}
{% block javascript %}
{% endblock %}
</head>
<body>
<div class="sidebar">{% block sidebar %}{% endblock %}</div>
<div class="content">{% block content %}{% endblock %}</div>
</body>
My Bundle have own layout.html.twig, which being extended by Controllers, for example PostController.php with action showAction.
Code of layout.html.twig
{% extends '::base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
...something
{% endblock %}
{% block navigation %}
...list of menus
{% endblock %}
{% block sidebar %}
{% render "DevMyBundle:Page:sidebar" %}
{% endblock %}
How can access to block 'javascript' in base.html.twig from sidebar.html.twig, which rendered by PageController like this in layout.html.twig: {% render "DevMyBundle:Page:sidebar" %} or How can i reorganize structure of my templating.
What for?: There are more blocks in sidebar may be. Each block call its own js. How? Thanks for advance.
Update: please, guys, help. There should be a simple answer. I have read twig docs, but im newbe in it. If i post this question not correctly, ask me.
One way would be to move the sidebar javascrpt to it's own template.
Something like this in layout.html.twig
{% block sidebar %}
{% render "ZaysoArbiterBundle:Test1\\Main:sidebar" %}
{% endblock %}
{% block javascript %}
{{ parent() }}
<script>Some layout javascript</script>
{% render "ZaysoArbiterBundle:Test1\\Main:sidebarjs" %}
{% endblock javascript %}

Resources