Why is my fetch request being sent multiple times? - javascript

I am trying to make it possible to comment on the site. If I post the first comment, it posts fine. If posting the second one, fetch sends 2 requests and displays the same comment 2 times, the third comment 3 requests and displays 3 times, etc. What do I need to do for everything to work?
js
function insertNewComment(data) {
var ul = document.getElementById("new-comment");
ul.insertAdjacentHTML('afterBegin', '<li class="list-group-item">' + data.text + '</li>');
}
function sendComment() {
document.getElementById('comment').addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(e.target);
fetch(e.target.getAttribute('action'), {
method: e.target.getAttribute('method'),
body: formData
})
.then((response) => {
return response.json();
})
.then((data) => {
insertNewComment(data);
document.getElementById('comment').reset();
});
});
}
html
{% extends 'base.html' %}
{% block other_resources %}
<link rel="stylesheet" href="{{ url_for('.static', filename='css/post.css') }}">
<script src="{{ url_for('.static', filename='js/post.js') }}"></script>
{% endblock %}
{% block content %}
<div class="card">
<img src="{{ post.photo_path }}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.text }}</p>
<p class="card-text"><small class="text-muted">{{ post.created }}</small></p>
<div class="card-header">
Написать комментарий:
</div>
<div>
<form action="{{ url_for('news.add_comment', post_id=post.id) }}" id='comment' method="POST">
{{form.csrf_token}}
<div class="form-floating">
{{form.text(class='form-control')}}
{{form.text.label(for="floatingTextarea2")}}
</div>
{{form.submit(class="btn btn-dark send-comment", onclick='sendComment()')}}
<div class="form-floating">
<label for="floatingTextarea2">Комментарий</label>
</div>
</form>
</div>
<div class="card comments">
<div class="card-header">
Комментарии:
</div>
<ul class="list-group list-group-flush" id='new-comment'>
{% for c in comments %}
<li class="list-group-item">{{ c.text }}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}

Every time you execute sendComment you add another submit listener to that form.
Make sure you only add one listener:
function sendComment(e) {
e.preventDefault();
const formData = new FormData(e.target);
fetch(e.target.getAttribute('action'), {
method: e.target.getAttribute('method'),
body: formData
})
.then((response) => {
return response.json();
})
.then((data) => {
insertNewComment(data);
document.getElementById('comment').reset();
});
};
// add the listener outside of `sendComment`
document.getElementById('comment').addEventListener('submit', sendComment);

Related

Why html buttons dynamically created by django cannot access my javascript function?

