I got
Rendering SLS 'base:nginx' failed: mapping values are not allowed here; line 6
when I run this code in ansible.
parent_dict = [{'nginx-1.13.2.tar.gz':'https://nginx.org/download/nginx-1.13.2.tar.gz'},{'zlib-1.2.11.tar.gz':'https://www.zlib.net/zlib-1.2.11.tar.gz'}]
{% for dict_item in parent_dict %}
{% for key, value in dict_item.items() %}
install-zlib:
cmd.run:
- name: |
cd /tmp
curl -L {{ value }} -o {{ key }}
tar xzf {{ key }}
rm -rf {{ key }}
- creates: /tmp/{{ key }}
{% endfor %}
{% endfor %}
A few things of note:
The assignment to parent_dict isn't enclosed in Jinja2's delimiters {% and %}.
There is an unnecessary indentation of 2 spaces before the entire block of install-zlib, which may make it an invalid YAML if you have any other YAML content in the same template with a different indentation.
With your loop iterated twice, you will end up with duplicating ID of install-zlib. You should make it install-{{ key }} instead.
The error message "mapping values are not allowed here" itself is from the YAML parser, which usually indicates that you are using a colon in unexpected places, which I don't see here, but could be the case since it's apparent that you did not simply copy and paste your code here, or you'd be reporting a different error of parent_dict being undefined due to the issue #1 above.
Related
I'm currently using Jinja2 to display reviews (taken from a database) on my webpage, and I stumbled upon the sort() filter. So I wrote some code to sort the reviews by their lowest rating.
{% for reviews in reviews.all()|sort(attribute='rating', reverse=false) %}
{{ reviews.text }}
{{ reviews.rating }}
{% endfor %}
The code above works, but now I want to get the very first sorted items (reviews.text, reviews.rating) from the list. I tried using:
{{ reviews.text[0] }}
{{ reviews.rating[0] }}
But this only returns the first character. I also tried using the |first filter, but this didn't work either.
Sorry if this seems like a silly question - I'm still getting to grips with Jinja2 - but is there any way I can pick out the first sorted item from my list?
Okay, the solution was simple (bear with me): Jinja2 has a list of control structures, one of them being loop.first which can be used in this instance.
{% for reviews in reviews.all()|sort(attribute='rating', reverse=false) %}
{% if loop.first %}
{{ reviews.text }}
{{ reviews.rating }}
{% endif %}
{% endfor %}
I'm learning to use the Craft CMS, which uses Twig templating. I'm trying to output a JSON object in Twig, but instead of 2 items in the JSON I'm getting info about a single item.
Here is my code:
{% set newsitems = craft.entries.section('newsitems').orderBy('PostDate desc').limit(100) %}
{% set response = [] %}
{% for newsitem in newsitems %}
{{ 'Here' }}
{% set response = response|merge({'type':0, 'id':newsitem.id, 'link':newsitem.sourceLink}) %}
{% endfor %}
{{ response|json_encode() }}
And here is the output I get:
Here Here {"type":0,"id":"25","link":"https:\/\/gadgets.ndtv.com"}
As can be seen, the loop executes two times ('Here' is printed 2 times) but there is only one item in the JSON array which is printed.
Am I missing something basic? Any help would be appreciated. Thanks in advance.
Twig's merge filter uses array_merge in the background.
The manual states the following
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended.
This is what is happening to your output, in the first iteration you've create an associative array with the key: type, id, link. In the x'th iteration you are just overwriting the values stored in said keys. The solution is also stated in the manual, numeric indices will be appended to the array instead of overwriting it.
In twig you would solve it as this:
{% set response = [] %}
{% for newsitem in newsitems %}
{% set response = response|merge([{ 'type': 0, 'id': newsitem.id, 'source': newsitem.source,},]) %}
{% endfor %}
{{ response|json_encode|raw }}
demo
I have a long Jinja2 template which has many nested if/for statements. It's very hard to read. I would like to indent the {% %} bits, to make it clearer.
However if I do that, the contents of those blocks gets indented further too.
How can I indent just the {% %} bits?
I'm using Ansible.
Steps to reproduce:
template.yaml.j2
{% for x in range(3) %}
Key{{ x }}:
# The following should be one list
- always here
{% if x % 2 %}
- sometimes here
{% endif %}
{% endfor %}
playbook.yaml
---
- hosts: localhost
connection: local
tasks:
- template:
src: template.j2
dest: template.yaml
Run with ansible-playbook playbook.yaml
Desired Output
Key0:
# The following should be one list
- always here
Key1:
# The following should be one list
- always here
- sometimes here
Key2:
# The following should be one list
- always here
Actual behavior:
Key0:
# The following should be one list
- always here
Key1:
# The following should be one list
- always here
- sometimes here
Key2:
# The following should be one list
- always here
Workaround
If I unindent the if statements like:
{% for x in range(3) %}
Key{{ x }}:
# The following should be one list
- always here
{% if x % 2 %}
- sometimes here
{% endif %}
{% endfor %}
Then I get the output I want.
But the problem is that this is hard to read. (In my actual template, I have if statements inside for inside if, etc. Highly nested.)
Q: "How to indent nested if/for statements in Jinja2?"
A: Turn off default trimming and manually ltrim only indented control statements {%-. For example, the template below does what you're looking for
shell> cat templates/template.j2
#jinja2: trim_blocks: False
{% for x in range(3) %}
Key{{ x }}:
# The following should be one list
- always here
{%- if x % 2 %}
- sometimes here
{%- endif %}
{%- endfor %}
The task
- template:
src: template.j2
dest: template.yaml
creates the file template.yaml
shell> cat template.yaml
Key0:
# The following should be one list
- always here
Key1:
# The following should be one list
- always here
- sometimes here
Key2:
# The following should be one list
- always here
See Whitespace Control.
Notes
The dash in {%- endfor %} removes the empty line among the keys.
By default parameter trim_blocks: yes. See template.
The documentation section Whitespace Control says:
You can manually disable the trim_blocks behavior by putting a plus sign (+) at the end of a block
Then, the following template gives the same result
shell> cat templates/template.j2
{% for x in range(3) %}
Key{{ x }}:
# The following should be one list
- always here
{%- if x % 2 +%}
- sometimes here
{%- endif +%}
{% endfor %}
I want to achive this is twig:
{{ form_widget(form.orderItems.0.enabled) }}
{{ form_widget(form.orderItems.1.enabled) }}
{{ form_widget(form.orderItems.2.enabled) }}
....
but the number to be a variable.
I tried this:
{% set index = 0 %}
{{ form_widget(form.orderItems.index.enabled) }}
Error: Method "index" for object "Symfony\Component\Form\FormView" does not exist
and this:
{{ form_widget(form.orderItems.{{index}}.enabled) }}
Error: Expected name or number
and this:
{{ form_widget(form.orderItems.~index~.enabled) }}
Error: Expected name or number
It is possible to achieve this :(
Some digging suggests you use the 'attribute' function - see Accessing array values using array key from Twig.
I suppose that would be something like
form_widget(attribute(form.orderItems, index).enabled)
Unfortunately I can't easily test that at the moment, but it should get you on the right track.
I am solving the same problem now and getting "expected name or number error" when I want to access variable dynamically.
I can not find simple answer, how to dynamically replace some part of variable in twig.
But It works without first dot as was first comment here.
{{ form[othervariable value].vars.label }}
and NOT
{{ form.[value].name }}
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.