collapsing accordion content in django for loop - python

I am trying to implement for loop in my accordion. Everything seems to be fine except the fact that when I click on 3rd or 4th button then other bodies stay expanded. It should work exactly the same as in the first example in Bootstrap documentation so if you click on Accordion Item #2 then Accordion Item #1 and Accordion Item #3 collapse.
I am sure that the issue is with my {% if forloop.first %} but I am not sure how can I dynamically change that so it will collapse all accordion contents except active one.
My code:
{% for category in top_categories %}
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{category.id}}">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapse{{category.id}}" aria-expanded="false" aria-controls="collapse{{category.id}}">
{{category.title}}
</button>
</h2>
<div id="collapse{{category.id}}" class="accordion-collapse {% if forloop.first %} show {% else %} collapse {% endif %}" aria-labelledby="heading{{category.id}}"
data-bs-parent="#accordionExample">
<div class="accordion-body">
{% for qa in category.qa.all|slice:"0:3" %}
Q: {{qa}}<hr>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}

I think that the main problem is that you are including <div class"accordion" id="accordionExample"> inside the loop. A secondary one is that the "collapse" class is also necessary for the first iteration. Try this:
<div class="accordion" id="accordionExample">
{% for category in top_categories %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{category.id}}">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{category.id}}" aria-expanded="true" aria-controls="collapse{{category.id}}">
{{category.title}}
</button>
</h2>
<div id="collapse{{category.id}}" class="accordion-collapse collapse {% if forloop.first %}show{% endif %}" aria-labelledby="heading{{category.id}}" data-bs-parent="#accordionExample">
<div class="accordion-body">
{% for qa in category.qa.all|slice:"0:3" %}
Q: {{qa}}<hr>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>

Related

Python Jinja2 loop index to make id unique