My Django application makes divs dynamically. Each div is a post of a blog post and also I have a like button on each div. Each button will automatically turn into unlike when a user clicks on it without reloading using javascript. I wrote a javascript function for this. Unfortunately, my javascript function works only the last post on a page (As I have pagination property).
document.addEventListener("DOMContentLoaded",function(){
// const colon = document.createElement('div');
// colon.setAttribute('id','colon')
// e.preventDefault()
// const likebtn = document.createElement('button');
// likebtn.setAttribute('class','likebtn btn btn-primary');
// likebtn.setAttribute('class','likebtn');
// document.querySelector('.card-footer').appendChild(likebtn);
// document.querySelector('.likebtn').innerHTML = "Like";
document.querySelector(`#likeform${posts_id}`).addEventListener('submit',(event) => {
event.preventDefault();
like_function();
// return false;
})
// document.querySelector('.likepost').addEventListener('click', ()=> like_function('likepost'));
})
// let is_like = "{{is_like}}";
// let num_like = "{{num_like}}";
function like_function(){
// document.createElement('button').innerHTML = "Love";
// console.log("Updated!")
fetch(`/like/${posts_id}`,{
method:"POST",
body : JSON.stringify({
"is_like" : is_like,
"num_like" : num_like,
})
})
.then(response => response.json())
.then(result => {
if(result.is_like){
console.log("function is liked");
console.log(`${result.is_like} for post ${posts_id}`);
let num_like = result.num_like;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#likebtn${posts_id}`).innerHTML = "Unlike";
num_like = num_like + 1;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#num_of_likes_${posts_id}`).innerHTML = `${num_like} `
// location.replace("http://127.0.0.1:8000")
}
else{
console.log("function is unliked, back off!");
console.log(`${result.is_like} for post ${posts_id}`);
let num_like = result.num_like;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#likebtn${posts_id}`).innerHTML = "Like";
num_like = num_like - 1;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#num_of_likes_${posts_id}`).innerHTML = `${num_like} `
// location.replace("http://127.0.0.1:8000")
}
})
}
// function like_function(){
// if (document.querySelector("#like").style.color == "blue"){
// document.querySelector("#like").style.color = "red";
// }else{
// document.querySelector("#like").style.color = "blue";
// }
// }
This is my full javascript code. Here I'm sharing my HTML template,
<div id="posts" class="card">
<ul class="card-body">
{% for posts in page_view %}
<li class="card">
<div class="card-header bg-success">
<h5 class="card-title"><a class="text-light" style="text-decoration: none;" href="{% url 'profile' posts.user.id %}">{{ posts.user }}</a></h5>
<h6 class="card-subtitle text-light">{{ posts.timestamp }}</h6>
</div>
<div class="card-body">
<h3 class="card-text">{{ posts.post }}</h3>
</div>
<div id="likeapost{{posts.id}}" class="card-footer">
{% if request.user in posts.likepost.all %}
<form action="{% url 'likepost' posts_id=posts.id %}" id="likeform{{posts.id}}" method="POST" style="display: inline;">
<!-- {% csrf_token %} -->
<button id="likebtn{{posts.id}}" class="btn btn-link" type="submit">Unlike</button>
</form>
{% else %}
<form action="{% url 'likepost' posts_id=posts.id %}" id="likeform{{posts.id}}" method="POST" style="display: inline;">
<!-- {% csrf_token %} -->
<button id="likebtn{{posts.id}}" class="btn btn-link" type="submit">Like</button>
</form>
{% endif %}
<small id="num_of_likes_{{posts.id}}" class="num_of_likes">{{ posts.likepost.all.count }}</small>
{% block script %}
<script>
posts_id = "{{ posts.id }}";
is_like = "{{is_like}}";
num_like = "{{ posts.likepost.all.count }}";
</script>
<script src="{% static 'network/controller.js' %}"></script>
{% endblock %}
<button class="btn btn-link" style="text-decoration: none;">Comment</button>
View Post
{% if request.user.id is posts.user.id %}
Edit
{% endif %}
<!-- <div class="likepost"></div> -->
</div>
</li>
{% empty %}
<h6>No post availabel 😔</h6>
{% endfor %}
</ul>
</div>
This is my HTML template. All the buttons should work like this,
Only this one button works perfectly, but others do not. This is what happening.
What should I do? I am about 90% done. Please help.

can't refresh the data in a DIV python Django nor the page after a jquery

