Minus In twig block definition - twig

What's the difference between this:
{%block body %}
and that
{%block body -%}

Just read something about it in the documentation, not sure if this will also apply on {% block ... %} tags.
Twig whitespace control
{% set value = 'no spaces' %}
{#- No leading/trailing whitespace -#}
{%- if true -%}
{{- value -}}
{%- endif -%}
{# output 'no spaces' #}
There's also another example given which trims the whitespace in front of the variable but doesnt't do it at the end - so the effect is only on one side.
{% set value = 'no spaces' %}
<li> {{- value }} </li>
{# outputs '<li>no spaces </li>' #}
The above sample shows the default whitespace control modifier, and how you can use it to remove whitespace around tags. Trimming space will consume all whitespace for that side of the tag. It is possible to use whitespace trimming on one side of a tag
So I think the difference in your given exmaples is that in the first block body there will be a whitespace after the block started. In your second example body - there's none after the block started.
Just read the documentation entry to see how it works.
EDIT
A simple example to demonstrate the example in the docu:
{% set value = 'NO space in source code after/before "value"' %}
<li> {{- value -}} </li>
...
outputs in Firebug in the HTML markup:
Whereas this
{% set value = 'space in source code after "value"' %}
<li> {{- value }} </li>
...
ouputs:
Notice the space between "value" and the closing </li> in the second example. So the minus - erases/trims a whitespace either before, after or on both sides of e.g. a variable.

Related

Macro causing white space in Jinja2?

I was expecting that this macro
{% macro join_them(first) -%}
{% set data = [] %}
{% if first not in ["", None, "None"] %}
{{ data.append(first) }}
{% endif %}
{% for arg in varargs %}
{% if arg not in ["", None, "None"] %}
{{ data.append(arg) }}
{% endif %}
{% endfor %}
{{' '.join(data)}}
{%- endmacro %}
Would render this
<td> * {{ join_them(docs["Author"]["Name"].title, docs["Author"]["Name"].first, docs["Author"]["Name"].middle, docs["Author"]["Name"].last, docs["Author"]["Name"].suffix) }}</td>
As this line, without the use of the macro, does
<td> * {{ ' '.join([docs["Author"]["Name"].title, docs["Author"]["Name"].first, docs["Author"]["Name"].middle, docs["Author"]["Name"].last, docs["Author"]["Name"].suffix])}}</td>
The non macro version is working fine, and outputing the name information correctly, but the Macro version is putting out white space and some "None" strings.
e.g.
<td> *
None
None
Computer Community
*</td>
<td>Computer Community </td>
Anyone have any suggestions on what I'm missing here? Or a better way to handle this? I don't typically use macros in Jinja2, but this seemed a perfect opportunity to do so.
The macro is indeed causing those blank lines, and you had the right approach already, you need to use whitespace control to remove them. You only placed them at the beginning and the end of your macro but note that:
You can also strip whitespace in templates by hand. If you add a minus sign (-) to the start or end of a block, a comment, or a variable expression, the whitespaces before or after that block will be removed
Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#whitespace-control, emphasis, mine
So, for each and every single block in your macro, you have to repeat this operation. To be extra sure, what you could do here is to frame any block in the macro with the minus sign.
Then for the None appearing, this is because you are using an expression delimiter {{ ... }} to append to your array, when you should use a statement delimiter {% ... %} and a do, as pointed in the documentation
If the expression-statement extension is loaded, a tag called do is available that works exactly like the regular variable expression ({{ ... }}); except it doesn’t print anything. This can be used to modify lists
{% do navigation.append('a string') %}
Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#expression-statement
So, your macro should end up looking like:
{%- macro join_them() -%}
{%- set data = [] -%}
{%- for arg in varargs if arg not in ["", None, "None"] -%}
{%- do data.append(arg) -%}
{%- endfor -%}
{{- ' '.join(data) -}}
{%- endmacro -%}
Note that, if you don't have the expression-statement extension loaded, you could also replace the {% do ... %} blocks by this inline-if 'hack':
{{- '' if data.append(arg) -}}

If statement with string length check

I'm trying to check if a string is empty in a twig template but I don't understand this behaviour. I want to show a textarea element when the condition is met. Here's the code:
{% if item.payload.customizationText|length == 0 %}
<textarea class="customization-text">{{ item.payload.customizationText|trim }}</textarea>
{% endif %}
It always shows the textarea, whether the string has something in it or not. Am I missing something?
It's in shopware 6 by the way and the dump() fuction throws an error because it's undefined.
Also, this expression just outputs the string. Is the length expression not available?
{{ item.payload.customizationText|length }}
I believe there is a mistake in the question. It does not make sense to print the variable, only if it is empty.
If you want to show the text area in case the customizationText has some contents, you should use this:
{% if item.payload.customizationText|trim|length %}
<textarea class="customization-text">{{ item.payload.customizationText|trim }}</textarea>
{% endif %}
https://twigfiddle.com/nmk2kq/4
You can try it.
{% if item.payload.customizationText is defined and item.payload.customizationText is empty %}
<textarea class="customization-text">{{ item.payload.customizationText|trim }}</textarea>
{% endif %}

Prevent quotes from being converted to HTML entities in twig ternary operator

The following does not convert the quotes to HTML entities
{% for row in files %}
<tr data-id="{{ row.id }}"><td>{{ row.name }}</td></tr>
{% endfor %}
The following does convert the quotes to HTML entities
{% for row in files %}
<tr{{ row.id?' data-id="'~row.id~'"' }}><td>{{ row.name }}</td></tr>
{% endfor %}
How can I prevent quotes from being converted to HTML entities in a twig ternary operator?
You should try the |raw filter (check out the documentation).
This is because in general, everything that twig prints out will be escaped to avoid things like cross-site-scripting. An exception is made for entirely static values like {{ '<b>static value</b>' }} which will not be escaped.
In your case, the following should work:
{% for row in files %}
<tr{{ (row.id?' data-id="'~row.id~'"')|raw }}><td>{{ row.name }}</td></tr>
{% endfor %}

Twig: Render block multiple times: parsed, escaped, verbatim

I’m building a pattern library that displays my production twig partials with some additional information. Under each pattern, I want to have two code snippets that I want to be generated from my pattern. One is the parsed HTML, the other one is my Twig code directly from the partial file, including variables and other Twig code.
Question
How do I reuse a block that I already parsed and rendered on my page, but in its unparsed form?
Files:
moduleXYZ.html (production module code), which extends…
_pattern.html (wrapper with additional informational code for pattern library), which uses…
_pattern_foot.html (in here, the block defined in _pattern.html and overwritten in moduleXYZ.html should be displayed verbatim)
moduleXYZ.html:
{% extends '_pattern.html' %}
{% block pattern %}
<h2>{{ variable|default('Some placeholder text') }}</h2>
{% endblock %}
_pattern.html:
{% block pattern %}
No pattern defined.
{% endblock %}
{% use '_pattern-foot.html' %}
{% block('patternfoot') %}
_pattern-foot.html:
{% block patternfoot %}
<h2>HTML for this pattern:</h2>
<pre><code>
{{ block('pattern')|e }} {# THIS WORKS, escaped HTML is displayed. #}
</code></pre>
<h2>Twig for this pattern:</h2>
<pre><code>
{{ block('pattern')|e }} {# THIS IS NOT WORKING, of course, because the block is already parsed. #}
</code></pre>
{% endblock %}
What didn’t work
I managed to get the result I want by wrapping the "pattern" block in moduleXYZ.html in {% verbatim %}, but then of course my logic/variables go unparsed in the module itself, too.
I understand it’s not possible to pass variables into the block() function, so I also can’t toggle verbatim conditionally for my reused block (or can I?).

How to pass the current context to include in Twig?

Say I have the following headline.twig partial:
<h2>{{ headline }}</h2>
and I want to include it in two places, once as:
<% for article in teasers %>
{{ include('headline.twig') }}
<% endfor %>
And then simply:
{{ include('headline.twig') }}
Is it possible to pass an include tag or function its context, so that the include "knows" that in the first instance the headline variable is actually article.headline?
I'm looking for a systematic way to do this, not with something like
{{ include('headline.twig', {headline: article.headline}) }}
If you want headline to be in the main context of your included file in all cases, you can do something like this:
{% for article in teasers %}
{% set headline = article.headline %}
{{ include('headline.twig') }}
{% endfor %}
But this will overwrite any existing headline variable in your current context if you're re-setting it this way (and risk to repeat 2 times the last iteration of teasers.article.headline).
The best solution if you want to keep your current context AND overwrite headline variable is to use the merge filter:
{% for article in teasers %}
{{ include('headline.twig', _context|merge({headline: article.headline})) }}
{% endfor %}
By default whole context is passed to included template.
So it will work in headline.twig:
<h2>{{ article.headline }}</h2>
And then in main template you only call:
{% include('headline.twig') %}
But article variable must be defined in moment of calling include.

Resources