Symfony4 twig for loop through variable variable - twig

i have a lot of sub entities, so i want to add/update them reusing one controller, view.
So i need before every form print out list of sub entities already created.
Sub entity name is variable
{% form_theme form 'bootstrap_4_horizontal_layout.html.twig' %}
<table class="table">
{% for entity in parentEntity.{{ subEntityName }} %}
<tr>
<td>{{ entity }}</td>
</tr>
{% endfor %}
</table>
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
But this is not working, also
~ subEntityName ~
Not works
Cant find any suggestion. Is possible to use variables in twig loop definition

Found solution
{% set entities = attribute(parentEntity, entityName) %}
{% for entity in entities %}
<tr>
<td>{{ entity }}</td>
</tr>
{% endfor %}
And this works for me like a charm!

Instead of creating a new variable entities, you can also simply do:
{% for entity in attribute(parentEntity, entityName) %}
And instead of using the attribute function, you can use the bracket notation, at least if parentEntity is an array:
{% for entity in parentEntity[entityName] %}

Related

flask jinja2 variable of variable

I am trying to access a variable of a object in jinja2 using this code:
{% for row in rows.items %}
<tr>
{% for column in model_settings['table_columns'] %}
<td>
<p>{{ row.column }}</p>
</td>
{% endfor %}
</tr>
{% endfor %}
what I want is if column was username I would want it to give me
row.username. sorry for the bad explanation.
Jinja2 has the builtin attr filter to access an attribute of an object. The name of the attribute can be a static string, or in your case: a string stored in a variable:
{% for row in rows.items %}
<tr>
{% for column in model_settings['table_columns'] %}
<td>
<p>{{ row|attr(column) }}</p>
</td>
{% endfor %}
</tr>
{% endfor %}

use an array variable in twig

I got a variable from a controller in the form of an array (variable ads), on twig, I used:
{% for ad in ads%}
<tr> <td> {{ad.xxx}}
{% if app.user and app.user == ad.author%}
{{ad.yyyy}}
{% endif%}
</td> </tr>
{$ endfor%}
there is no problem, it's classic.
I want to use for example
{% if app.user and app.user == ad.author%}
just before the for loop, to hide a balise for logged in users but it’s not their announcement
If userAds is a collection from ads from one and the same author, you could go with the following snippet(s):
{{ attribute(userAds|first, 'author') }}
{{ (userAds|first).author}}
{{ (userAds|first)['author'] }}
{% if app.user and (userAds|first).author == app.user %}
<th>Action</th>
{% endif %}

Jinja2/Flask makes my table tags disappear?

I'm trying to modify Flask-Security's _macros.html to nicely align everything. I replaced the p tags with tr and wrapped {{ field.label }} and {{ field(**kwargs)|safe }} both between td tags. For some reason those tags totally vanish from the output poof, like they've never been there. Using some nonsense like trrrrr and tdddd outputs just fine, so I know I'm editing the right file.
What's going on? Why does jinja2 hate my tables?
I also tried about wrapping the macro call in the template between <td></td> and then every supposedly wrapping tag just appear after all the macro calls without any content in between (but above).
I tried googling my vanishing tags but couldn't find anything.
{% macro render_field_with_errors(field) %}
<tr>
<td>{{ field.label }}</td> <td>{{ field(**kwargs)|safe }}</td>
{% if field.errors %}
<ul>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</tr>
{% endmacro %}
{% macro render_field(field) %}
<td>{{ field(**kwargs)|safe }}</td>
{% endmacro %}

Iterating over key-value arrays in Twig?

I'm (finally) switching my site from Drupal7 to Symfony3.
I'm working on migrating my layout to .twig.
I've got a question about loops and key-value arrays.
I've configured a k/v array
{% set test = {
sec1: {
title: "title 1",
content: "content 1"
},
sec2: {
title: "title 2",
content: "content 2"
},
}
%}
I can reference any specific array element in other markup. E.g., this works as intended
<ul>
<li>
<h1>{{ test.sec1.title }}</h1>
<div>
<p>{{ test.sec1.content }}</p>
</div>
</li>
</ul>
Now I want to loop/repeat the block over a count of n. But trying this,
<ul>
{% for i in 1..5 %}
<li>
<h1>{{ test.sec{{ i }}.title }}</h1>
<div>
<p>{{ test.sec{{ i }}.content }}</p>
</div>
</li>
{% endfor %}
</ul>
fires an error
Unexpected token "punctuation" of value "{" ("end of print statement" expected) in default/index.html.twig at line 67.
500 Internal Server Error - Twig_Error_Syntax
I've been working with variations of these
http://twig.sensiolabs.org/doc/tags/for.html#iterating-over-keys
but can't get anything to work yet.
What's the right way to loop this list over the secN's in the key-value array?
The reason why your script does not work is that you cannot compose an object-property accessor like this.
What you probably need is iterating over a subset:
{% for sec in test|slice(0, 5) %}
{{ sec.title }}
{% endfor %}
Or, if you do indeed need access to the key: iteration over key and value:
{% for key, sec in test %}
{{ sec.title }}
{{key}}
{% endfor %}
If you need to slice while still accessing the key, you could combine the latter with a check to the loop variables:
{% for key, sec in test %}
{% if loop.index0 < 5 %}
{{ sec.title }}
{{key}}
{% endif %}
{% endfor %}
{% for key, item in content.field_invoice_no if key|first != '#' %}
<tr>
<td>{{ item }}</td>
<td> {{ content.field_invoice_date[key] }} </td>
</tr>
{% endfor %}
Iterating over key-value arrays in Twig
for reference entity code below
{% for key, item1 in node.field_quotation.entity.field_product %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ item1.entity.body.value |raw }}</td>
<td> {{ item1.entity.field_product_unit_required.value }}</td>
<td>{{ item1.entity.field_hsn_code.value }}</td>
<td>{{item1.entity.field_pro.number|number_format(2, '.', ',')}}</td>
{% endfor %}
</tr>