Let me start by saying I have 2 variables in an HTML template(messages and users) and I have multiple buttons that when one of them is clicked it calls a jquery code that sends a post request to a Django server and it returns an update to a variable(messages)
however, it's not updating the loop, I also tried to return a new HTML page that contains the new variable updated but the jquery is not updating the whole page with the new HTML
if I can update the variable alone it would be better and if I can't do that how can I make jquery use the new HTML page
the python code i used to return the update to the varialbe messages:
if request.method == 'POST':
send=Message.objects.filter(from_id=request.POST.get('userId'),to_id=2)
rec=Message.objects.filter(from_id=2,to_id=request.POST.get('userId'))
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
print(messages)
return HttpResponse(list(messages))
and the code i used to return new HTML template:
m = Message.objects.filter(to_id=2).order_by('-id')
users = {}
for i in m:
if users.get(i.from_id.username) == None:
users[i.from_id.username] = User.objects.get(id=i.from_id.id)
users = list(users.values())
send=Message.objects.filter(from_id=users[0].id,to_id=2)
rec=Message.objects.filter(from_id=2,to_id=users[0].id)
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
if request.method == 'POST':
send=Message.objects.filter(from_id=request.POST.get('userId'),to_id=2)
rec=Message.objects.filter(from_id=2,to_id=request.POST.get('userId'))
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
print(messages)
return render(request,'psych.html',{"users":users, "messages":list(messages)})
return render(request,'psych.html',{"users":users, "messages":list(messages)})
the HTML code and jquery code that uses the variable and try to update it
function newUser(id){
$.ajax({
type: 'POST',
url:'/psych.html/',
data:{
userId:id,
},
success: function(data){
console.log(data);// the data returnd are correct and as needed
//but i cant make it update the messages
$('#messageDiv').load(document.URL + ' #messageDiv');
}
})
}
{% for i in users %}
<li class="">
<button type="button" class="btn" onClick="newUser({{i.id}})">
<div class="d-flex bd-highlight">
<div class="img_cont">
<!-- here was an image ----------------------------------------------->
</div>
<div class="user_info">
<span>{{i.id}}</span>
</div>
</div>
</button>
</li>
{% endfor %}
<!-- The varialbe that i'm trying to update is called messages bottom -->
{% for o in messages %}
{% if o.to_id.id != 2 %}
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<!-- here was an image-->
</div>
<div class="msg_cotainer">
{{o.message}}
<!-- <span class="msg_time">{{o.time}}</span> -->
</div>
</div>
{% else %}
<div class="d-flex justify-content-end mb-4">
<div class="msg_cotainer_send">
{{o.message}}
<!-- <span class="msg_time_send">{{o.time}}</span> -->
</div>
<div class="img_cont_msg">
<!-- here was an image-->
</div>
</div>
{% endif %}
{% endfor %}
if it helps i did it before and updated the messages from jquery but i used form and there was only 1 variable i will add the code to that too
$(document).on('submit','#submitMessage', function (e){
e.preventDefault();
$.ajax({
type: 'POST',
url:'/psych.html/',
data:{
message:$('#messageHolder').val(),
csrfmiddlewaretoken: $('input[message=csrfmiddlewaretoken]').val(),
},
success: function(data){
// it work like charm here
$('#messageDiv').load(document.URL + ' #messageDiv');
}
})
})
{% for o in messages %}
{% if o.to_id.id == 2 %}
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<!-- here was an image-->
</div>
<div class="msg_cotainer">
{{o.message}}
<!-- <span class="msg_time">{{o.time}}</span> -->
</div>
</div>
{% else %}
<div class="d-flex justify-content-end mb-4">
<div class="msg_cotainer_send">
{{o.message}}
<!-- <span class="msg_time_send">{{o.time}}</span> -->
</div>
<div class="img_cont_msg">
<!-- here was an image-->
</div>
</div>
{% endif %}
{% endfor %}
<form id="submitMessage" >
{% csrf_token %}
<div class="card-footer">
<div class="input-group">
<div class="input-group-append"></div>
<input name="message" class="form-control type_msg" placeholder="Type your message..." id="messageHolder">
<div class="input-group-append">
<button type="submit" class="btn">
<span class="input-group-text send_btn" ><i class="fas fa-location-arrow"></i></span>
</button>
</div>
</div>
</div>
</form>
Try this
$("#messageDiv").load(location.href+" #messageDiv>*");
i figured the problem and it was because i didn't know that
$("#messageDiv").load(location.href+" #messageDiv>*");
would make a GET request so all I did was adding the necessary data to the URL and then change the URL too(so if the client refreshed the page it would stay in the same spot) without refreshing the page and then do the command app there
if it could help anyone please look at the code below:
function newUser(id){
var url = document.URL;
url = url.split('/');
url[url.length-2] = id;
url = url.join('/');
window.history.pushState("object or string", "my website name", url);
$('#messageDiv').load(url + ' #messageDiv');
}
sadly i don't know how to do post requst then load the page please if you know comment down bellow so someone else might get help from it

Django adding eventlistener to a for...loop on template tag

