picocms / Pico

Pico is a stupidly simple, blazing fast, flat file CMS.
http://picocms.org/
MIT License
3.81k stars 616 forks source link

Check if a variable is an element of an array #670

Closed notakoder closed 1 year ago

notakoder commented 1 year ago

I want to check if the current_page.id belongs to one of the elements in the array. The closest technique I figured is to use the has some filter. But I am confused about its usage in this case.

{% set array_name = ["element-1", "element-2"] %}
{% if (page.id starts with "(any of the array element)") %}

Or is there any better way to check this than the has some filter?

digitalinferno commented 1 year ago

Try this:

{% set array_name = ["element-1", "element-2"] %}

{% if current_page.id in array_name %}
    <!-- do something -->
{% else %}
    <!-- do something else -->
{% endif %}
notakoder commented 1 year ago

I think a premise of what I am trying to achieve will make things clearer. I am trying a modified version of @mayamcdougall 's code to display list of posts in every page (not just the posts list page).

<section>
<ul>
{% if (current_page.id starts with "docs") %}
  <!-- Code block starts -->
  {% set prev_part = "" %}
  {% for page in pages|sort_by("page.meta.ListPosition") %}
    {% if (page.id starts with "doc/") and not page.hidden %}

    {% set first_part = not prev_part and page.meta.Part %}
    {% set new_part = prev_part and page.meta.Part and prev_part != page.meta.Part %}
    {% set end_part = prev_part and not page.meta.Part %}

    {% if new_part or end_part %}
                </ul>
            </li>
    {% endif %}

    {% if first_part or new_part %}
            <li>{{ page.meta.Part }}
                <ul>
    {% endif %}

    <li><a {% if page.id == current_page.id %} class="active"{% endif %} href="{{ page.url }}">{{ page.title }}</a></li>

    {% if loop.last and page.meta.Part %}
            </li>
        </ul>
    {% endif %}

    {% set prev_part = page.meta.Part %}

    {% endif %}
  {% endfor %}
  <!-- Code block starts -->
{% endif %}
</ul>
</section>

There are many pages in various directories that need such list of posts and I don't want multiple templates. So, I'll have to duplicate to code block for every section.

{% if (current_page.id starts with "docs") %}
    <!-- Code block -->
{% elseif (current_page.id starts with "tuts") %}
    <!-- Code block -->
{% elseif (current_page.id starts with "books") %}
    <!-- Code block -->
{% endif %}

The problem here is the code block is repeated making the source code long and any changes to the code block must be repeated in every block. Not the best way. An easier way is to use an array of sections and check the current_page.idto be one of the array elements.

{% set array_name = ["docs", "tuts", "books"] %}
{% if (current_page.id starts with "(any of the array element)") %}
    <!-- Code block -->
{% endif %}

I tried the in filter as {% if (current_page.id in array_name) %}, but I just get a empty section. However, I tested your syntax in another part of the site and it works.

{% set post_sections = ["docs", "tuts"] %}
{% if (current_page.id in post_sections) %}
  {% for page in pages|sort_by("time") %}
    {% if (page.id starts with "docs") and not (page.id == current_page.id) and not page.hidden %}
        <li>{{ page.title }}</li>
    {% endif %}
  {% endfor %}              
{% endif %}

Secondly, I still need to rely on the starts with filter for the {% if (page.id starts with "doc/") and not page.hidden %} line inside the codeblock because in filter is not inclusive of the child pages. So need to find a way with starts with filter too even if the initial if condition works with in filter.

PhrozenByte commented 1 year ago

You can't combine starts with and in, but you don't really need starts with:

{% set sections = ["docs", "tuts"] %}
{% for page in pages|sort_by("time") %}
    {% set section = page.id|split('/')[0] %}
    {% if section in sections %} … {% endif %}
{% endif %}

Or you don't use in:

{% set sections = ["docs", "tuts"] %}
{% for section in sections %}
    {% for page in pages|sort_by("time") %}
        {% if page.id starts with section ~ "/" %} … {% endif %}
    {% endfor %}
{% endfor %}
notakoder commented 1 year ago

I tried the second code for its simplicity. Here is my existing code.