I'm using jinja2 template with Flask and MongoDB and I need to display users' cars in a template.
I was able to render cars in the profile template with car information and buttons to edit or delete a car. The problem is that if a user has multiple cars, only the first DELETE CAR button actually works (where I have an include modals to confirm) but nothing happens when click on the second or third delete button. Apparently I duplicated the id that needs to be unique. But I cannot achieve the results.
Here is my template: profile.html
{% extends 'layout/base.html' %}
{% block content %}
{% include 'components/navigation/navigation.html' %}
<div class="container">
<div class="row g-3 justify-content-center">
<div class="col-6 mt-5">
<div class="card">
<div class="card-body">
<h3 class="text-center profileHeader">
{{ username }}'s profile
</h3>
</div>
</div>
</div>
</div>
<div class="row row justify-content-center mt-4">
{% for car in cars %}
{% if session.user|lower == car.created_by|lower %}
<div class="card mb-3" style="max-width: 840px;">
<div class="row g-0">
<div class="col-md-4">
<img src="{{ car.car_image }}" class="imgCard" alt="ferrari image">
</div>
<div class="col-md-8">
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="list-group-item"><strong>Year</strong>: {{ car.car_year }} </li>
<li class="list-group-item"><strong>Name</strong>: {{ car.car_name }}</li>
<li class="list-group-item"><strong>Designer</strong>: {{ car.car_design }}</li>
</ul>
<ul class="list-group list-group-flush">
<li class="list-group-item"><strong>Engine</strong>: {{ car.spec_engine }}</li>
<li class="list-group-item"><strong>Power</strong>: {{ car.car_power }}</li>
<li class="list-group-item"><strong>Trasmission</strong>: {{ car.trasmission }}</li>
<li class="list-group-item"><strong>Races</strong>: {{ car.races }}</li>
<li class="list-group-item"><strong>Wins</strong>: {{ car.wins }}</li>
<li class="list-group-item"><strong>Podiums</strong>: {{ car.podiums }}</li>
<li class="list-group-item"><strong>Poles</strong>: {{ car.poles }}</li>
<li class="list-group-item"><strong>Fast Laps</strong>: {{ car.fast_laps }}</li>
<li class="list-group-item"><strong>Constructor Championship</strong>: {{
car.constructor_champ
}}</li>
<li class="list-group-item"><strong>Driver Championship</strong>: {{ car.drivers_champ }}
</li>
<li class="list-group-item"><strong>Description</strong>: {{ car.description }}</li>
<p><em>by: {{ car.created_by }}</em></p>
</ul>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a id="myBtn" class="btn btn-danger"><i class=" fas fa-trash-alt"></i> Delete</a>
<a href="{{ url_for('editcar', car_id=car._id) }}" class="btn btn-danger" id="edit-btn"><i
class="far fa-edit"></i> Edit</a>
{% with car_id=car._id %}
{% include 'components/modals/confirm.html' %}
{% endwith %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
enter code here
</div>
{% endblock %}
Just make unique the tag id of delete button, so the trigger will be unique in every button. How to make unique ? just add the car._id on tag id
<a id="myBtn-{{car._id}}" class="btn btn-danger"><i class=" fas fa-trash-alt"></i> Delete</a>
As #Darn pointed out, your ids need to be unique. But that is not the cause of your issue here.
I assume you have some Javascript code that gets triggered when user clicks on the delete link. The issue is probably from how that code is being triggered. The code needs to be written in such a way that it is bound to dynamically generated elements (you're generating the delete links on the fly i.e. afte the code for the delete was already bound; that delete code needs to also apply to each element when it is created).
You should bind the delete action to a class e.g.
$(".fa-trash-alt").on("click", function(){
$(this).parent() ...... // this is the link to the instance of the link that was clicked
});

Button must have a tooltip in one of template, but not in the others

I include the button for different templates (and different divs). There should be a tooltip in one of these divs, but not in the others. How can I do this?
I think I can check the div class in the button (something like {% if div_class=="..."%}), but I do not know how to do this. Is it possible? Maybe I need to check the template name or something else?
first.html
<div class="single-card__favorite">
{% include 'button.html' %}
</div>
second.html
<div class="card__footer">
{% include 'button.html' %}
</div>
button.html
{% load user_filters %}
{% if author|isfavorite:user %}>
<button class="button button_style_none" name="favorites">
<span class="icon-favorite icon-favorite_active"></span>
</button>
<div class="single-card__favorite-tooltip tooltip">Delete from favorites</div>
{% else %}
<button class="button button_style_none" name="favorites" data-out>
<span class="icon-favorite"></span>
</button>
<div class="single-card__favorite-tooltip tooltip">Add to favorites</div>
{% endif %}
You can pass additional context to the included template using the with clause (Reference: documentation). So you can pass some variable that will indicate that the tooltip should be rendered:
first.html:
<div class="single-card__favorite">
{% include 'button.html' with tooltip="yes" %}
</div>
button.html:
{% load user_filters %}
{% if author|isfavorite:user %}>
<button class="button button_style_none" name="favorites">
<span class="icon-favorite icon-favorite_active"></span>
</button>
{% if tooltip == "yes" %}
<div class="single-card__favorite-tooltip tooltip">Delete from favorites</div>
{% endif %}
{% else %}
<button class="button button_style_none" name="favorites" data-out>
<span class="icon-favorite"></span>
</button>
{% if tooltip == "yes" %}
<div class="single-card__favorite-tooltip tooltip">Add to favorites</div>
{% endif %}
{% endif %}
If your URLs are different for each template.
Suppose you have 2 URLs:
1. domain.com/url1_bro
2. domain.com/url2_bro
Then you can do something like this:
{% if 'url1' in request.path %}
<!--add tooltip html-->
{%else%}
<!--html button without tooltip-->
{%endif%}

Flask render_template taking 5+ seconds to run

I'm trying to display a page with some data, passing a dictionary through to the jinja template. This takes a ridiculously long time to load, easily 5-10 seconds for a very small dataset. This is what my Python looks like
#app.route("/request")
def request_page():
category_dict = CONFIG.get_categories()
return render_template('request.html', category_dict=category_dict)
And this is what my HTML looks like
{% extends "layout.html" %}
{% block title %}Categories{% endblock %}
{% block content %}
<h1 class="display-4">Category List</h1>
<form method='post' action='/result' onsubmit="return checkForm(this)">
<div class="form-group">
{% for category, list in category_dict.items() %}
<div class="card">
<div class="card-header">
<a class="text-dark" data-toggle="collapse" href="#{{loop.index}}">
{{category}}
</a>
</div>
<div id="{{loop.index}}" class="collapse">
<div class="card-body text-small">
<ul class="list-group list-group-flush">
{% for project in list %}
<li class="list-group-item">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="checkbox" value="{{project}}" id="{{project}}">
<label class="form-check-label" for="{{project}}">
{{project}}
</label>
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endfor %}
<br>
<h5>Reason for group access</h5>
<textarea rows="4" cols="60" name="reasons">
</textarea>
<br>
<button class='btn btn-primary' name='Submit'>Submit</button>
</form>
{% endblock %}
So as an overview, its a nested for loop where the outer loop iterates through the key value pairs of the dictionary and the inner loop iterates through the values, which are lists of strings. The dictionary that I'm passing through isn't very big, ~5 keys and maybe ~5 items in each list. Doing some tests also revealed that the slowdown is definitely with this chunk of code in the HTML, not in the layout.html or anything.

How do I hide or show content for each iteration?

I have a Lecture model which contains lectures. For each lecture I have a title and a content and eventually some files. I was trying to make that when somebody presses on a title, the title and the content will display under, but only for that lecture. Problem is that if I use a class, all lectures will be shown and if I use and id only the first one will be shown. How should I do this ?
$('#title').click(function () {
$('#lecture-hide').toggle();
});
{% for c in category.list %}
<div id="title">
<p>Lecture {{ forloop.counter }}: <span>{{ c.lecture_title }}</span>
</p>
<br>
</div>
<div id="lecture-hide" style="display: none;">
<br>
<li><h5>{{ c.lecture_title }}</h5></li>
<li><p>{{ c.content }}</p></li>
{% for file in c.files.all %}
{% if file.files %}
{% if forloop.first %}
<p id="files">Lecture files:</p>
{% endif %}
<li><a class="media"
href='{{ MEDIA_URL }}{{ file.files.url }}'><i
class="fas fa-download"></i>{{ file.files.name }}</a></li>
{% endif %}
{% endfor %}
<br>
</div>
{% endfor %}
Maybe you can use next selector in your jQuery code like this:
$('div.title').click(function () {
$(this).next().toggle();
});
You have to change id of the title to class to apply this to every element of the list.
You need to set dynamic attributes in your html:
{% for c in category.list %}
<div class="title" id="title-{{ c.id }}">
<!-- the content -->
</div>
<div class="lecture-hide" for="title-{{ c.id }}" style="display: none;">
<!-- the content -->
</div>
{% endfor %}
and in JQuery
$('.title').click(function () {
$('.lecture-hide[for="title-' + $(this).attr('id') + '"]').toggle();
});
This will works even if the html elements order change.

Django templatetags rendered as None in the template

Somebody can help me how to solve this problem, why my templatetags isn't rendered in the template? but, only rendered as None instead.
Previously I working with Django==1.10.4.
1. templatetags/total_tags.py, I already created __init__.py inside this folder.
from django import template
from myapp.models import Category
register = template.Library()
#register.simple_tag
def total_categories():
"""
{% load total_tags %}
{% total_categories %}
used in: `includes/menus_dashboard.html`
"""
print(Category.objects.all()) # this worked well
Category.objects.all().count()
2. myapp/dashboard.html
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Dashboard" %} :: {{ block.super }}{% endblock %}
{% block content %}
<div class="ui two column stackable grid">
<div class="four wide column dashboard-menu">
{% include "includes/menus_dashboard.html" %}
</div>
</div>
{% endblock %}
3. includes/menus_dashboard.html
The templatetags of {% total_topics %} is similiar with {% total_categories %}
{% load total_tags %}
<div class="ui fluid large inverted vertical pointing menu">
<a class="active item">
Dashboard
</a>
<a class="item">
Categories <div class="ui small label">{% total_categories %}</div>
</a>
<a class="item">
Topics <div class="ui small label">{% total_topics %}</div>
</a>
<a class="item">
Moderators <div class="ui small label">2</div>
</a>
<div class="item">
<div class="ui icon input">
<input type="text" placeholder="Search threads...">
<i class="search icon"></i>
</div>
</div>
</div>
Another idea, I tried like this answer: https://stackoverflow.com/a/12143011. and handle it with #register.assignment_tag, but still doesn't work well and only None instead.

Categories