Jinja2 & Flask: Bootstrap Tabs display the content for the first tab only and not the rest

Question:

I’m using Jinja2 and Python to generate html and I’m also using bootstrap for the css and js. I’m trying to create Bootstrap pill tabs by using Jinja2 for loops and some other logic. Here is what my code looks like:

I pass this dictionary into the render_template function of flask along with the name of the html file I want to generate:

my_dict = {"First tab": "tab1", "Second tab": "tab2", "Third tab": "tab3"}

The keys of the dictionary represent the text that is displayed as the name of the tab when the website is loaded. The values of the keys represent the string that is passed to the html attributes for the tags (This will become clearer once you look at the html below). The first tab is active/selected and the rest are not when the any user loads the website. I use this Jinja2 logic in the html file to generate the tabs and content:

<ul class="nav nav-pills nav-fill" id="pills-tab" role="tablist">
    {% for name, attr in my_dict.items() %}

        {% if loop.index == 1 %}
            <li class="nav-item">
                <a class="nav-link active" id="{{ attr }}" data-toggle="pill" href="#{{ attr }}" role="tab"
                   aria-controls="{{ attr }}_c" aria-selected="true">{{ name }}
                </a>
            </li>

        {% else %}
            <li class="nav-item">
                <a class="nav-link" id="{{ attr }}" data-toggle="pill" href="#{{ attr }}" role="tab"
                   aria-controls="{{ attr }}_c" aria-selected="false">{{ name }}
                </a>
            </li>
        {% endif %}
    {% endfor %}
</ul>

After doing this, I generate the content for each tab by using another for loop. Here is the html:

<div class="tab-content" id="pills-tabContent">

    {% for attr in my_dict.values() %}

        {% if loop.index == 1 %}

            <div class="tab-pane fade show active" id="{{ attr }}_c" role="tabpanel"
                 aria-labelledby="{{ attr }}">
                <h4>This is a tab</h4>
            </div>

        {% else %}

            <div class="tab-pane fade" id="{{ attr }}_c" role="tabpanel" aria-labelledby="{{ attr }}">
                <h4>Hope this works</h4>
            </div>

        {% endif %}

    {% endfor %}

</div>

I know that the last two tabs are supposed to have the same content: Hope this works and the first tab is supposed to have This is a tab. However, when I run the code, all three tabs say the same thing: This is a tab. What is causing this behavior and how can I fix it? Thanks in advance.

Asked By: Rangana Herath

||

Answers:

The id of your <a class="nav-link"s in your tab html generation (the first code block posted) is not what controls the tab action in the second code sample: rather, the href. However, in the first code block, the href is {{ attr }}, while in the second, {{ attr }}_c. Instead, try using the same href ({{ attr }}_c"):

Update the <a class in the first code block:

<a class="nav-link active" id="{{ attr }}" data-toggle="pill" href="#{{ attr }}_c" role="tab"
    aria-controls="{{ attr }}_c" aria-selected="true">{{ name }}
</a>
Answered By: Ajax1234

I’m using the Bootstrap 5 Navs and Tabs snippet which may also be helpful for this sort of problem
https://getbootstrap.com/docs/5.2/components/navs-tabs/#javascript-behavior

Answered By: Natalia Lamas