1

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?

gandalf3
  • 1,636
  • 4
  • 24
  • 40

1 Answers1

3

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 %}
Ajir
  • 215
  • 1
  • 10
  • Thanks for the pointers. Is there really no proper way to conditionally include js/css only when needed? It seems a waste to include all assets in *every* page, regardless of whether or not the page make use of them (most pages will not have any fancy widgets). – gandalf3 Feb 29 '16 at 08:49
  • Unfortunately, as far as I know, this is the best way to do it. But your JS and CSS for the fancy widgets are loading ONLY when you are importing the macro. That is, when viewing page.html.twig, you will have your CSS, JS N WIDGET. But when you view another page (about.html.twig) which extends base.html.twig, it wont have the CSS, JS and the fancy widget. – Ajir Feb 29 '16 at 12:40
  • Ah I see, that makes sense. That works for my current dillema well enough, but I think I'll hold out on accepting for a bit in case there is a way that doesn't involve including the widget css/js and the widget markup in separate places. Thanks – gandalf3 Mar 01 '16 at 03:33