Is defined in twig ternary - php

Consider {{ item.nw is defined and item.nw ? ' target="_blank"' }} in the below script. Is there a more concise way to do this?
I thought {{ item.nw ?? ' target="_blank"' }} might work but it returns item.nw if item.nw is not false and target="_blank"' if item.nw is false (reference https://twig.symfony.com/doc/2.x/templates.html#test-operator)
{% macro menu(menu,active) %}
{# menu is an associated array of containing:
name. required
path or id: One of the two are required. If both, URL will use path
path. optional and defaults to javascript:void(0)
id. optional and defaults to not adding an id to the item.
nw. optional and defalts to false. This is a flag for a new window.
class. optional and defaults to not adding an class to the item.
#}
{% for item in menu %}
{% set path = item.path is defined?item.path:"javascript:void(0)" %}
{% set id = item.id is defined?item.id:null %}
{% set class = item.class is defined?item.class:"" %}
{% if (path == active or id == active) %}
{% set class = class~' active ' %}
{% endif%}
{% if loop.first %}
{% set class = class~' first ' %}
{% elseif loop.last %}
{% set class = class~' last ' %}
{% endif %}
<li class="{{ class|trim }}">
<a href="{{ path }}"{{ item.nw is defined and item.nw ? ' target="_blank"' }}{{ id?"id=#{id}" }}>{{item.name}}</a>
</li>
{% endfor %}
{% endmacro %}

Null coalesce will only return the value on the left if "it is defined and not null". Remember that false is a value also.
{{ item.nw ?? ' target="_blank"' }}
When "Strict Mode" is turned on you should check every variable you use in the Twig Template. It might be verbose, but it's better than having your template break.
There are alternative ways to check variables if you use the default filter. https://twig.symfony.com/doc/2.x/filters/default.html
{{ item.nw|default() is not false ? ' target="_blank"' }}
{{ item.nw|default() == 'some_value' ? ' target="_blank"' }}

Related

How to set a custom class to submenus in Drupal 8?

I am working on sidebar menu in a Custom Drupal 8 Theme. I am trying to set a class of sidebar__menu--submenu-1,sidebar__menu--submenu-2, sidebar__menu--submenu-3 and so on depending on the submenu's level.
So far, I was able to add the class sidebar__menu to the first level & sidebar__menu--submenu to all submenu's level. However, I want to add the 'class' sidebar__menu--submenu-(number of the level) so I can style & control the sidebar better with CSS.
Here it is my code menu.html.twig:
{{ menus.menu_links(items, attributes, 0) }}
{% macro menu_links(items, attributes, menu_level, menu_name) %}
{% import _self as menus %}
{%
set menu_classes = [
'sidebar__menu' ~ menu_name|clean_class,
]
%}
{%
set submenu_classes = [
'sidebar__menu' ~ menu_name|clean_class ~ '--submenu',
]
%}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes.addClass('container mx-auto', menu_classes) }}>
{% else %}
<ul {{ attributes.removeClass(menu_classes).addClass(submenu_classes) }}>
{% endif %}
{% for item in items %}
{%
set classes = [
'sidebar__item',
item.is_expanded ? 'sidebar__item--expanded',
item.is_collapsed ? 'sidebar__item--collapsed',
item.in_active_trail ? 'sidebar__item--active-trail',
]
%}
<li{{ item.attributes.addClass(classes) }}>
{{ link(item.title, item.url) }}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
Any help will be really appreciate it!
I have found the answer. First we set the classes:
{%
set submenu_classes = [
'sidebar__menu' ~ menu_name|clean_class ~ '--submenu',
'sidebar__menu--submenu-' ~ (menu_level + 1),
]
%}
Then using the logic like so:
{% if menu_level == 0 %}
<ul{{ attributes.addClass('container mx-auto', menu_classes) }}>
{% else %}
<ul{{ attributes.removeClass(menu_classes, 'sidebar__menu--submenu-' ~ (menu_level)).addClass(submenu_classes) }}>
{% endif %}

Twig accessing variables that need a variable expansion

I have the following code in a Twig template:
{% macro option_display(type) -%}
{% if server.options_array.options.{{type}}.name is defined %}
{{server.options_array.options.{{type}}.name}} +${{server.options_array.options.~type~.price}}
{%else%}
None
{% endif %}
{%- endmacro %}
I'm getting this error:
Twig_Error_Syntax: Expected name or number
How should I get it to expand the variable 'type' in that context? I've tried using ~type~ as well (concat).
would be something like $server['options_array']['options'][$type]['name']; in PHP.
Just use the bracket notation:
{% macro option_display(type) -%}
{% if server.options_array.options[type].name is defined %}
{{ server.options_array.options[type].name }} +${{ server.options_array.options[type].price }}
{% else %}
None
{% endif %}
{%- endmacro %}
You probably want to also check whether server.options_array.options[type] is defined before checking whether server.options_array.options[type].name is defined.
Furthermore, like the documentation of the macro tag says, "as with PHP functions, macros don't have access to the current template variables." So you probably need to pass the server variable to the macro as well.
So here's a more complete example:
{% macro option_display(server, type) -%}
{% set option = server.options_array.options[type]|default(null) %}
{% if option.name is defined %}
{{ option.name }} +${{ option.price }}
{% else %}
None
{% endif %}
{%- endmacro %}
{% from _self import option_display %}
{{ option_display(server, 'foo') }}
{{ option_display(server, 'bar') }}
Notice that I'm using a helper variable option and the default filter. Without the filter, you'd get a Twig_Error_Runtime exception if the server.options_array.options array doesn't have the key you are using. (Whether you get this exception or not depends on the value of the environment option strict_variables.)
See TwigFiddle

