How to concatenate strings in twig - syntax

Anyone knows how to concatenate strings in twig? I want to do something like:
{{ concat('http://', app.request.host) }}

This should work fine:
{{ 'http://' ~ app.request.host }}
To add a filter - like 'trans' - in the same tag use
{{ ('http://' ~ app.request.host) | trans }}
As Adam Elsodaney points out, you can also use string interpolation, this does require double quoted strings:
{{ "http://#{app.request.host}" }}

Also a little known feature in Twig is string interpolation:
{{ "http://#{app.request.host}" }}

The operator you are looking for is Tilde (~), like Alessandro said, and here it is in the documentation:
~: Converts all operands into strings and concatenates them. {{ "Hello
" ~ name ~ "!" }} would return (assuming name is 'John') Hello John!. – http://twig.sensiolabs.org/doc/templates.html#other-operators
And here is an example somewhere else in the docs:
{% set greeting = 'Hello' %}
{% set name = 'Fabien' %}
{{ greeting ~ name|lower }} {# Hello fabien #}
{# use parenthesis to change precedence #}
{{ (greeting ~ name)|lower }} {# hello fabien #}

In this case, where you want to output plain text and a variable, you could do it like this:
http://{{ app.request.host }}
If you want to concatenate some variables, alessandro1997's solution would be much better.

{{ ['foo', 'bar'|capitalize]|join }}
As you can see this works with filters and functions without needing to use set on a seperate line.

Whenever you need to use a filter with a concatenated string (or a basic math operation) you should wrap it with ()'s. Eg.:
{{ ('http://' ~ app.request.host) | url_encode }}

You can use ~ like {{ foo ~ 'inline string' ~ bar.fieldName }}
But you can also create your own concat function to use it like in your question:
{{ concat('http://', app.request.host) }}:
In src/AppBundle/Twig/AppExtension.php
<?php
namespace AppBundle\Twig;
class AppExtension extends \Twig_Extension
{
/**
* {#inheritdoc}
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('concat', [$this, 'concat'], ['is_safe' => ['html']]),
];
}
public function concat()
{
return implode('', func_get_args())
}
/**
* {#inheritdoc}
*/
public function getName()
{
return 'app_extension';
}
}
In app/config/services.yml:
services:
app.twig_extension:
class: AppBundle\Twig\AppExtension
public: false
tags:
- { name: twig.extension }

In Symfony you can use this for protocol and host:
{{ app.request.schemeAndHttpHost }}
Though #alessandro1997 gave a perfect answer about concatenation.

Quick Answer (TL;DR)
Twig string concatenation may also be done with the format() filter
Detailed Answer
Context
Twig 2.x
String building and concatenation
Problem
Scenario: DeveloperGailSim wishes to do string concatenation in Twig
Other answers in this thread already address the concat operator
This answer focuses on the format filter which is more expressive
Solution
Alternative approach is to use the format filter
The format filter works like the sprintf function in other programming languages
The format filter may be less cumbersome than the ~ operator for more complex strings
Example00
example00 string concat bare
{{ "%s%s%s!"|format('alpha','bravo','charlie') }}
--- result --
alphabravocharlie!
Example01
example01 string concat with intervening text
{{ "The %s in %s falls mainly on the %s!"|format('alpha','bravo','charlie') }}
--- result --
The alpha in bravo falls mainly on the charlie!
Example02
example02 string concat with numeric formatting
follows the same syntax as sprintf in other languages
{{ "The %04d in %04d falls mainly on the %s!"|format(2,3,'tree') }}
--- result --
The 0002 in 0003 falls mainly on the tree!
See also
http://twig.sensiolabs.org/doc/2.x/filters/format.html
https://stackoverflow.com/tags/printf/info

To mix strings, variables and translations I simply do the following:
{% set add_link = '
<a class="btn btn-xs btn-icon-only"
title="' ~ 'string.to_be_translated'|trans ~ '"
href="' ~ path('acme_myBundle_link',{'link':link.id}) ~ '">
</a>
' %}
Despite everything being mixed up, it works like a charm.

The "{{ ... }}"-delimiter can also be used within strings:
"http://{{ app.request.host }}"

Related

Changing a html class after the first item in a Hugo loop

So I am making a review carousel using Bootstrap and Hugo, I've got code that breaks down into this:
{{ range seq 1 3 (len site.Data.reviews) }}
...
{{ range seq . (add . 2) }}
{{ with (index site.Data.reviews (string .)) }}
{{ .des }}
{{ end }}
{{ end }}
...
{{ end }}
So there's two loops, one to make the slides for the carousel, and the other to fill the slides with data files. The issue is I need to delete the active class and adjust the data-bs-interval input on the next few slides I thought about making an if statement but I'm not sure how to replace the first div with one that doesn't have the active class after that in whats generated.
I don't know if this is the best solution to it, instead of editing the loop I wrote a bit of javascript:
var addActive = document.getElementById('carouselExampleDark').getElementsByClassName('carousel-item')[0];
addActive.classList.add("active");
That works for my use case so I'll leave it at that.

How do you parse brackets from path string on a variable return in Hugo?

I am writing a layout template for a Hugo generated post/page. The .yaml header is
image:
- post/mytitle/image.jpg
The template incorporates the variable in Hugo as
{{ .Params.Image }}
When served, the variable is returned as
[post/mytitle/image.jpg]
My html then becomes
<img src="[post/mytitle/image.jpg]"/>
which is 404 in the browser. I've tried a number of Hugo functions to no avail like {{ trim .Param.Image "[]" }} and {{ subset .Params.Image 1 -1 }} and {{ print .Params.Image }}. Each time Hugo returned the error: "error calling substr: unable to cast []string{"post/mytitle/image.jpg"} of type []string to string".
How do I get the variable to return the string without the brackets, or alternatively, how do I omit the brackets from the string?
In Go template, you access an item in a slice with index:
{{ index .Params.Image 0 }}
The question is why the value is a sequence in the first place. You could simply do
image:
post/mytitle/image.jpg
Then you could keep the original syntax since it is now a simple value, not a sequence.
If you want to possibly include multiple images, you'd do
{{ range .Params.Image }}<img src="{{.}}">{{ end }}
Then you can have
image:
- post/mytitle/image.jpg
- post/mytitle/anotherimage.jpg

Hugo data files from dynamic parameter

I'm developing a big hugo template. I try to simplfy the problem, so I have two datafile:
PROMO_00_1.yaml
PROMO_00_2.yaml
that are phisically stored in this directory:
themes/data/hp/
So, in the site config the user will decide which of this data file will be used simply indicate it in a param (HpElement).
In the template I call the partial in this way:
{{ partial "multiplepages/homepage/promos/00_promo_singleslide_text_video" (dict "context" . "data" $.Site.Params.HpElement) }}
In a partial I write:
{{ $data_partial := (printf "$.Site.Data.homepage.%s" .data)}}
{{ $data_partial}}
and the Hugo output is on the website:
$.Site.Data.homepage.PROMO_00_1
What I need is to access the single variable inside the .yaml file but the user MUST can decide which YAML file have to use. How can I achieve that?
Thanks
I just finished up a similar use case where I needed to select a YAML based on a date stamp. The index function is key to this operation.
https://gohugo.io/functions/index-function/
Here is a simplified version of my situation:
{{ $date := now.Format "s20060102"}}
{{ $data := (index .Site.Data.schedule $date) }}
{{ with $data }}
<h1>.longdate</h1>
{{ range .times }}
<h2>{{ .name }} - {{ .location }}
{{ end}
{{ end}
The example in the Hugo documentation uses an example where the data file is selected based on an attribute in the front matter.

How to strip leading whitespace before a Nunjucks variable only if the variable string is empty?

I'd like to support construction of Nunjucks templates where leading whitespace before variables is only stripped if the variable is empty.
A simple example could begin:
Hello {{ firstname }}, how are you?
If firstname is "Dave", this renders to "Hello Dave, how are you?". But if not set, it renders to "Hello , how are you?" with the extra space.
How can templates be constructed, in a general way (without fore-knowledge of the surrounding text), such that leading whitespace is only stripped before variables if the variable string is empty?
Using {{- firstname }} would always strip leading whitespace (so doesn't work if the variable is not empty, resulting in "HelloDave" in the example above
).
Something like the following works for this specific case (or better, the approaches suggested by #AikonMogwai in the comments), but is overly complex and relies on knowledge of the surrounding text (e.g., inserts a space if the variable is non-empty):
Hello
{%- if firstname %}
{{- " " + firstname -}}
{% else %}
{{- "" -}}
{% endif %},
Here's a fiddle that demonstrates the above examples: https://jsfiddle.net/davebeyer/3L5146jg/ (Click "Run" then "Render".)
Ideally, I'd like to create/use a custom tag (like {{=) or custom filter (like condlstrip for conditional left-strip) so that templates can be constructed using something like:
{{= firstname }}
or
{{ firstname | condlstrip }}
But it's not clear to me how to create a custom {{= tag, and also not clear whether filters, like condlstrip, can affect rendering outside of the tags.
Regarding the requirement "without fore-knowledge of the surrounding text," this specifically means that the solution should not assume that there will always be a space in front of the variable. For instance, it should also work in a case like the following (using my wished-for {{= tag), and where the user is fairly confident that school and studentId are set, but firstname may or may not be set):
Hi {{= firstname }}, your user ID is {{= school }}-{{= studentId }} and you can reach your profile at http://{{= school }}.example.com/profiles/{{= studentId }}
to produce:
Hi Dave, your user ID is CentralValley-1234 and you can reach your profile at http://CentralValley.example.com/profiles/1234
I.e., it would be great if our system could use the pattern {{= variable_name }} to produce reasonable default behavior for inserted variables in templates created by users (who are actually using a graphical, higher-level UI).
A simpler solution:
<li>Hello{{ " " if user.firstname }}{{ user.firstname }}, how are you?</li>
https://jsfiddle.net/pn8koeg3/
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
env.addFilter('ws', val => !val || val == 0 ? val : ' ' + val);
var html = env.renderString(`
Hi {{- firstname | ws -}}, your user ID ...
`, { firstname: 'Dave' });
console.log(html);

Combining two if conditions into one

The below works
{{- if hasKey (index $envAll.Values.policy) "type" }}
{{- if has "two-wheeler" (index $envAll.Values.policy "type") }}
<code goes here>
{{- end }}
{{- end }}
while the below fails with "runtime error: invalid memory address or nil pointer dereference"
{{- if and (hasKey (index $envAll.Values.policy) "type") (has "two-wheeler" (index $envAll.Values.policy "type")) }}
<code goes here>
{{- end}}
There is no list by name "type" declared under $envAll.Values.policy.
In Go, if the right operand is evaluated conditionally, why does the last condition gets evaluated in the second code snippet? How do I solve it?
Edit (since it marked as duplicate):
Unfortunately, I cannot use embedded {{ if }} like it is mentioned in the other post.
I simplified my problem above. I actually have to achieve this...
{{if or (and (condition A) (condition B)) (condition C)) }}
<code goes here>
{{ end }}
You get an error when using the and function because the and function in Go templates is not short-circuit evaluated (unlike the && operator in Go), all its arguments are evaluated always. Read more about it here: Golang template and testing for Valid fields
So you have to use embedded {{if}} actions so the 2nd argument is only evaluated if the first is also true.
You edited the question and stated that your actual problem is this:
{{if or (and (condition A) (condition B)) (condition C)) }}
<code goes here>
{{ end }}
This is how you can do it in templates only:
{{ $result := false }}
{{ if (conddition A )}}
{{ if (condition B) }}
{{ $result = true }}
{{ end }}
{{ end }}
{{ if or $result (condition C) }}
<code goes here>
{{ end }}
Another option is to pass the result of that logic to the template as a parameter.
If you can't or don't know the result before calling the template, yet another option is to register a custom function, and call this custom function from the template, and you can do short-circuit evaluation in Go code. For an example, see How to calculate something in html/template.
You could potentially use the Helm default function to avoid the second conditional.
It looks like your code is trying to test if .Values.policy.type.two-wheeler is present, with the caveat that the type layer may not exist at all. If there's no type, then .Values.policy.type evaluates to nil, and you can't do additional lookups in it.
The workaround, then, is to use default to substitute an empty dictionary for nil. Since it's then a dictionary you can do lookups in it, and since the default is empty testing for any specific thing will fail.
{{- $type := $envAll.Values.policy.type | default dict }}
{{- if has "two-wheeler" $type }}
<code goes here>
{{- end }}
You can put this into a one-liner if you want
{{- if has "two-wheeler" ($envAll.Values.policy.type | default dict) }}...{{ end }}
If you're actually going to use the value and not just test for its presence, another useful trick here can be to use the standard template with block instead of if. If with's conditional is "true" then it evaluates the block with . set to its value, and otherwise it skips the block (or runs an else block). In particular here, if a map value isn't present, then its lookup returns nil, which is "false" for conditional purposes (though note other things like 0 and empty string are "false" as well).
{{- $type := $envAll.Values.policy.type | default dict }}
{{- with $type.two-wheeler }}
{{-/* this references .Values.policy.type.two-wheeler.frontWheel */}}
frontWheel: {{ .frontWheel }}
backWheel: {{ .backWheel }}
{{- end }}

Resources