How to preserve whitespace relative to the start of a block? - jinja2

lstrip and trim options are used
Since an example is worth a thousand words:
Input template:
{% if True %}
Lorem
{% for i in range(3) %}
{{ i }}
{% endfor %}
Ipsum
{% endif %}
Of course this produce:
Lorem
0
1
2
Ipsum
My question is, what must be used to have this output:
Lorem
0
1
2
Ipsum
I tried varying the strip and trim options, and using the + and - modifier.

Related

How to iteratively render the first X fields in a Django form

I'm rendering a form in a Django html template by using a for loop and iterating over the fields. I want to add some sections & headers inside my form, so is there a way to 'slice' the form fields or otherwise iteratively display only a subsection of fields?
Something like
{% for field in form.slice(0, 5) %}
<!-- render first 5 fields -->
<h2>Section 2</h2>
<p>Some text about next section</p>
{% for field in form.slice(5, 10) %}
<!-- render the next 5, etc. -->
I know the worst case answer is to render each field manually, but it's a VERY long form. Here's my existing form code.
{% for field in form %}
<div class="field">
<label class="label" for="{{field.id_for_label}}">
{{field.label}}
</label>
<div class="control">{{field}}</div>
{% if field.help_text %}
<p class="help">{{field.help_text}}</p>
{% endif %}
<ul class="errorlist">
{% for error in field.errors %}
<li class="is-danger help">{{error}}</li>
{% endfor %}
</ul>
</div>
{% endfor %}
You can make use of Django forloop.counter0 or forloop.counter variables. The first one which keeps track of the iteration inside the loop starting with index 0 and the second one indexes iterations starting with 1 (see the bottom of this section see the bottom of this section https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#for for more):
{% for field in form %}
{% if forloop.counter0 == 5 %}
<h2>Section 2</h2>
<p>Some text about section 2</p>
{% elif forloop.counter0 == 8 %}
<h2>Section 3</h2>
<p>Some text about section 3</p>
{% endif %}
<div class="field">
<label class="label" for="{{field.id_for_label}}">
{{field.label}}
</label>
<div class="control">{{field}}</div>
{% if field.help_text %}
<p class="help">{{field.help_text}}</p>
{% endif %}
<ul class="errorlist">
{% for error in field.errors %}
<li class="is-danger help">{{error}}</li>
{% endfor %}
</ul>
</div>
{% endfor %}
You would still have to manually check for the values. If you want for example to add section content in a way you can iterate the content every 5 fields you can take advantage of divisibleby and Django-Mathfilters to divide:
{% for field in form %}
{% if forloop.counter0|divisibleby:5 %}
<h2>Section {{ forloop.counter0|div:5}}</h2>
<p>Some text about section {{ forloop.counter0|div:5}}</p>
{% endif %}
<div class="field">
<label class="label" for="{{field.id_for_label}}">
{{field.label}}
</label>
<div class="control">{{field}}</div>
{% if field.help_text %}
<p class="help">{{field.help_text}}</p>
{% endif %}
<ul class="errorlist">
{% for error in field.errors %}
<li class="is-danger help">{{error}}</li>
{% endfor %}
</ul>
</div>
{% endfor %}
For more references you can see Modulus % in Django template to know about divisibleby and Is there a filter for divide for Django Template? to get some other methods on how to divide numbers in your Django template!

Kramdown table of content doesn't display inside HTML block

I was following this question, but it doesn't seem to take effect for me. Any help would be greatly appreciated.
_includes/layout.html
<main>
<div>
<!-- Sidebar -->
<aside markdown="1">
<h4>Table of Contents</h4>
* ToC
{:toc}
</aside>
<!-- END Sidebar -->
<!-- Main content -->
<article>
{{ content }}
</article>
<!-- END Main content -->
</div>
</main>
_config.yml
markdown: kramdown
Result:
Update
_layouts/site.html
<aside markdown="1">
mark**down**
</aside>
It just renders as above. Kramdown is turned on in config.
Solved by using pure liquid ToC by allejo.
% capture tocWorkspace %}
{% comment %}
"...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe
Usage:
{% include toc_pure_liquid.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %}
Parameters:
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
Optional Parameters:
* sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC
* class (string) : '' - a CSS class assigned to the TOC
* id (string) : '' - an ID to assigned to the TOC
* h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored
* h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored
Output:
An unordered list representing the table of contents of a markdown block. This snippet will only generate the table of contents and will NOT output the markdown given to it
{% endcomment %}
{% capture my_toc %}{% endcapture %}
{% assign minHeader = include.h_min | default: 1 %}
{% assign maxHeader = include.h_max | default: 6 %}
{% assign nodes = include.html | split: '<h' %}
{% for node in nodes %}
{% if node == "" %}
{% continue %}
{% endif %}
{% assign headerLevel = node | replace: '"', '' | slice: 0, 1 %}
{% assign headerLevel = headerLevel | times: 1 %}
{% assign indentAmount = headerLevel | minus: minHeader | add: 1 %}
{% assign _workspace = node | split: '</h' %}
{% unless headerLevel >= minHeader %}
{% continue %}
{% endunless %}
{% if headerLevel > maxHeader %}
{% continue %}
{% endif %}
{% assign _idWorkspace = _workspace[0] | split: '"' %}
{% assign html_id = _idWorkspace[1] %}
{% capture _hAttrToStrip %}{{ headerLevel }} id="{{ html_id }}">{% endcapture %}
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
{% assign space = '' %}
{% for i in (1..indentAmount) %}
{% assign space = space | prepend: ' ' %}
{% endfor %}
{% capture my_toc %}{{ my_toc }}
{{ space }}- [{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}](#{{ html_id }}){% endcapture %}
{% endfor %}
{% if include.class %}
{% capture my_toc %}{:.{{ include.class }}}
{{ my_toc | lstrip }}{% endcapture %}
{% endif %}
{% if include.id %}
{% capture my_toc %}{: #{{ include.id }}}
{{ my_toc | lstrip }}{% endcapture %}
{% endif %}
{% endcapture %}{% assign tocWorkspace = '' %}
{{ my_toc | markdownify }}
In theory it should work that way, (it is not working for me either) but you can force to process the code inside a block with `markdown="1" like this:
<aside markdown="1">
<h4>Table of Contents</h4>
* ToC
{:toc}
</aside>
Make sure you don't indent the code inside the aside tag or it will be parsed as kramdown code.
By default, kramdown parses all block HTML tags and all XML tags as
raw HTML blocks. However, this can be configured with the
parse_block_html. If this is set to true, then syntax parsing in HTML
blocks is globally enabled. It is also possible to enable/disable
syntax parsing on a tag per tag basis using the markdown attribute:
If an HTML tag has an attribute markdown="0", then the tag is parsed as raw HTML block.
If an HTML tag has an attribute markdown="1", then the default mechanism for parsing syntax in this tag is used.
Update
I've checked your repo, you need to rename index.html to index.md so kramdown will parse it and then you can also add the line to _config.yml to parse markdown inside html blocks.
Jekyll only parse .md or .mardown files.
.html files are not proccessed by markdown parser.
If you rename a file from .html to .md, it will be processed as kramdown.
But there you will have problems with indentation.
Mixing html and markdown is not so easy ;-)

display data in liquid

I'm looking to display information from a csv file on a jekyll-generated site. I need to search for the appropriate category in the csv file, then display four of them on the page. Filtering to the selected category is no problem, but I'm having difficulty limiting the output to four.
Is there a way to apply a limit to an if statement? Or is there any other way to write this? I'm not that savvy in Liquid, so it's extremely likely that I'm missing an obvious solution.
Basic code to make all the applicable data show up on the screen:
{% for study in site.data.studies %}
{% if study.category contains "foo" %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>Attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% else %}
{% continue %}
{% endif %}
{% endfor %}
I've also tried unless and tablerow, neither of which worked at all. Am I at least on the right track? How can I limit this forloop to stop at four items?
Thank you!
Ideally data should be filtered before rendering however you can also create a variable in liquid to hold the number of stuff rendered
{% assign rendered = 0 %}
{% for study in site.data.studies %}
{% if study.category contains "foo" %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% assign rendered = rendered | plus: 1 %}
{% if rendered == 4 %}
{% break %}
{% endif %}
{% endif %}
{% endfor %}
The ideal solution as I said would be to create your own filter which does all the work (filter by category and limit the number of results)
{% assign filtered = site.data.studies | my_custom_filter %}
{% for study in filtered %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% endfor %}
Presuming that your category is a string, not an array, you can do :
{% assign selected = site.data.studies | where: 'category','foo' %}
{% for study in selected limit:4 %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>Attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% endfor %}
And if your category is a string like "foo, bar, baz" or and array of strings you can use the jekyll 3.2 where_exp filter like this :
{% assign selected = site.data.studies | where_exp:"item", "item.category contains 'foo'" %}

How to reference properties in liquid

I have a Jekyll template pulling in text from data objects.
eg.
{% for speaker_hash in site.data.2015.speakers %}
{% assign speaker = speaker_hash[1] %}
<li>
<div class="speaker">
<img class="head" src="/img/2015/speakers/sample.jpg">
<h2> {{ speaker.name}} </h2>
</div>
</li>
{% endfor %}
However I would like to have each page specify what year its for with a page.year property.
Is it possible to create the same for loop but specify the year dynamically?
eg
{% for speaker_hash in site.data.[page.year].speakers %}
Answer yes.
1 - Your page.year must be a string as hash indexes are strings. So in you front matter : year: '2015'
2 - Get speakers depending on page.year :
{% for speaker_hash in site.data[{{page.year}}].speakers %}

multiple arbitrary blocks in Jinja2 macros

I use both Jinja2 and Nunjucks (depending on the project), but have yet to figure out how to create reusable elements with multiple blocks containing arbitrary HTML. For example (pseudo-code):
{% macro item(class) %}
<article class="{{ class }}">
<h3>{{ caller(1) }}</h3>
<p>{{ caller(2) }}</p>
</article>
{% endmacro %}
{% call item %}
Hello <abbr title="...">world</abbr>!
{% ---- %}
lorem <em>ipsum</em> dolor <strong>sit</strong> amet
{% endcall %}
Passing the respective blocks' HTML as regular arguments (i.e. strings) to the macro seems unrealistic.
A less contrived example might be Bootstrap-style forms:
<div class="form-group">
<label for="{{ id }}" class="control-label">$label</label>
<input type="{{ type }}" id="{{ id }}">
<p class="help-block">$hint</p>
</div>
Here both $label and $hint might be arbitrary blocks of HTML - perhaps there might even be multiple fields, defined outside the macro.
What's the recommended approach here?
You might find this useful for re-usable HTML components:
https://github.com/mozilla/nunjucks/pull/277
Example:
{% include 'search-box.html.twig' with {placeholder: 'Search users'} %}
You can use embed tag of atpl template engine.
Example:
{% embed "teasers_skeleton.twig" %}
{# These blocks are defined in "teasers_skeleton.twig" #}
{# and we override them right here: #}
{% block label %}
Some content for the label box
{% endblock %}
{% block hint %}
Some content for the hint box
{% endblock %}
{% endembed %}