How to push values to array in templates and partials to output them in layout? - twig

We are using Twig as our templating engine but I'm stuck with a problem of the rendering order now. See - https://straightupcraft.com/articles/twig-processing-order
We are using amp for our new site and with amp and web components in general it is necessary to import the components (once) to use them. So the idea was to create a Twig Extension to accomplish exactly this. Everytime a component is used it is called via this twig function call. The function is checking the component, adding it to a stack and you can use it from now on. The stack then is transformed into a list of imports for the head of the page.
The problem here is Twigs top to bottom rendering. When I call said function in a partial or a template the rendering of "layout.twig" already took place and all function calls below/after are not affecting the stack.
Is there an option to change the rendering to inside out, so that the rendering is first using the lowest partial and "bubbles" up to templates and finally layout without them rendering before?
Another way to get the values in layout would nice as well.
Desired behavior:
Layout.twig
{{ getAllComponents() }}
{# should output component1 and component2 #}
{% block content %}
Page.twig
{% extends Layout.twig %}
{{ addComponent('component1') }}
{% include partials.twig %}
<component1>...</component1>
partials.twig
{{ addComponent('component2') }}
<component2>...</component2>
Actual behavior:
Layout.twig
{{ getAllComponents() }}
{# is empty #}

There is no option to change this behavior. However you can use a trick to archive this:
Render your content to a variable before you call getAllComponents()
layout.twig
{% set mainContent %}
{% block content %}{% endblock %}
{% endset %}
{{ getAllComponents() }}
{{ mainContent }}

There actually is a magical solution to this problem -- some genius wrote this Deferred Twig Extension which lets you run some code (output a variable, call a function, etc) in a block near the top of your layout, but wait until the rest of the twig templates have parsed before executing them. There's an example on that github page showing how you can achieve this.

As far as I know, no you cannot switch the rendering of a TWIG template.
However, I would recommend possibly looking at the {% embed %} or {{ render }} tags.
Embed allows specific TWIG files to be placed within others with variables, so you could pass each component into their relevant partials.twig file.
Render allows the use of embedding a separate controller action, so you create a ControllerAction that took in a component variable and renders its own TWIG file.
Documentation on Embed: https://twig.sensiolabs.org/doc/2.x/tags/embed.html
Documentation containing Render: https://twig.sensiolabs.org/doc/2.x/api.html

Related

Choose twig block process order

I want to make modular css load with instruction in twig
{{ addCss("csspath") }}
{# rest of template #}
and then write all css in a twig block in my html header like this :
{% block css %}
{{ writeCss() }}
{% endblock %}
My custom function work well, all css store with addCss will be write by writeCss but I have some problem with twig processing order. Css needs to be in head html tag and because of that twig process writeCss before all my addCss call.
I've made some test to guess twig process order by i have no ideo of how control it.

Load extends after blocks

So I have been working with twig now, and I use a function manner to render all of my html. My question is, is there a way to render the blocks (with functions) first, and then the {% extends X %} afterwards?
example:
{% extends 'base.html.twig' %}
{% block body %}
{{ resource('script', 'google.com') }}
{% endblock %}
I would want the resource function to trigger, before the file is extended, in some way.
Reason why:
In the base I define another function which loads a head with a base configuration, it also gives easy options to load certain libraries such as bootstrap etc. But I want the user able to later, externally in a body add more resources and make them pop up in the head instead of in the body. I can give more code if needed but I think this will suffice since this is more a twig question than a php question.

Grav CMS: how to show/hide parts of the page depending on conditions?

The Grav's documentation clearly describes how a whole page or a folder could be hidden from unregistered users. It also describes how a whole page could be seen only by particular user groups.
But what about pieces of a page, let's say, some links or a private info I want to show on some conditions?
Ok, for registered users I found a snippet at Login plugin docs:
{% if grav.user.authenticated %}
content for registered users goes here
{% endif %}
But going wider - how can I show/hide pieces of a particular page depending on some custom logic in PHP code, i.e. not necessarily user related?
I'm thinking about a twig/shortcode plugin, something like:
{% if some.custom.condition.or.PHP.function %}
hidden content goes here
{% endif %}
or
[hidden_if_something] hidden content goes here [/hidden_if_something]
But not sure how exactly this should be implemented. So working examples would be appreciated. Thanks.
There is a recipe in the Grav documentation here. This provides an example of how to render the output of a PHP code result in a twig template.
In the example they create a plugin, and implement a twig extension providing access to a php function. They can then simply call that php function like in a twig template.
{{ example() }}
Following that example, you can implement whatever logic you would like in php, and call the function in a twig if statement.
{% if example() == true %}
your conditional output
{% endif %

symfony3 override fields theme from vich bundle

I have installed VichUploaderBundle into my Symfony app and would like to have better control of the appearance of the upload buttons. I have taken the contents of fields.html.twig located in the bundle itself, and copied them into the same file at the path app/Resources/views/myControllerName/fields.html.twig This is also where all the other twig templates are for that set of controllers.
I can not seem to affect anything on the page through this method. Yes I have cleared the cache, I have even removed the cache directory.
Am I placing the file in the wrong place?
You can override that form theme in many ways, just when you customize vich_file_widget and/or vich_image_widget e.g.:
{% extends 'base.html.twig' %}
{% form_theme form _self %}
{% block vich_file_widget %}
{# some content… #}
{% endblock %}
{% block content %}
{{ form(my_form) }}
{% endblock %}
Thanks for the effort #jkucharovic, I was already using the 3rd method you were suggesting. My question was whether or not I put the fields.html.twig file in the correct place. Turns out I did not. For others that come along with this question, to override the fields file in the VichUploaderBundle for twig you must put a copy of the fields.html.twig file at the following path so that Symfony can override what comes with the bundle.
the fields file is found here and should not be edited...
vendor/vich/uploader-bundle/Resources/views/Form/fields.html.twig
a copy of that file should be placed here in Symfony 3. This file is editable.
app/Resources/VichUploaderBundle/views/Form/fields.html.twig

Combining optional block with element in twig

In defining a base template in twig, I want to reserve an area for special notifications, using a block. It could be an extra sidebar that may contain all kinds of things (calendar, or some extra information, whatever.) The default should be emtpy, but any child template may extend and fill it.
Now I want to have all those extensions be included in a <div class='special-sidebar'> if the block is extended, and not show anything if it is not included. Is there any way to define the containing element in the base template?
{# Base template #}
Content etc...
{% block special %} {# may be overridden by child template #}{% endblock %}
{# child template #}
{% block special %} Here, the special sidebar is filled! {% endblock %}
The base page should show just the content:
// base template:
Content etc...
And the child page:
// child template
Content etc...
<div class"special-sidebar"> Here, the special sidebar is filled! </div>
Where and how do I put the html for this? I could define it in every child, but that means you have to remember to use the correct html each time, I'd rather set this in the base template and not bother with it. But I don't want empty elements in my page if the block is not overridden.
You can solve it like this, if you want to display a certain block only if it has content. Hope, this is what you're looking for.
Example index.html.twig
{% set _block = block('dynamic') %}
{% if _block is not empty %}
{{ _block|raw }}
{% endif %}
Example part.html.twig
{% extends "index.html.twig" %}
{% block dynamic %}
Block content goes here.
{% endblock %}
Source: How to check if a block exists in a twig template - Symfony2

Resources