I'm trying to make a for loop to build an accordion in Bootstrap, but every time I click the button to expand/ collapse a card, every card expands/ collapses. I think this is due to a shared id across all cards, so I want to use the post title as the id selector for each card. Here is the code I currently have:
{% for post in blog_posts %}
<div id="accordion">
<div class="card">
<h3 class="card-header" id="header">
<button class="btn btn-info" data-toggle="collapse"
data-target="#body" aria-expanded="false"
aria-controls="post: ">
{{ post.title }}
<small><p>Posted by {{ post.author }} on
{{ post.date_added|date:'D M d, Y H:i' }}</p></small>
</button>
</h3>
<div id="body" class="collapse show" aria-labelledby="header"
data-parent="#accordion">
<div class="card-body">
{{ post.text|linebreaks }}
<small>
<a class="text-dark" href="{% url 'blogs:edit_post' post.id %}">
edit post</a>
</small>
</div>
</div>
</div>
{% empty %}
<p>There are no posts to display.</p>
{% endfor %}
I've tried changing the h3 id to {{ post.title }}, but that doesn't seem to work. Any help is much appreciated.
Example code:
https://getbootstrap.com/docs/4.0/components/collapse/#accordion-example
You need the id's to be unique for each accordion section (otherwise they will all open at once), you could use the post.id for that.
{% if blog_posts %}
<div id="accordion">
{% for post in blog_posts %}
<div class="card">
<h3 class="card-header" id="header-{{post.id}}">
<button class="btn btn-info" data-toggle="collapse"
data-target="#post-{{post.id}}" aria-expanded="false"
aria-controls="post-{{post.id}}">
{{ post.title }}
<small><p>Posted by {{ post.author }} on
{{ post.date_added|date:'D M d, Y H:i' }}</p></small>
</button>
</h3>
<div id="post-{{post.id}}" class="collapse show" aria-labelledby="header-{{post.id}}"
data-parent="#accordion">
<div class="card-body">
{{ post.text|linebreaks }}
<small>
<a class="text-dark" href="{% url 'blogs:edit_post' post.id %}">
edit post</a>
</small>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p>There are no posts to display.</p>
{% endif %}
Related
My Django template renders the post.slug value in the first 'edit' form button correctly but on the modal pop up for the 'delete' it does not.
This was working a week or so ago. The 'Edit' button works perfectly, yet with almost identical code, under a Bootstrap modal (for confirmation of the post deletion) the 'Delete' button does not load the value="{{ post.slug }}"
my template:
{% extends "base.html" %}
{% block content %}
<div class="post-opinion">
<h3>{{ user }}'s Posts</h3>
</div>
<div class="container-fluid">
<div class="row">
<!-- Blog Entries Column -->
<div class="col-12 mt-3 left">
<div class="row">
{% for post in post_list %}
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<div class="image-container">
{% if "placeholder" in post.featured_image.url %}
<img class="card-img-top"
src="https://codeinstitute.s3.amazonaws.com/fullstack/blog/default.jpg">
{% else %}
<img class="card-img-top" src=" {{ post.featured_image.url }}">
{% endif %}
<div class="image-flash">
<p class="author">Author: {{ post.author }}</p>
</div>
</div>
<a href="{% url 'post_detail' post.slug %}" class="post-link">
<h2 class="card-title">{{ post.title }}</h2>
<p class="card-text">{{ post.excerpt }}</p>
</a>
<hr />
<p class="card-text text-muted h6">{{ post.created_on}} <i class="far fa-heart"></i>
{{ post.number_of_likes }}</p>
<div>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-primary mt-3" value="{{ post.slug }}" name="edit">Edit</button>
<button type="button" class="btn btn-danger mt-3" data-toggle="modal" data-target="#delete-confirmation">
Delete
</button>
</form>
</div>
</div>
</div>
</div>
{% if forloop.counter|divisibleby:3 %}
</div>
<div class="row">
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li>« PREV </li>
{% endif %}
{% if page_obj.has_next %}
<li> NEXT »</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
<div class="modal fade" id="delete-confirmation" tabindex="-1" role="dialog" aria-labelledby="deleteConfirmationModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Are you sure you want to delete this post?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method="post" class="d-inline">
{% csrf_token %}
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger" value="{{ post.slug }}"
name="delete">Delete</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
I am making a django app. This is my index.html template:
{% extends "blog/base.html" %}
{% block content %}
{% if latest_post %}
<div class="jumbotron p-4 p-md-5 text-white rounded bg-dark">
<div class="col-md-6 px-0">
<h1 class="display-4 font-italic">
{{ latest_post.title }}
</h1>
<p class="lead my-3">
{{ latest_post.body|truncatewords:30 }}
</p>
<p class="lead mb-0">
Continue reading...
</p>
</div>
</div>
{% endif %}
{% for post in posts %}
<div class="row mb-2">
<div class="col-md-6">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">{{ post.title }}</h3>
<div class="mb-1 text-muted">{{ post.date_posted }}</div>
<p class="mb-auto">
{{ post.body|truncatewords:30 }}
</p>
Continue reading
{% endfor %}
{% endblock %}
However, I am getting this error:
django.template.exceptions.TemplateSyntaxError: Unclosed tag on line 2: 'block'. Looking for one of: endblock. in django
I have made sure:
All the blocks are closed
There is no whitespaces between the percent signs and the block names
I am not missing any percent signs
Please help me
Inside for loop you have four opened <div> element after closing those it seems ok.
{% for post in posts %}
<div class="row mb-2">
<div class="col-md-6">
<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">{{ post.title }}</h3>
<div class="mb-1 text-muted">{{ post.date_posted }}</div>
<p class="mb-auto">
{{ post.body|truncatewords:30 }}
</p>
Continue reading
</div></div></div></div>
{% endfor %}
So I am developing a website and for the life of me I can't figure out how to align the description, price, stock and add cart button in multiple versions of the same <div>. I know it is to do with the size of the image I am using but I'm not sure how to fix this.
Here is a diagram of how I want it to look:
But when I apply a 'h-100' class to the card <div> this is what happens:
I want the images to keep their positions but for the descriptions, add cart button and price/stock to all be horizontally aligned, as well as the height of the overall cards to be the same.
Here is my Django template code:
{% extends 'base.html' %}
{% block content %}
<div class="container-fluid">
<div class="jumbotron">
<h2>Welcome to MyTea</h4>
<p>Here we have teas of all varieties from all around the globe</p>
</div>
<div class="row">
<div class="col-sm-3">
<h4>Categories</h4>
<ul class="list-group">
All Categories
{% for c in countcat %}
<a href="{{ c.get_absolute_url }}" class="list-group-item catheight">{{c.name}}
<span class="badge badge-light">{{c.num_products}}</span>
</a>
{% endfor %}
</ul>
</div>
<div class="col-sm-9">
{% for product in products %}
{% if forloop.first %}<div class="row">{% endif %}
<div class="col-sm-6">
<div class="card border-primary mt-3 h-100">
<div class="card-header"><h3>{{product.name}}</h3></div>
<div class="card-body">
{% if product.image %}
<div class="h">
<img src="{{product.image.url}}" class="img-fluid">
</div>
{% endif %}
<p class="bg-light font-weight-light ">{{product.description}}</p>
{% if product.stock > 0 %}
<a href="{% url 'add_cart' product.id %}" type="button" class="btn btn-primary btn-sm mb-2">
<p class="m-0">Add to cart</p>
</a>
{% else %}
<a href="#" type="button "class="btn btn-danger btn-sm mb-2">
<p class="m-0">Out of stock</p>
</a>
{% endif %}
<div class="card-footer">
<p>Price: €{{product.price}}</p>
<p>Stock left: {{product.stock}}</p>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
Thanks for any help
The code can be corrected with a simple re-alignment of the content inside .card and correctly closing </div> statements.
Remove {% if forloop.first %}<div class="row">{% endif %} statement and place <div class="row"> above the for loop.
Add to cart and Out of stock buttons should be placed inside .card-footer and .card-body should be closed appropriately. This will leave the image and description within .card-body
Make sure h-100 class is added to `.card'.
Might I suggest adding end of div comments to all the div statements. The code is readable better in this way and helps in mitigating missing or misplaced </div> statements.
{% extends 'base.html' %}
{% block content %}
<div class="container-fluid">
<div class="jumbotron">
<h2>Welcome to MyTea</h4>
<p>Here we have teas of all varieties from all around the globe</p>
</div>
<!-- .jumbotron -->
<div class="row">
<div class="col-sm-3">
<h4>Categories</h4>
<ul class="list-group">
All Categories
{% for c in countcat %}
<a href="{{ c.get_absolute_url }}" class="list-group-item catheight">{{c.name}}
<span class="badge badge-light">{{c.num_products}}</span>
</a>
{% endfor %}
</ul>
</div>
<!-- .col-sm-3 -->
<div class="col-sm-9">
<div class="row">
{% for product in products %}
<div class="col-sm-6">
<div class="card border-primary mt-3 h-100">
<div class="card-header">
<h3>{{product.name}}</h3>
</div>
<!-- .card-header -->
<div class="card-body">
{% if product.image %}
<div class="h">
<img src="{{product.image.url}}" class="img-fluid">
</div>
<!-- .h -->
{% endif %}
<p class="bg-light font-weight-light ">{{product.description}}</p>
</div>
<!-- .card-body -->
<div class="card-footer">
{% if product.stock > 0 %}
<a href="{% url 'add_cart' product.id %}" type="button" class="btn btn-primary btn-sm mb-2">
<p class="m-0">Add to cart</p>
</a>
{% else %}
<a href="#" type="button " class="btn btn-danger btn-sm mb-2">
<p class="m-0">Out of stock</p>
</a>
{% endif %}
<p>Price: €{{product.price}}</p>
<p>Stock left: {{product.stock}}</p>
</div>
<!-- .card-footer -->
</div>
<!-- .card -->
</div>
<!-- . col-sm-6 -->
{% endfor %}
</div>
<!-- .row -->
</div>
<!-- .col-sm-9 -->
</div>
<!-- .row -->
</div>
<!-- .container-fluid -->
{% endblock content %}
I have a comments section on some pages on my site that I build with a {% for ... %} loop (and another nested loop for comment replies. The section was hacked together, and I am still learning web development and Django, so please forgive any frustrating sloppiness or weirdness. I am not concerned with efficiency at the moment, only efficacy, and right now it is not working quite right.
For each comment I have a Bootstrap dropdown button that will bring up the options Edit and Delete. Edit will open a modal to edit the comment. The modals are rendered with an {% include %} tag. Below I have included part of my code unmodified, rather than trying to simplify my example and risk leaving something crucial out:
<div class="panel panel-default">
{% for comment in spot.ordered_comments %}
<div class="panel-heading row">
<div class="col-sm-10">
<strong>{{ comment.poster.username }}</strong>
<em style="margin-left: 2em">{{ comment.created|date:'M d \'y \a\t H:i' }}</em>
</div>
<div class="btn-group col-sm-2" role="group">
{% if comment.poster == user %}
<form id="delete-comment-form" class="form"
method="post" action="{% url 'delete_comment' spot.id comment.id %}">
{% csrf_token %}
</form>
{% include 'topspots/editmodal.html' with edit_type='comment' %}
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-edit"></i> <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li>Edit</li>
<li role="separator" class="divider"></li>
<li>
Delete
</li>
</ul>
</div>
{% endif %}
{% if user.is_authenticated %}
{% include 'topspots/replymodal.html' %}
<button type="button" class="btn btn-default" data-toggle="modal"
data-target="#replyModal">
Reply
</button>
{% endif %}
</div>
</div>
<div class="panel-body">
<div class ="row">
<div class="col-sm-8">
{{ comment.comment_text }}
</div>
</div>
<br/>
<!-- Comment replies -->
{% if comment.commentreply_set %}
{% for reply in comment.commentreply_set.all %}
<div class="row" style="padding-left: 1em">
<div class="col-sm-8 well">
<p>{{ reply.reply_text }}</p>
<div class="row">
<div class="col-sm-4">
<p>
<strong>{{ reply.poster.username }}</strong>
<em style="margin-left: 2em">{{ comment.created|date:'M d \'y \a\t H:i' }}</em>
</p>
</div>
{% if reply.poster == user %}
{% include 'topspots/editmodal.html' with edit_type='reply' %}
<form id="delete-reply-form" class="form"
method="post" action="{% url 'delete_reply' spot.id reply.id %}">
{% csrf_token %}
</form>
<div class="col-sm-2">
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-edit"></i> <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" data-toggle="modal"
data-target="#editModal">Edit</a></li>
<li role="separator" class="divider"></li>
<li>
<a href="javascript:;"
onclick="$('#delete-reply-form').submit();">Delete</a>
</li>
</ul>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
Here is the edit modal:
<!-- editmodal.html -->
{% load static %}
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h2 class="modal-title" id="editModalLabel">
Edit {{ edit_type }}:
</h2>
</div>
<form action="{% url 'edit_comment' spot.id comment.id %}" method="post">
<div class="modal-body">
<input class="form-control" name="text" value="{{ comment.comment_text }}" autofocus>
<input type="hidden" name="edit_type" value="{{ edit_type }}">
{% csrf_token %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-default">Finish editing</button>
</div>
</form>
</div>
</div>
</div>
<script>
$('.modal').on('shown.bs.modal', function() {
$(this).find('[autofocus]').focus();
});
</script>
and the reply modal:
<!-- replymodal.html -->
{% load static %}
<div class="modal fade" id="replyModal" tabindex="-1" role="dialog" aria-labelledby="replyModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h2 class="modal-title" id="replyModaLabel">
Reply to <strong>{{ comment.poster.username }}'s</strong> comment
</h2>
</div>
<div class="modal-body">
<form action="{% url 'reply_comment' spot.id comment.id %}" method="post">
<input class="form-control" name="reply_text" placeholder="Write a reply..." autofocus>
{% csrf_token %}
</form>
</div>
</div>
</div>
</div>
<script>
$('.modal').on('shown.bs.modal', function() {
$(this).find('[autofocus]').focus();
});
</script>
The issue I am having is that my reply and edit modals (e.g. {% include 'topspots/editmodal.html' with edit_type='reply' %} or {% include 'topspots/replymodal.html' %} seem to be only rendered once with the context of the first iteration of my for loop. So even though all the questions are correctly rendered on the page, when I click reply, edit or delete, regardless of which button I click (i.e., whether I click the button for the first comment, or the fifth comment, etc.) I can only reply to, edit, or delete the very first comment. I have a feeling that this has something to do with closures and scope in a way I am not quite understanding (I have gotten into trouble in the past with unexpected results using lambda in Python loops because of this or this), but I am not sure.
I did a test with the following view:
def test(request):
spots = Spot.objects.all()
return render(request, 'test.html', {'spots': spots})
and templates:
<!-- test.html -->
<h1>Hello world</h1>
{% for spot in spots %}
{% include 'testinclude.html' %}
{% endfor %}
and
<!-- testinclude.html -->
<h3>{{ spot.name }}</h3>
And it printed out a list of unique spot names, so why the difference with the modals?
As emulbreh postulates, a modal is in fact rendered for every comment. However, all of the modals have the same ID, so regardless of which comment’s edit button was clicked, the first modal gets triggered every time. IDs are supposed to be unique across an HTML document.
How can you fix this? You can make the IDs of the modals unique to each comment. You can get a unique identifier by writing id="editModal-{{ comment.id }}" or just id="editModal-{{ forloop.counter }} (documentation here).
But then your editModal.html template is coupled very tightly with your ‘master’ template. A better solution would be to use classes instead of IDs and put the identification where it belongs: the container of each comment. You can try:
adding an ID to each comment’s container:
<div class="panel panel-default">
{% for comment in spot.ordered_comments %}
<div class="panel-heading row" id="comment-{{ comment.id }}">
...
using classes instead of IDs in your modal templates as so:
<!-- editmodal.html -->
{% load static %}
<div class="modal fade editModal" tabindex="-1" ...>
...
changing data-target in your buttons from:
<li>Edit</li>
to:
<li>Edit</li>
It looks like all your edit modals will have the same id id="editModal" as well as your reply modals id="replyModal" if you are showing them based on id probably you will always open the first DOM element with that id. You could try appending a unique identifier like forloop.counter
https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#for
I have figured out how to use bootstrap carousel but problem is I want to render my featured stories to be in the carousel.
currently I have a carousel that shows three slides, but what I'm trying to do is to have featured stories instead of the three slides.
<div class='carousel slide' id="myCarousel">
<ol class="carousel-indicators">
<li class="active" data-slide-to="0" data-target="#myCarousel"></li>
<li class="active" data-slide-to="1" data-target="#myCarousel"></li>
<li class="active" data-slide-to="2" data-target="#myCarousel"></li>
</ol>
<div class="carousel-inner">
<div class="item active" id="slide1">
<img src="http://i.imgur.com/SQ691ZO.jpg" >
<div class="carousel-caption">
<h4>hello</h4>
<p>hi you</p>
</div>
</div>
<div class="item" id="slide2">
<img src="http://i.imgur.com/zN4h51m.jpg" >
<div class="carousel-caption">
<h4>hello</h4>
<p>hi you</p>
</div>
</div>
<div class="item" id="slide3">
<img src="http://i.imgur.com/3ruWvoG.jpg">
<div class="carousel-caption">
<h4>hello</h4>
<p>hi you</p>
</div>
</div>
</div>
<a class="left carousel-control" data-slide="prev" href="#myCarousel"><span class="icon-prev"></span></a>
<a class="right carousel-control" data-slide="next" href="#myCarousel"><span class="icon-next"></span></a>
So this is what I tried that's not quite working.
I have
{% for a in featuredStory %}
{{a.title}}
{{a.sub}}
<img src='{{a.get_featuredImage_url}}' class="img-rounded" alt="Cinque Terre" width="330" height="236"/>
{% endfor %}
working fine, but problem is to incorporate these with carousel.
with one featured story it works but with more than one, I get one in the top one in the bottom.
Here;s what I tried
<div class="row">
<div class="col-sm-8">
{% for a in featuredStory %}
<div class='carousel slide' id="myCarousel">
<ol class="carousel-indicators">
<li class="active" data-slide-to="0" data-target="#myCarousel"></li>
<li class="active" data-slide-to="1" data-target="#myCarousel"></li>
<li class="active" data-slide-to="2" data-target="#myCarousel"></li>
</ol>
<div class="carousel-inner">
<div class="item active" id="slide1">
<img src='{{a.get_featuredImage_url}}' class="img-rounded" alt="Cinque Terre" width="330" height="236"/> <div class="carousel-caption">
<h4> {{a.title}}</h4>
<p> {{a.sub}}
</p>
</div>
</div>
</div>
<a class="left carousel-control" data-slide="prev" href="#myCarousel"><span class="icon-prev"></span></a>
<a class="right carousel-control" data-slide="next" href="#myCarousel"><span class="icon-next"></span></a>
{% endfor %}
</div>
</div>
You can use the for loop inside carousel-inner:
<div class="carousel-inner">
{% for a in featuredStory %}
<div class="item {% if forloop.counter == 1 %}active{% endif %}" id="slide{{ forloop.counter }}">
<img src="{{ a.get_featuredImage_url }}" >
<div class="carousel-caption">
<h4>{{ a.title }}</h4>
<p>{{ a.sub}}</p>
</div>
</div>
{% endfor %}
</div>
Make sure to activate the first item by checking the counter:
{% if forloop.counter == 1 %}active{% endif %}
If there are other information such as class, title etc. that also you can set in context on the featuredStory and then render it here same as title or url.
Create div[class=item] dynamically with for loop in next way:
{% for item in featuredStory %}
<div class="item" id="slide{{forloop.counter}}">
<img src="item.get_featuredImage_url">
<div class="carousel-caption">
<h4>{{ item.title }}</h4>
<p>{{ item.sub }}</p>
</div>
</div>
{% endfor %}
I used loop.index instead of forloop.counter
<div class="carousel-inner">
{% for item in sliders %}
<div class="carousel-item {% if loop.index == 1 %}active{% endif %}" id="slide{{ loop.index }}" >
<img class="d-block w-100" width="330" height="236" src="{{ url_for('static', filename='images/sliders/') }}{{ item.imagelocation }}" alt="{{item.filmtitle}}">
<div class="carousel-caption">
<h4>{{ item.filmtitle }}</h4>
</div>
</div>
{% endfor %}
</div>