I am struggling with creating a macro to automatically generate section number and subsection numbers. I had thought the snippet below would work but it is failing to assign the value if not already set. I am sure this is obvious but my lack of jinja experience is showing... or perhaps lack of Python experience... or both!
I get this error.
{% set sectionnumber.value = sectionnumber.value + 1 %}
jinja2.exceptions.TemplateRuntimeError: cannot assign attribute on non-namespace object
{% macro getsectionnumber(type) -%}
{% if subsectionnumber is none %}
{% if sectionnumber is none %}
{% set sectionnumber = namespace(value=0) %}
{% endif %}
{% set subsectionnumber = namespace(value=0) %}
{% endif %}
{% if type == 'section' %}
{% if sectionnumber is none %}
{% set sectionnumber = namespace(value=0) %}
{% endif %}
{% set sectionnumber.value = sectionnumber.value + 1 %}
{{ sectionnumber.value }}
{% endif %}
{% if type == 'subsection' %}
{% set subsectionnumber.value = subsectionnumber.value + 1 %}
{{ sectionnumber.value }}.{{ subsectionnumber.value }}
{% endif %}
my template:
template = """
{% set sectionnumber = namespace(value=0) %}
{% set subsectionnumber = namespace(value=0) %}
{% macro getsectionnumber(type) -%}
{% if type == 'section' %}
{% set sectionnumber.value = sectionnumber.value + 1 %}
{{ sectionnumber.value }}
{% endif %}
{% if type == 'subsection' %}
{% set subsectionnumber.value = subsectionnumber.value + 1 %}
{{ sectionnumber.value }}.{{ subsectionnumber.value }}
{% endif %}
{% endmacro %}
{{ getsectionnumber('section') }}
{{ getsectionnumber('subsection') }}
{{ getsectionnumber('subsection') }}
{{ getsectionnumber('section') }}
{{ getsectionnumber('subsection') }}
{{ getsectionnumber('subsection') }}
"""
print the template:
print(Template(template).render())
result:
1
1.1
1.2
2
2.3
2.4
I'm using Twig in Views to rewrite output with condition.
{{ field_illus_lycee }}
{% if field_titre_pour_views is defined %}
{% if field_titre_pour_views is not empty %}
{{ field_titre_pour_views }}
{% endif %}
{% else %}
{{ title }}
{% endif %}
<span class="accroche-admin">{{ body }}</span>
I want to display field_titre_pour_views only if it exists and isn't empty, otherwise the regular title should be displayed. But at this point the regular title isn't displayed.Inspired by this
I don't understand which mistake I've made.
EDIT: correct code
{{ field_illus_lycee }}
{% if field_titre_pour_views is defined %}
{% if field_titre_pour_views is not empty %}
{{ field_titre_pour_views }}
{% else %}
{{ title }}
{% endif %}
{% else %}
{{ title }}
{% endif %}
<span class="accroche-admin">{{ body }}</span>
Sometimes, to ask is to find...this code do the trick:
{% if field_titre_pour_views |default %}
{{ field_titre_pour_views }}
{% else %}
{{ title }}
{% endif %}
Auto fixed :)
Hope it would help someone else.
I'm trying to build a simple dropdown menu on drupal 8 by using twig templates. My problem is that I can't find a way to give the dropdown links a class. Here's my code
{#
/**
* #file
* Theme override to display a menu.
*/
#}
{% import _self as menus %}
{{ menus.menu_links(items, attributes, 0, menu_name) }} {# 1. #}
{% macro menu_links(items, attributes, menu_level, menu_name) %} {# 1. #}
{% import _self as menus %}
{# 1. #}
{%
set menu_classes = [
'c-menu-' ~ menu_name|clean_class,
]
%}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes.addClass('navbar-nav u-header__navbar-nav') }}>
{% else %}
<ul class="hs-sub-menu list-unstyled u-header__sub-menu u-header__sub-menu-offset animated">
{% endif %}
{% for item in items %}
{%
set classes = [
menu_level ? 'dropdown-item u-header__sub-menu-list-item' : 'nav-item u-header__nav-item',
item.is_expanded ? 'menu-item',
item.is_collapsed ? 'menu-item',
item.in_active_trail ? 'active',
item.below ? 'nav-item hs-has-sub-menu u-header__nav-item hs-sub-menu-opened',
]
%}
<li{{ item.attributes.addClass(classes) }} data-event="hover" data-animation-in="fadeInUp" data-animation-out="fadeOut">
{%
set link_classes = [
not menu_level ? 'nav-link u-header__nav-link',
item.in_active_trail ? 'active',
item.below ? 'nav-link u-header__nav-link',
item.url.getOption('attributes').class ? item.url.getOption('attributes').class | join(' '),
]
%}
{% if item.below %}
{{ link(item.title, item.url, {'class': link_classes, 'data-toggle': 'dropdown', 'aria-expanded': 'false', 'aria-haspopup': 'true' }) }}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% else %}
{{ link(item.title, item.url, {'class': link_classes}) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
I just need to give a class to {{ menus.menu_links(item.below, attributes, menu_level + 1) }} completely independent from its parents.
Is there a way to achieve it? My output is like this
Sublink
I need to change it like this
Sublink
If there's any other way to do so like with custom module or with hooks, please let me know
Achieved the result with following code
{#
/**
* #file
* Theme override to display a menu.
*/
#}
{% import _self as menus %}
{{ menus.menu_links(items, attributes, 0, menu_name) }} {# 1. #}
{% macro menu_links(items, attributes, menu_level, menu_name) %} {# 1. #}
{% import _self as menus %}
{# 1. #}
{%
set menu_classes = [
'c-menu-' ~ menu_name|clean_class,
]
%}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes.addClass('navbar-nav u-header__navbar-nav') }}>
{% else %}
<ul class="hs-sub-menu list-unstyled u-header__sub-menu u-header__sub-menu-offset animated">
{% endif %}
{% for item in items %}
{%
set classes = [
menu_level ? 'dropdown-item u-header__sub-menu-list-item' : 'nav-item u-header__nav-item',
item.is_expanded ? 'expanded',
item.is_collapsed ? 'collapsed',
item.in_active_trail ? 'active',
item.below ? 'nav-item u-header__nav-item expanded nav-item hs-has-sub-menu u-header__nav-item',
]
%}
<li{{ item.attributes.addClass(classes) }} data-event="hover" data-animation-in="fadeInUp" data-animation-out="fadeOut">
{%
set link_classes = [
'nav-link',
not menu_level ? 'u-header__nav-link' : 'u-header__sub-menu-nav-link',
item.in_active_trail ? 'active',
item.url.getOption('attributes').class ? item.url.getOption('attributes').class | join(' '),
]
%}
{% if item.below %}
{% set title %}
{{ item.title }}<span class="fa fa-angle-down u-header__nav-link-icon"></span>
{% endset %}
{{ link(title, item.url, {'class': link_classes, 'data-toggle': 'dropdown', 'aria-expanded': 'false', 'aria-haspopup': 'true' })}}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% else %}
{{ link(item.title, item.url, {'class': link_classes}) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
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