Cleanly including css/js required by content in a macro? - symfony

I have a page template like so:
{# page.twig #}
{% import "_widgets.twig" as widgets %}
{% include '_header.twig' %}
<body>
{{ widgets.fancy_widget(record.items) }}
{# more content goes here #}
</body>
_header.twig contains the <head> tag and some blocks for css and javascript:
{# _header.twig #}
<!DOCTYPE html>
<head>
{% block javascripts %}
{% endblock %}
{% block stylesheets %}
{% endblock %}
</head>
_widgets.twig contains a macro which generates some markup
{# _widgets.twig #}
{% macro fancy_widget(fanciful_items) %}
{# insert special css and js into <head> only if the macro is used #}
{% block stylesheets %}
<link rel="stylesheet" href="css/some_fancy_widget.css">
{% endblock %}
{% block javascripts %}
<script src="js/some_fancy_widget.js"></script>
{% endblock %}
{% for item in fanciful_items %}
{# output some fancy markup #}
{% endfor %}
{% endmacro %}
What I'd like to do is add the widget css/js to the blocks in _header.twig if the macro is called. Ideally they'll only be added once, so multiple calls won't create extra <link> and <script> tags.
Is this possible? Or is there a better way to accomplish this?

I would say that you are not using Twig correctly.
In fact your page.twig must extends base.html.twig.
{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
Then your page.html.twig must extends this base.html.twig
You cannot define or overide blocks in macros.
The simplest way will be:
In your page.html.twig:
{% extends 'base.html.twig' %}
{% import "_widgets.twig" as widgets %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="css/some_fancy_widget.css">
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="js/some_fancy_widget.js"></script>
{% endblock %}
{% block body %}
{% endblock %}
and the rest (Your macro) :
_widgets.twig :
{# _widgets.twig #}
{% macro fancy_widget(fanciful_items) %}
{% for item in fanciful_items %}
{# output some fancy markup #}
{% endfor %}
{% endmacro %}

Related

Twig template parent() caused double parent block

I have a base.html.twig template.html.twig and dashboard.html.twig.
Dashboard extends template which extends base.
Base:
{% block javascripts %}
<script src="1.js"></script>
{% endblock %}
Template:
{% block javascripts %}
{{ parent() }}
<script src="2.js"></script>
{% endblock %}
Dashboard:
{% block javascripts %}
{{ parent() }}
<script src="3.js"></script>
{% endblock %}
This templating setup resulted in redundant script tags where everything is doubled like this
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
Am I missing something? Why is it happening?
Edit1:
base.html.twig:
extends nothing
template.html.twig:
{% extends 'base.html.twig' %}
default/dashboard.html.twig:
{% extends '::template.html.twig' %}
In my opinion your {% extends "file" %} is bad.
The fix :
Template :
{% extends "base_path" %}
{% block javascripts %}
{{ parent() }}
<script src="2.js"></script>
{% endblock %}
Dashboard :
{% extends "template_path" %}
{% block javascripts %}
{{ parent() }}
<script src="3.js"></script>
{% endblock %}
A bit late but since there's no accepted answer yet, I'll give it a go.
I was experiencing the same and this solved it for me, in my case it had to do with the structure of the base template and the current template from where you're trying to extend the base file, in my case, it was, in a similar situation as yours as far I can tell from your code.
Basically the structure in this example is wrong:
base:
{% block notAddedInTemplate %}
{% block javascripts %}
<script src="1.js"></script>
{% endblock javascripts%}
{% endblock notAddedInTemplate %}
Template:
{% extends 'base.html.twig' %}
{% block javascripts %}
{{ parent() }}
<script src="2.js"></script>
{% endblock javascripts%}
And should be:
base:
{% block notAddedInTemplate %}
{% block javascripts %}
<script src="1.js"></script>
{% endblock javascripts%}
{% endblock notAddedInTemplate %}
Template:
{% extends 'base.html.twig' %}
{% block notAddedInTemplate %}
{% block javascripts %}
{{ parent() }}
<script src="2.js"></script>
{% endblock notAddedInTemplate %}
I hope this will help you solve your issue.

Twig blank page ( using 3 templates model and inheritance mechanism)

I'm following a tutorial about symfony2 framework ..
I've already managed to render a template which inherits from the base layout as follows
# ::layout.html.twig > MyvendorBlogBundle:blog:index.html.twig
# then inside the controller action, call
$this->render('MyvendorBlogBundle:Blog:index.html.twig', ['foo'=>'bar']);
I decided to add an intermediate template in between but I'm having trouble to make twig templating engine to render my 3 layouts cascaded templates: As mentioned above, i'm not able to get any output using the following schem but a blank page instead (no raised exceptions and the source only contains the base.html.twig code)
# ::layout.html.twig > MyvendorBlogBundle::layout.html.twig > MyvendorBlogBundle:blog:index.html.twig
Here are my templates
base (app/Ressources/views):
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
<div id="app-container">
<header id="app-header">
</header>
<div id="app-page-content">
{% block body %}{% endblock %}
</div>
</div>
{% block javascripts %}{% endblock %}
{# FLASH BAG HANDLING #}
{% for label, flashes in app.session.flashbag.all %}
{% for flash in flashes %}
<div class="alert alert-{{ label }}">
{{ flash }}
</div>
{% endfor %}
{% endfor %}
</body>
</html>
Then the blog bundle layout (src/Myvendor/BlogBundle/Resources/views):
{% extends "::base.html.twig" %}
{% block body %}
{% block side %}
<div id="blog-side">
<nav>
1rst link
</nav>
</div>
{% endblock %}
{% block main %}{% endblock %}
{% endblock %}
Now the index page template (src/Myvendor/BlogBundle/Resources/views/Blog):
{% extends "MyvendorBlogBundle::layout.html.twig" %}
{% block main %}
<h1>{{ caption }} !</h1>
{% if caption is not empty %}
{{ caption }}
{% endif %}
{% endblock %}
And finally the controller
namespace Myvendor\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class BlogController extends Controller
{
public function indexAction()
{
return $this->render('MyvendorBlogBundle:Blog:index.html.twig', ['caption'=>'hello!']);
}
}
May someone spot what's going on here ?
The pb maybe obvious but I've been stuck for a couple of hours now and blocks nesting seems perfect to me :/

Using TWIG variables from children template

Need to use a variable that is in the parent template.
I have layout:
<html>
<head>
</head>
<body>
{%block content %}
{% set com = 0 %}
DEFALUT CONTENT
{% endblock %}
</body>
</html>
And children template:
{% extends '::layout.html.twig' %}
{% block content %}
HOW USE LAYOUT VARIABLE HERE? LIKE: {{ com }}
{% endblock %}
Thanks in advance!
Use the parent() twig function.
{% extends '::layout.html.twig' %}
{% block content %}
{{ parent() }}
{{ com }}
{% endblock %}
The above code will render a template like :
<html>
<head>
</head>
<body>
{% block content %}
{% set com = 0 %}
DEFAULT CONTENT
{{ com }}
{% endblock %}
</body>
</html>

Use array variable to load javascripts at bottom of the page

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 %}

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