I made a website with HTML and CSS that I now want to move into Pelican/Jinja templates. I want the navbar to automatically highlight the current section when the page is generated.
For example, on the "Masthead" page (which is under "About"), I want the "About" tab in the navbar to have a different color. Previously, I did this by adding a CSS class (and a <div>) manually. Now I want to do it more efficiently using Jinja macros and variables. I also want to add another inside current-page nav-buttons.
Here’s the relevant part of base.html:
{% block nav %}
<nav>
{% set btnH = '<a class="nav-button" href="/">Home</a>'%}
{% set btnA = '<a class="nav-button" href="/about.html">About</a>'%}
{% macro set_curr (section) %}
{% if section == 'Home' %}
{% set btnH = '<a class="nav-button current-page" href="/">Home</a>'%}
{% elif section == 'About' %}
{% set btnA = '<a class="nav-button current-page" href="/about.html">About</a>'%}
{% endif %}
{% endmacro %}
{{ set_curr("{{ section }}") }}
{{ btnH }}
{{ btnA }}
{% endblock nav %}
And in index.html:
{% extends "base.html" %}
{% block title %}Homepage{% endblock title %}
{% block section %}{% set section = 'Home' %}{% endblock section %}
{% block content %}
<content><p>Lorem ipsum</p></content>
{% endblock content %}
But when I run it, the navbar just shows:
<a class="nav-button" href="/">Home</a>
<a class="nav-button" href="/about.html">About</a>
No changed classes or added <div>s. The rest of the template works fine.
Why isn’t this working? What am I misunderstanding about Jinja macros or variable scoping?
Problem is that macro treats it as local variable (similar to varaibles in functions in Python)
It needs to use namespace() and keep values in this object
{% set ns = namespace() %}
{% set ns.btnH = '<a class="nav-button" href="/">Home</a>' %}
{% set ns.btnA = '<a class="nav-button" href="/about.html">About</a>' %}
and use them in macro
{% macro set_curr(section) %}
{% if section == 'Home' %}
{% set ns.btnH = '<a class="nav-button current-page" href="/">Home</a>' %}
{% elif section == 'About' %}
{% set ns.btnB = '<a class="nav-button current-page" href="/about.html">About</a>' %}
{% endif %}
{% endmacro %}
and use them to generate HTML
<nav>
{{ ns.btnH }}
{{ ns.btnA }}
</nav>
It has to be {{ set_curr(section) }} instead of {{ set_curr("{{ section }}") }}
I found namespace in this answer for question flask - Jinja2: local/global variable - Stack Overflow
Minimal working code for tests using jinja2
import jinja2
env = jinja2.Environment()
text = """
{% set ns = namespace() %}
{% set ns.btnH = '<a class="nav-button" href="/">Home</a>'%}
{% set ns.btnA = '<a class="nav-button" href="/about.html">About</a>'%}
{% macro set_curr(section) %}
{% if section == 'Home' %}
{% set ns.btnH = '<a class="nav-button current-page" href="/">Home</a>'%}
{% elif section == 'About' %}
{% set ns.btnB = '<a class="nav-button current-page" href="/about.html">About</a>'%}
{% endif %}
{% endmacro %}
{% block nav %}
{{ set_curr(section) }}
<nav>
{{ ns.btnH }}
{{ ns.btnA }}
</nav>
{% endblock %}
"""
template = env.from_string(text)
result = template.render(section="Home")
print(result)
By The Way:
Frankly, I would use different macro which could work with any number of buttons without changes in macro.
{% macro is_current(variable, value) %}
{%- if variable == value %} current-page{% endif -%}
{% endmacro %}
<a class="nav-button{{ is_current(section, "Home") }}" href="/">Home</a>
<a class="nav-button{{ is_current(section, "About") }}" href="/about.html">About</a>
There is - in {%- and -%} to put value in the same line and skip new lines at both sides.
There has to be space before current-page to separate it from nav-button.
I made example with more universal version txt(variable, value, text) which can set any text when variable == value, and I used it to create cur(variable, value) which sets current-page.
import jinja2
env = jinja2.Environment()
text = """
{% macro txt(variable, value, text) %}
{%- if variable == value %}{{ text }}{% endif -%}
{% endmacro %}
{% macro cur(variable, value) %}
{{- txt(variable, value, " current-page") -}}
{% endmacro %}
{% block nav %}
<nav>
<a class="nav-button{{ cur(section, "Home") }}" href="/">Home</a>
<a class="nav-button{{ cur(section, "About") }}" href="/about.html">About</a>
</nav>
{% endblock %}
"""
template = env.from_string(text)
result = template.render(section="Home")
print(result)
Macros are so short you can put every macro in one line.
And then they don't need -.
{% macro txt(variable, value, text) %}{% if variable == value %}{{ text }}{% endif %}{% endmacro %}
{% macro cur(variable, value) %}{{ txt(variable, value, " current-page") }}{% endmacro %}