cant access to .vars.prototype from macros

I'm trying to custom the add/delete feature of a collection with the data-prototype attr.
To do so, I use some twig macro:
view called by the controller
{# the principal view .html.twig #}
{% from"AppBundle:Macros:tabRangeVal.html.twig" import tableau %}
[...]
{{ tableau('InstrumentSharedUtils/File',form.documentations) }}
{{ tableau('SpecificationsSheet/SpecificationSheet',form.list) }}
[...]
macro for tableau
{# /src/AppBundle/Resources/views/Macros/tabRangeVal.html.twig #}
{% macro tableau(classe, arrayVar) %}
{% from "AppBundle:Form:"~ classe ~ ".html.twig" import val %}
{% from "AppBundle:Form:"~ classe ~ ".html.twig" import label %}
{% from "AppBundle:Macros:dataProto.html.twig" import proDataType %}
<div class="well well-sm">
<h5> {{ form_label(arrayVar) }}</h5>
<table class="table">
<thead>
<tr>
<th><button class="btn btn-default btn-add-row" type="button" id="{{ "add_#{arrayVar.vars.id}" }}"><span class="glyphicon glyphicon-plus"></span></button></th>
{{ label() }}
</tr>
</thead>
<tbody id="{{ "container_#{arrayVar.vars.id}" }}" data-prototype="{{ proDataType(classe, arrayVar.vars.prototype)|e }}">
{% for i in arrayVar %}
<tr >
<td><button class="btn btn-default btn-delete-row"><span class="glyphicon glyphicon-minus"></span></button></td>
{{ val(i) }}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}
macro for the data-prototype field
{# /src/AppBundle/Resources/views/Macros/dataProto.html.twig #}
{% macro proDataType(classe, value) %}
{% from "AppBundle:Form:"~ classe ~ ".html.twig" import val %}
<tr>
{% for v in value %}
<td><button class="btn btn-default btn-delete-row"><span class="glyphicon glyphicon-minus"></span></button></td>
{{ val(v) }}
{% endfor %}
</tr>
{% endmacro %}
an exemple of the AppBundle:Form:"~ classe ~ ".html.twig
{# /src/AppBundle/Resources/views/Form/InstrumentSharedUtils/File.html.twig #}
{% macro val(var) %}
<td>{{ form_widget(var.name, { 'attr': { 'class':'form-control' } }) }}</td>
<td>{{ form_widget(var.path, { 'attr': { 'class':'form-control' } }) }}</td>
<td>{{ form_widget(var.isExtra) }}</td>
{% endmacro %}
{% macro label() %}
<th>Name</th>
<th>Path</th>
<th>Is Extra</th>
{% endmacro %}
The problem is that I get the error
Method "name" for object "Symfony\Component\Form\FormView" does not exist in AppBundle:Form:InstrumentSharedUtils/File.html.twig at line 2
The thing that really bothers me is that I can access to the name (and even the others prototype.vars.children) if I dont use macro
by exemple, the following code works:
{# principal view .html.twig #}
{% for i in form.documentations.vars.prototype %}
{{ form_widget(i) }}
{% endfor %}
and i feel like its the exact same as above, with
form.documentations => arrayVar,
arrayVar.vars.prototype => value, and
{% for i in value %}
{{ form_widget(i.name) }}
{{ form_widget(i.path) }}
{{ form_widget(i.isExtra) }}
{% endfor %}
the output should be the same ...
could you help me to find what is wrong in there?
Ok, I will probably flog myself to be so stupid, but by writing it down, I realised (in the last code section) that I made a mistake:
the i in value at the end is already either name or path or isExtra...
so when I try to access to form.documentations.vars.prototype.name.name its logic that I get a beautifull exception...
so the solution was to modify the dataproto macro
{% macro proDataType(classe, value) %}
<tr class='block-to-remove'>
<td><button class='btn btn-default btn-delete-row'><span class='glyphicon glyphicon-minus'></span></button></td>
{% for truc in value %}
<td>{{ form_widget(truc, { 'attr': { 'class':'form-control' } }) }}</td>
{% endfor %}
</tr>
{% endmacro %}
and to send a arrayVar.vars.prototype to it

Resources