Find if twig array is empty of non-protected values

Given an a php array in a twig template:
object(Drupal\Core\Template\Attribute)#1208 (1) {
["storage":protected]=> array(0) { }
}
How do I check if there are no non-protected elements in the array? The idea is that I can only operate on non-protected values, so I can pretend the array is empty if only protected values are present.
So far, my check is as follows:
{% if attributes is defined and attributes is not empty %}
<div{{ attributes }}>
{{ content }}
</div>
{% else %}
{{ content }}
{% endif %}
In its current form, this displays <div>[Content]</div>. Instead, I'd like to see: [Content]
Any help?
If this is in Drupal 8, you can pass the attributes value through render to find out, like this:
{% if attributes|render %}
<div{{ attributes }}>
{{ content }}
</div>
{% else %}
{{ content }}
{% endif %}
Extend twig
<?php
$twig = new Twig_Environment($loader);
$twig->addFilter(new Twig_SimpleFilter('accessible_properties', 'get_object_vars'));
Use it inside template
{% set public_attributes = attributes is defined ? (attributes|accessible_properties) : [] %}
{% if public_attributes is not empty %}
...
{% else %}
...
{% endif %}

TWIG syntax do not show field IF

I have the script written in TWIG.
Weight: {{ doc.weight }} kgs<br />
// This part of script shows text information from "detail" and "info" MySQL fields ONLY IF these fields is NOT empty
{% if "" == doc.info_hl %}
{% if '' != doc.detail_code %}
<b>Info: {{ doc.info }}</b>
{% endif %}
{% else %}
Info: {{ doc.info_hl|raw }}
{% endif %}
<br />
{% if "" == doc.detail_code_hl %}
{% if '' != doc.detail_code %}
<b>Details: {{ doc.detail_code }}</b>
{% endif %}
{% else %}
<b>Details: {{ doc.detail_code_hl|raw }}</b>
{% endif %}
Sometimes MySQL field "weight" have values "0.00"
How to modify the code above - DO NOT SHOW "weight" MySQL field IF value in this field "0.00" ?
We can see above how we can do it with empty text fields, but how to do that with decimal fields equals to "0.00" ?
Thanks in advance for any hint to try !
What about simply write
{% if '0.00' != doc.weight %} Weight: {{ doc.weight }} kgs {% endif %}
or even better
{% if not ('0.00' == doc.weight) %}... {% endif %}
?
Please have a look at the is empty test in the Twig documentation. This test operates the same as the PHP empty() function. Here is a handy Type Comparison Table to help you get any idea of what it will return.
If it is expected that this field will only ever contain numbers, or String representations of numbers, you can get around empty('0.00') === FALSE by adding a 0 to the variable:
empty('0.00' + 0) === TRUE
// In Twig: {%if ($value + 0) is empty %}
Here is a list of available Expressions available in Twig Conditionals.

Comma separated list in twig

What is the shortest (and clearest) way to add comma after each element of the list except the last one?
{% for role in user.roles %}
{{ role.name }},
{% endfor %}
This example will add comma after all lines, including the last one.
Don't know about shortest but this could be clear. Try the following to add comma after all lines in the loop except the last one:
{% for role in user.roles %}
{{ role.name }}
{% if not loop.last %},{% endif %}
{% endfor %}
Shorter version as suggested in comments:
{% for role in user.roles %}
{{ role.name }}
{{ not loop.last ? ',' }}
{% endfor %}
This works with Symfony 2.3.x but should work with every 2.x version:
{{ user.roles|join(', ') }}
{{- not loop.last ? ',' : '' -}}
{{ user.roles|column('title')|join(', ') }}
Since the OP asked for iterating on name key of a role.
Shortest would be:
{{ user.roles|column('name')|join(', ') }}
where user.roles is a list of user roles.
Here is how I managed to print author names (in authors array) in some scientific publication format using twig's local loop variable in for loop-
{% spaceless %}
{% for author in authors %}
{{- loop.last ? ' and ' : (not loop.first ? ', ') -}}
{{- author -}}
{% endfor %}
{% endspaceless %}
The output is something like
For 1 author
Author1
For 2 authors
Author1 and Author2
For 3 or more authors
Author1, Author2, and Author3

Categories