<section id="toc">
    <h1>Table of Contents</h1>
    <ol>
    {% if (current_page.id starts with "docs") %}
        {% set prev_part = "" %}
        {% for page in pages %}
            {% if (page.id starts with "docs") and not page.meta.TocHidden and not page.hidden %}
                {% set first_part = not prev_part and page.meta.Part %}
                {% set new_part = prev_part and page.meta.Part and prev_part != page.meta.Part %}
                {% set end_part = prev_part and not page.meta.Part %}

                {% if first_part or new_part %}
                        <h2>[{{ page.meta.Part }}]</h2>
                {% endif %}

                <li><a href="{{ page.url }}">{{ page.title }}</a></li>

                {% set prev_part = page.meta.Part %}

            {% endif %}
        {% endfor %}
    {% endif %}
    </ol>
</section>

{{ content }}

Here is the modified code.

<section id="toc">
    <h1>Table of Contents</h1>
    <ol>
    {% set sections = ["doc", "tuts", "xyz"] %}
    {% for section in sections %}
        {% for page in pages|sort_by("time") %}
            {% set prev_part = "" %}
            {% if page.id starts with section ~ "/" and not page.meta.TocHidden and not page.hidden %}
                {% set first_part = not prev_part and page.meta.Part %}
                {% set new_part = prev_part and page.meta.Part and prev_part != page.meta.Part %}
                {% set end_part = prev_part and not page.meta.Part %}

                {% if first_part or new_part %}
                        <h2>[{{ page.meta.Part }}]</h2>
                {% endif %}

                <li><a href="{{ page.url }}">{{ page.title }}</a></li>

                {% set prev_part = page.meta.Part %}

            {% endif %}
        {% endfor %}
    {% endfor %}
    </ol>
</section>

{{ content }}

The output is in the following format.

(Part name)
1. Doc 1 from /docs
(Part name)
2. Doc 2 from /docs
.
.
.
 (Part name)
10. Doc 10 from /docs
11. Doc 1 from tuts
.
.
19. Doc 9 from tuts
20. Doc 1 from xyz
.
.
25 Doc 6 from xyz

I think I messed up with the iteration when adding the new code.

github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in two days if no further activity occurs. Thank you for your contributions! :+1:

PhrozenByte commented 1 year ago

Check the position of {% set prev_part = "" %}: in the first snippet it was outside the pages loop, now it's within.

notakoder commented 1 year ago

Thank you. That fixed the repeated rendering of part name above every list. However, the rendering of links of pages from tuts and xyz directories are still there. I think this is what's going on. When we say {% for section in sections %}, every array element in the array is iterated and naturally all the pages in each directory (array element) is rendered one after other irrespective of what the current url is.

PhrozenByte commented 1 year ago

Yes, sure, that's what you did there :wink:

If you rather want it as some sort of function, use a Twig macro, see https://twig.symfony.com/doc/1.x/tags/macro.html

Totally untested:

{% import _self as utils %}
{% macro toc(pages, section) %}
    <ol>
        {% set prev_part = "" %}
        {% for page in pages %}
            {% if page.id starts with section ~ "/" and not page.meta.TocHidden and not page.hidden %}
                {% set first_part = not prev_part and page.meta.Part %}
                {% set new_part = prev_part and page.meta.Part and prev_part != page.meta.Part %}
                {% set end_part = prev_part and not page.meta.Part %}

                {% if first_part or new_part %}
                        <h2>[{{ page.meta.Part }}]</h2>
                {% endif %}

                <li><a href="{{ page.url }}">{{ page.title }}</a></li>

                {% set prev_part = page.meta.Part %}
            {% endif %}
        {% endfor %}
    </ol>
{% endmacro %}

<section id="toc">
    <h1>Table of Contents</h1>
    {{ utils.toc(pages|sort_by("time"), "doc") }}
</section>
github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in two days if no further activity occurs. Thank you for your contributions! :+1:

notakoder commented 1 year ago

Fantastic, this untested macro works so far.

Although it requires to repeatedly call the macro as per the current url, the calling code is just two lines. I can live with that. This issue has leached a lot of your time and I express my sincere thanks and gratitude to both of you for your help @PhrozenByte and @vodkafilm.