I have an array I'm iterating over to pull in different types of components into my page:
array(
'content'=> array(
'componentA'=>array(
'val'=>'1',
'title'=>'sample title'
),
'componentB'
)
)
I'm attempting to pass variables through from the array to the included template, but I'm not sure how to turn that string produced by the join into something that the include can understand as an array of variables. When I exclude the "with" from the first #components include, it prints out all the default values I've set in the iterable components like I would expect, but still gives me a white screen when I keep the with attribute in. When I display var itself, it returns this string:
(Note, I've also tried putting quotes around the {{k}} to no avail)
{ val:'1',title:'sample title' }
How can I pass the variables from my array to my component?
{% for key,item in content %}
{% if item is iterable %}
{% set var = [] %}
{% for k,v in item %}
{% set temp %}{% if loop.first %} { {% endif %}{{ k }}:'{{ v }}'{% if loop.last %} } {% endif %}{% endset %}
{% set var = var|merge([ temp ]) %}
{% endfor %}
{% set var = var|join(',') %}
{{ include ("#components/" ~ key ~ ".tmpl",var) }}
{% else %}
{{ include ("#components/" ~ item ~ ".tmpl") }}
{% endif %}
{% endfor %}
Your include statements are incorrect. You are using {{ include ... }}, which should be {% include ... %}.
The following snippet should work, if you only want to provide the data from the array (and not the loop data):
{% for key,item in content %}
{% if item is iterable %}
{% include ("#components/" ~ key ~ ".tmpl") with item %}
{% else %}
{% include ("#components/" ~ item ~ ".tmpl") %}
{% endif %}
{% endfor %}
You can then use {{ val }} and {{ title }} within your component template.
If you want to include the loop data, you can use:
{% for key,item in content %}
{% if item is iterable %}
{% include ("#components/" ~ key ~ ".tmpl") with {item: item, loop: loop} %}
{% else %}
{% include ("#components/" ~ item ~ ".tmpl") %}
{% endif %}
{% endfor %}
You can then use {{ item.val }}, {{ item.title }} and {{ loop.index }} in your component template.
Related
Hey iam currently trying to get the description of the first menu navigation in Shopware 6.
For that i use the array page.header.navigation.active.breadcrumb and use its key in page.header.navigation.tree[key].description, but my key value is empty.
Thats happening due to the key beeing empty for no reason.
Heres my Code:
{% sw_extends "#Storefront/storefront/section/cms-section-sidebar.html.twig" %}
{% set topMenu = null %}
{% for key, value in page.header.navigation.active.breadcrumb %}
{% if loop.index == 2 %}
{% set topMenu = value %}
{# {% set topMenuDescription = page.header.navigation.tree[key].category.description %} #}
{% set topMenuDescription = key %}
{% endif %}
{% endfor %}
{% set currentMenu = page.header.navigation.active.breadcrumb | last %}
{# {% set currentMenu = page.header.navigation.active.name %} #}
{# {% set topMenuDescription = page.header.navigation.active.description %} #}
{# {% if ! topMenuDescription %}
{% set topMenuDescription = page.header.navigation.active.description %}
{% endif %} #}
{% block section_main_content_block %}
<div class="category-top">
<div class="category-banner">
<img src="/media/6a/fd/8b/1632946677/listing-banner.jpg">
<div class="category-banner-headlines">
{% if (currentMenu != topMenu) %}
<h3>{{ topMenu }}<h3>
<h2>{{ currentMenu }}<h2>
{% else %}
<h2 class="sameMenu">{{ currentMenu }}<h2>
{% endif %}
</div>
</div>
<div class="category-description">
<h1>{{ currentMenu }}</h1>
{{ topMenuDescription | trans | raw }}
</div>
</div>
{{ parent() }}
{% endblock %}
Also here is the structure of the key i want to get:
key-i-want-to-get
And heres the description i want to get:
description-i-want-to-get
Sidenote: The description in my example is empty, since i do the showcase in a seperate testing area, where i havent set a description
The reason topMenuDescription is empty is because the variable only exist inside the scope of the {% for %}-loop you've created. Outside this loop the variable doesn't exist.
In order to solve this issue you need to alter the scope of topMenuDescription by defining the variable outside the {% for %}-loop
{% set topMenuDescription = null %}
{% for key, value in page.header.navigation.active.breadcrumb %}
{% if loop.index == 2 %}
{% set topMenu = value %}
{# {% set topMenuDescription = page.header.navigation.tree[key].category.description %} #}
{% set topMenuDescription = key %}
{% endif %}
{% endfor %}
sidenote
You really should enable twig's debug whilst developing as your current snippet would throw a RuntimeError explaining the variable does not exist.
Say I have a collection of items
$collection = array(
'item1' => array(
'post' => $post,
'category' => $category,
// ...
),
'item2' => array(...)
);
And I have a template:
{% for item in collection %}
Now I can use item data
- {{ item.post.title }}
- {{ item.category.id }}
- {{ item.var1 }}
- {{ item.var2 }}
- and another 20 vars
I want to extract those vars into more global FOR context, and use them as:
{{ post.title }}
{{ category.id }}
{{ var1 }}
... etc
{% endfor %}
Is this possible?
I was thining of defining the loop as a template block and then iterating it with Twig_Template::renderBlock(). But the docs say renderBlock is for 'internal' use only :) So not sure.
EDIT:
Another idea I had:
{% for item in collection %}
{% do extract(item) %}
// extract() would work similar to extract function from php
{% endfor %}
However, it seems that context is passed to twig functions by value, so this would not work.
Lastly I could write a TokenParser and do:
{% for item in collection %}
{% extract item %}
// would probably get direct access to the context, but haven't tried it
{% endfor %}
But this is quite a bit of work.. I am just hoping that twig can already do this natively :)
You can use a macro :
http://twig.sensiolabs.org/doc/tags/macro.html
{% import _self as macro %}
{% macro render(item) %}
{{ item.post.title }}
{{ item.category.id }}
{{ item.var1 }}
{{ item.var2 }}
...
{% endmacro %}
{% for item in collection %}
{{ macro.render(item) }}
{% endfor %}
If you really want to assign variables in the global context :
{% for item in collection %}
{% for var, value in item %}
{% set _context[var] = value %}
{% endfor %}
{{ post.title }}
{{ category.id }}
{{ var1 }}
... etc
{% endfor %}
I have this array:
$modules = array(
'users',
'submodule' => array(
'submodule1',
'submodule2',
),
);
My question is how can I access all the values and display it on html?
I have tried the following but no luck :
{% for key, module in modules %}
{% if modules.key is iterable %}
{{ module }}
{$ else %}
{{ module }}
{% endif %}
{% endfor %}
Thanks!
If your array has only 2 levels, you can just do something close to what you did:
{% for module in modules %}
{% if module is iterable %}
{% for submodule in module %}
<p>{{ submodule }}</p>
{% endfor %}
{% else %}
<p>{{ module }}</p>
{% endif %}
{% endfor %}
Will give you (with the context you given):
<p>users</p>
<p>submodule1</p>
<p>submodule2</p>
See fiddle
But if your array has an arbitrary number of levels, you should do some recursive using macros:
{% macro show_array(array) %}
{% from _self import show_array %}
{% for module in array %}
{% if module is iterable %}
{{ show_array(module) }}
{% else %}
<p>{{ module }}</p>
{% endif %}
{% endfor %}
{% endmacro %}
{% from _self import show_array %}
{{ show_array(modules) }}
With the following context (in YAML format):
modules:
0: users
submodule:
0: submodule1
1: submodule2
subsubmodule:
0: subsubmodule1
1: subsubmodule2
This will give you:
<p>users</p>
<p>submodule1</p>
<p>submodule2</p>
<p>subsubmodule1</p>
<p>subsubmodule2</p>
See fiddle
Why does this not work:
{% set relations = [{'cat':'friends','foo':'bar1'},{'cat':'enemies','foo':bar2},....] %}
{% set friends = [{'firstName':'John', 'lastName':'Goodman'},....] %}
{% set enemies = [{'firstName':'Ron', 'lastName':'Badguy'},....] %}
{% for relCat in relations %}
{% set list = relCat.cat %}
{% for person in list %}
{{ person.firstName }}
{% endfor %}
{% endfor %}
I admit that I 'm pretty new to twig, so I really searched and searched, but cannot find a solution for my problem (that I thought to be trivial) ....
I hope someone can help - I lost all my hair over this, thanks, Rudolph
Order of variables is important
So first: enemies and friends:
{% set friends = [{'firstName':'John', 'lastName':'Goodman'}] %}
{% set enemies = [{'firstName':'Ron', 'lastName':'Badguy'}] %}
Then set relations: again friends not 'friends', first is variable, second a string:
{% set relations = [{'cat':friends,'foo':'bar1'},{'cat':enemies,'foo':bar2}] %}
{% for relCat in relations %}
{% set list = relCat.cat %}
{% for person in list %}
{{ person.firstName }}
{% endfor %}
{% endfor %}
And this should work
See fiddle
You can also use the _context variable if you really need to keep strings:
{% set relations = [{'cat':'friends','foo':'bar1'},{'cat':'enemies','foo':bar2}] %}
{% set friends = [{'firstName':'John', 'lastName':'Goodman'}] %}
{% set enemies = [{'firstName':'Ron', 'lastName':'Badguy'}] %}
{% for relCat in relations %}
{% set list = _context[relCat.cat] %} {# <--- here #}
{% for person in list %}
{{ person.firstName }}
{% endfor %}
{% endfor %}
See fiddle
I need to add multiple blocks in my template, every with different name.
{% for item from items %}
{% block item.name %}sometext{% endblock %}
{% endfor %}
But I get error. How can I do this ?
In
Dynamic block names are not possible with Twig. There has been a discussion about it over at GitHub.
You can load blocks dynamically using the block function.
{% for item in items %}
{{ block( item.name )|raw }}
{% endfor %}
Twig documentation for the block function
If I understood the question correctly, you can do this (use parent context):
parent.html.twig
{% for item from items %}
{% set currentLoopItemName = item.name %}
{% block item_loop %}sometext{% endblock %}
{% endfor %}
override.html.twig
{% extends "base.html" %}
{% block item_loop %}
{% if item.name == 'custom' %}
// do something
{% else %}
{{ parent() }}
{% endif %}
{% endblock %}
I was attempting to do the same thing and found a solution using the template_from_string function.
_items.html.twig
{% for item in items %}
{{ '{% block ' ~ item.name ~ ' %}'}}
sometext
{{ '{% endblock %}' }}
{% endfor %}
enter code here
page.html.twig
{% embed template_from_string(include('_items.html.twig')) %}
{% block someItemName %} someDifferentText {% endblock %}
{% endembed %}
What's happening is the block tags are initially being created as text. Then we use the include function to get the rendered content of _items, as a string. Finally, we convert that string to a working template (which we can embed or extend).
This works because the template_from_string function will create and compile a template at runtime, where as normally twig is compiled before hand and unchanged at runtime.