I'm trying to add a event listener to a list of links created by django template tag.
It should take the list of objects with class div.cat-link and add an eventlistener to each one to display the matching id of div.cat-select
html
<div class="cat">
<div class="cat-links">
{% for t in tags %}
<div id="{{t|lower}}" class="cat-link">
<a class="cat" href="{% url 'list_product1' t %}">{{t}}</a>
</div>
{% endfor %}
</div>
</div>
<div class="cat-list">
{% for t in tags %}
<div class="cat-select" id="cat_{{t|lower}}">
{% for p in t.produto_set.all %}
<div class="cat-product">
<!--IMAGES-->
<div class='img'>
<amp-carousel lightbox controls autoplay delay="3000" width="250"
height="250" layout="responsive" type="slides">
{% for pic in p.images.all %}
<amp-img src="{{ pic.image.url }}" width="250" height="150"
layout="responsive" alt="{{ p.nome }}">
</amp-img>
{% endfor %}
</amp-carousel>
</div>
<!-- INFOS -->
<div class='infos-prod'>
<a class='cat-product' href="{% url 'detail_product' p.id %}">
<h3>{{p.nome}} </h3>
</a>
<a class='cat-product' href="{% url 'detail_product' p.id %}">
R$: {{ p.preco }}
</a>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
JavaScript:
<script id="cat-script" type="text/javascript" target="amp-script">
function Showing(one) {
var v1 = document.getElementById(one);
v1.style.display = "flex";
};
function Hiding(one) {
var v1 = document.getElementById(one);
v1.style.display = "none";
};
function Category() {
var v1 = document.getElementsByClassName('cat-link');
for (o in v1) {
var v2 = 'cat_' + v1[o].getAttribute('id')
v1[o].addEventListener('mouseover', function () { Showing(v2) });
v1[o].addEventListener('mouseout', function () { Hiding(v2) });
};
};
Category()
</script>
It should take the list of objects with class div.cat-link and add an eventlistener to each one to display the matching id of div.cat-select
I'm getting the error:
Uncaught TypeError: v1[o].getAttribute is not a function
and nothing is happening.
Looks like you're mixing up Python syntax with JavaScript.
First, try for (let o of v1) instead of for (o in v1).
Then, you're trying to access it by index, but that's not the kind of loop you're using. Try o.getAttribute('id').

how to display comments with ajax submit call on the same post detail page

