Symfony Twig block generation priority - twig

I have a little problem in twig to render symfony forms,
so, first I will explain the context of it.
In Twig, blocks are defined in a certain order on a template:
{# base.layout.html.twig #}
{% block firstBlock %}
{% endblock firstBlock %}
{% block secondBlock %}
{% endblock secondBlock %}
And when we extends this template we can write this:
{# child.layout.html.twig #}
{% embed "base.layout.html.twig" %}
{% block secondBlock %}
{{ form_widget(form.submit_button) }}
{% endblock secondBlock %}
{% block firstBlock %}
{{ form_widget(form.some_field) }}
{{ form_rest(form) }}
{% endblock firstBlock %}
So the problem is with the form() functions in twig which renders Symfony forms.
I am trying to generate a submit button at the very end of a modal window
but the problem is that form_rest() renders all parts of the form not already rendered.
There is a little fix to avoid form_rest to render form.submit_button, it's to set form.submit_button as an already rendered field with:
{% do form.submit_button .setRendered %}
But with this form.submit_button is never rendered
Of course the finality of all this is to don't remove the form_rest instruction.
So I search something to unset rendered value of form.submit_button after the form_rest instruction or even better a way to choose the order of blocks generation of a template.
Like this:
{% block secondBlock with(1) %}
{{ form_widget(form.submit_button) }}
{% endblock secondBlock %}
{% block firstBlock with(2) %}
{{ form_widget(form.some_field) }}
{{ form_rest(form) }}
{% endblock firstBlock %}
Thank you for your help !

If it is just for a submit button, you can remove it from your form type and write it in html, it is'nt a problem, and it will work.
The problem is that you render first the rest (including the button), and after you try to render the button but it is already rendered...
Of course, your html button must be inside < form > tags.
If you want to keep your submit button from your form type,
1 solution is to move it using some javascript ...
2 solution is to mark it as rendered (as you have done), and write the exact html of your submit button in the place you want.
[edit]
You can also render every field of your form in the first block, render your submit button in the second block, and put form_rest after all these (as all the fields have been rendered, it will not have something to render,
this is a solution also)

If that's just a matter of a submit button that has no logic and no mapping at all, the easiest way is to render your submit button in HTML directly and removing it from your form.
<input type="submit" value="Submit"> {# ¯\_(ツ)_/¯ #}
Another solution is to put your modal in a modal_layout.html.twig, and then use the embed tag that is exactly made for this kind of issues.
modal_layout.html.twig
<div class="modal">
<div class="form">
{% block form_part %}{% endblock %}
</div>
<div class="submit pull-right">
{% block submit_part %}{% endblock %}
</div>
</div>
your_page.html.twig
{% embed 'modal_layout.html.twig' %}
{% block submit_part %}
{{ form_row(form.submit_button) }}
{% endblock %}
{% block form_part %}
{{ form(form) }} {# won't render submit again #}
{% endblock %}
{% endembed %}
But IMO the second solution, even if it appears cleaner, does not worth it for a stupid submit button.
Cheers

Related

Using include disable's add-to-cart button in products pages in opencart 3.0.2

In the product.twig file, i need to include another twig file , the file gets linked and working, but that disables the add-to-cart button in every product page
{{ header }}
<div id="product-product" class="container">
<ul class="breadcrumb">
{% for breadcrumb in breadcrumbs %}
<li>{{ breadcrumb.text }}</li>
{% endfor %}
</ul>
<div class="row">{{ column_left }}
{{ include('default/template/extension/total/demo_file.twig') }}
{% if column_left and column_right %}
{% set class = 'col-sm-6' %}
{% elseif column_left or column_right %}
{% set class = 'col-md-9 col-sm-12' %}
{% else %}
{% set class = 'col-sm-12' %}
{% endif %}
Event is bind on the add to cart button. The even code lies in the same file , so make sure the id of the button is same as previous button you are replacing.
Other way could be updating the event bind function jQuery selector.

Multiple blocks in Grav cms twig template

I am making a Grav template from a static html. This is a one-pager, so I am using modular templates and content. The problem is that I have a gallery, and my clicking on an image, a modal pops up. The code for the modals are places outside the container I am using for the image gallery.
Is there a way to have a twig modular template creating output code two different places on the base template? I have tried using multiple blocks in the modular template, but in the final output all code are places inside the image gallery container? My templates have multiple level nesting just to make it a little bit easier:
main
Listing of categories
Listing of images for each category
base.html.twig (simplified):
{% block body %}
{% block modal %}{% endblock %}
{% block content %}{% endblock %}
{% endblock %}
modular.html.twig:
{% extends 'partials/base.html.twig' %}
{% block content %}
{{ page.content }}
{% for module in page.collection() %}
<div id="{{ _self.pageLinkName(module.menu) }}">
{{ module.content }}
</div>
{% endfor %}
{% endblock %}
category-list.html.twig (simplified):
{% block content %}
{% for module in page.collection() %}
{{ module.content }}
{% endfor %}
{% endblock %}
content template (simplified):
{% block modal %}
{{ page.titel }}
{% endblock %}
{% block content %}
{{ page.content }}
{% endblock %}
My problem: rendering the block modal from the content template in the block Modal in the base template. Currently the block modal is rendered in block content.
Any ideas?
Thanks

Extending twig blocks in a complicated scenario

Let's say I have these twig templates:
base.twig
{# base.twig #}
<html>
<head>
{% include 'head_js.twig' %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
head_js.twig
{# head_js.twig #}
{% block headJS %}
<script src='/js/some-script.js'></script>
{% block headJSExtra %}{% endblock %}
{% endblock %}
page.twig (the one loaded by the controller)
{# page.twig #}
{% extends base.twig %}
{% block content %}
<p>Widget 1</p>
{% include 'widget.twig' with { name: 'foo' } %}
<p>Widget 2</p>
{% include 'widget.twig' with { name: 'bar' } %}
{% endblock %}
widget.twig
{# widget.twig #}
{% if wigetAlreadyIncluded is not defined %}
{% block headJSExtra %}
{{ parent() }}
<script src='/js/widget.js'></script>
{% endblock %}
{% set widgetAlreadyIncluded = true %}
{% endif %}
<p>My name is {{ name }}</p>
This code doesn't work (can't use parent() in widget.twig as it's not extending or using any template), but it should illustrate what I'm trying to achieve. The basic idea is:
In order to work, widget.twig requires a js library to be loaded in as a tag in the .
The widget can be rendered several times in one page.
Other widgets should be able to also add their own tags in the in this fashion, but they shouldn't override previous added tags (they should be appended).
I don't want to add more than once any tag required by any widget found in the page.
Any ideas on how can I achieve this would be greatly appreciated. I've read two SO related questions with no luck at all.
https://stackoverflow.com/a/29132604/4949663
https://stackoverflow.com/a/18160977/4949663
I finally got a good answer in the twig github issue tracker.
https://github.com/twigphp/Twig/issues/2275

where is block/about-us coming from in bolt _aside.twig

I was just reading through content fetching in bolt HERE , which gave me a fair idea of how setcontent works. Now i came across the following code in _aside.twig template in the default bolt theme:
<div class="panel">
{# The next section attempts to fill the panel in the sidebar with the contents of the
'about-us'-block, if it exists. First, attempt to fetch the block with slug 'about-us' #}
{% setcontent block = "block/about-us" %}
{# check if we have a title. If so, we know we have content to display. Otherwise, we will
output a default text, telling the user to create the 'blocks' in the backend. #}
{% if block.title is defined %}
<h5>{{ block.title }}</h5>
{{ block.content }}
{% if link(block.contentlink) or block.editlink() %}
<p>
{{ link(block.contentlink, __('general.phrase.read-more')) }} /
{{ __('general.phrase.edit') }}
</p>
{% endif %}
{% else %}
<h5>{{ __('general.phrase.missing-about-page') }}</h5>
<p>{{ __('general.phrase.missing-about-page-block') }}</p>
{% endif %}
</div>
My question is about the below line of code:
{% setcontent block = "block/about-us" %}
Where is "block/about-us" coming from ??
it refers to a record with the slug about-us on the blocks ContentType. It's set up as a resource ContentType. You can read more about resource ContentTypes here:
https://docs.bolt.cm/3.1/howto/resource-contenttype

Twig embed block not working when its within a loop?

I need to place a twig template into my page and place another twig template inside it.
In page.twig:
{% embed "parent.twig" %}
{% block something %}
{% include "child.twig" %}
{% endblock %}
{% endembed %}
In parent.twig:
{% set array = ['', '', '']
%}
<div class="parent">
{% for i in array %}
<div class="parent__item">
{% block cardBoard %}
{% endblock %}
</div>
{% endfor %}
</div>
The problem is that the block doesn't work when its within a loop. I can edit page.twig all I like but I would rather not change parent.twig if possible as its consumed by other applications.
Why do you put a block inside a for loop ? You can't have 2 blocks defined with the same name.
In your case it will try to put this in your template 3 times :
<div class="parent__item">
{% block cardBoard %}
{% endblock %}
</div>
This will create a 500 server error :
The block 'cardBoard' has already been defined line '' in :parent.twig at line ''.
If you really want to have content depending on the values in your array, put the block opening and closing outside the for loop.
Like this for example :
{% set array = ['', '', '']%}
<div class="parent__item">
{% block cardBoards %}
{% for i in array %}
<div class="cardBoard{{ i }}>
</div>
{% endfor %}
{% endblock %}
</div>

Resources