I am trying to display user comments after they click on the comment submit button. I added an ajax call on my comment section. However when i click on the submit button after writing a comment, Page is not refreshing but it is not displaying the new comment right away. Only after i refresh the page my new comment is available. I want to display new comment on the page right after clicking the submit button without reloading the page.
I am unable to figure out what is going wrong here, my codes are as follow:
at the end of base.html
<script>
$(document).on('submit', '.comment-form', function(event){
event.preventDefault();
console.log($(this).serialize());
$.ajax({
type: 'POST',
url: $(this).attr('action'),
data: $(this).serialize(),
datatype: 'json',
success: function(response) {
$('.main-comment-section').html(response['form']);
},
error: function(rs, e) {
console.log(rs.responseText);
},
});
});
in post_detail.html, i am calling comment_section.html using the following
<div class="main-comment-section">
{% include 'webpage/comment_section.html' %}
</div>
My comment_section.html
<h1>New comment</h1>
<form method="post" class="comment-form" action=".">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" class="save btn btn-outline-success">
</form>
{{ comments.count }} Comments</h3>
<!-- comments -->
{% for comment in comments %}
<article class="media comment">
<img class="rounded-circle article-img" src="{{ comment.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-profile' comment.author.username %}">{{ comment.author.first_name }} {{ comment.author.last_name }}</a>
<small class="text-muted">{{ comment.created_date }}</small>
</div>
<p class="article-content" "mr-4">{{ comment.content | linebreaks }}</p>
</div>
</article>
<div class="row">
<div class="col-1"></div>
<div class="col-10">
<div class="reply">
<div class="form-group row">
<form method="POST">
{% csrf_token %}
<input type="hidden" name="{{ comment_id }}" value="{{ comment.id }}">
{{ comment_form.as_p }}
<button type="submit" class="save btn btn-default">Comment</button>
</form>
</div>
</div>
</div>
</div>
{% empty %}
<pclass="article-content" "mr-4"> No comments here yet :(</p>
{% endfor %}
views.py
class PostDetailView(FormMixin,DetailView):
model = Post
context_object_name = 'post'
slug_url_kwarg = 'slug'
slug_field = 'slug' # DetailView's default value: optional
form_class = CommentForm
def get_success_url(self):
return reverse('post-detail', kwargs={'slug': self.object.slug})
def get_context_data(self, *args, **kwargs):
slug = self.kwargs.get("slug")
post = get_object_or_404(Post, slug=slug)
comments= Comment.objects.filter(post=post)
context = super(PostDetailView,self).get_context_data()
context['form'] = CommentForm(initial={'post': self.object, 'author': self.request.user})
context["comments"] = comments
context["post"] = post
return context
if self.request.is_ajax():
html = render_to_string('webpage/comment_section.html', context, request=self.request)
return JsonResponse({'form': html})
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super(PostDetailView, self).form_valid(form)
form.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['author','content', 'post']
widgets = {
'content' : forms.Textarea(attrs={'class':'form-control', 'placeholder' : 'Write a comment here', 'rows':'4', 'cols': '50'}),
'author' : forms.TextInput({'class':'form-control', 'value':'', 'id':'Idspacename', 'type':'hidden'}),
'post' : forms.TextInput({'class':'form-control', 'value':'', 'id':'Idspacename', 'type':'hidden'}),
}
and urls.py
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
Please help. Thanks in advance!
I will answer on the javascript part, first you need to create wrapper for the comment list. If you get the response from success you can append the element into the wrapper.
$.ajax({
method: 'POST',
url: your_url,
data: your_data,
success(function(response) {
const comment_list = document.getElementById('comment_list');
const _el = document.createElement('article');
_el.setAttribute('class', 'media comment');
const _elImg = document.createElement('img');
_elImg.setAttribute('class', 'rounded-circle article-img')
_elImg.setAttribute('src', response.author.profile.image.url);
const mediaBody = document.createElement('div');
mediaBody.setAttribute('class', 'media-body');
const metaData = document.createElement('div');
metaData.setAttribute('class', 'article-metadata');
metaData.innerHTML = '<a class="mr-2" href="url_to_profile' + response.author.username '">' + response.author.first_name + response.author.last_name '</a><small class="text-muted">' + response.created_date + '</small>';
const commentContent = document.createElement("p");
commentContent.setAttribute("article-content mr-4");
commentContent.innerHTML = response.content;
mediaBody.appendChild(metaData);
mediaBody.appendChild(commentContent);
_el.appendChild(_elImg);
_el.appendChild(mediaBody);
})
})
<div id="comment_list">
<!-- foreach starting here and loop this below element -->
<article class="media comment">
<img class="rounded-circle article-img" src="{{ comment.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-profile' comment.author.username %}">{{ comment.author.first_name }} {{ comment.author.last_name }}</a>
<small class="text-muted">{{ comment.created_date }}</small>
</div>
<p class="article-content" "mr-4">{{ comment.content | linebreaks }}</p>
</div>
</article>
<!-- end foreach -->
</div>

Django: Show product detail data using Ajax in modal

The usecase of my app is to show a list of furnitures in the homepage. There is quick preview button in all those furnitures which when clicked should show its detail information. I tried to use ajax for that.
If i click on the furniture quick preview button, I get the clicked furniture slug from which I do the query and get that furniture detail information. Upto this, its working and also the modal is shown but could not show the content. How can i now show the content in the modal-body?
Here is what I have tried
def ajax_furniture_detail(request):
furniture_slug = request.GET.get('slug', None)
qs = Furniture.objects.get(slug=furniture_slug)
cart_obj, new_obj = Cart.objects.new_or_get(request)
context = {
'furniture': model_to_dict(qs),
'cart': model_to_dict(cart_obj),
'status': 'ok'
}
return JsonResponse(context)
{% block content %}
{% include 'furnitures/furnitures_home.html'%}
{% endblock content %}
{% block js %}
{{ block.super }}
<script>
$(document).ready(function(){
$('.quick-view-button').click(function() {
var _this = $(this);
var slug = _this.attr("data-slug");
$.ajax({
url: '/ajax/furniture',
type: "get",
data: {'slug': slug},
success: function(data) {
$('#product-quick-view-modal').modal('show');
$('#product-quick-view-modal').find('.modal-body').html(data.html);
},
error: function(err) {
console.log('error', err);
}
})
})
});
</script>
{% endblock js %}
furnitures_home.html
{% load static %}
<div class="furnitures" id="content">
{% for furniture in furnitures %}
{% if forloop.first %}<div class="row products">{% endif %}
<div class="col-md-4 col-sm-3">
<div class="product">
<div class="image" style="height: 205px;">
<div class="quick-view-button" data-slug={{ furniture.slug }}>
Quick view
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
<div class="modal fade" id="product-quick-view-modal" tabindex="-1" role="dialog" aria-hidden="false" style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-body">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<p>Hello</p>
<p>{{furniture.name}}</p>
</div>
</div>
</div>
<!--/.modal-dialog-->
</div>
One neat way to do this is to use a snippet html for product detail and send the product detail html snippet using render_to_string and just replace that html snippet in the modal.
rendered = render_to_string('product_detail_snippet.html', context,
context_instance=RequestContext(request))
return JsonResponse({'product_snippet': rendered})
And in the ajax success:
$('#product-quick-view-modal').find('.modal-body').html(data.product_snippet);

Categories

Resources