Chart js multiple pie chart on one page - javascript

Iam building my portfolio website and i build a simple stocktracker. I added some charts from Chart.js, but somehow only one gets rendered when I excecute the code.
I managed to assign all the divs and data dynamically by making the variables dynamic. I did this by adding the portfolio id in the for loop. I can't figure out why its not working. Appreciate any help.
{% extends 'base.html' %}
{% block page_content %}
{% if user.is_authenticated %}
<body>
<div class="container">
<div class="row">
<div class="col-12">
<h2>
Welcome to the stocktracker
</h2>
<p>I build this stocktracker app to learn database operations. To get current stock data, I linked this to
the free API from Polygon.io. It's only possible to do 5 API calls per minute. You will see an error
message whenever the api calls are exceeded.
<br><br>Current features are:
<ol>
<li>Basic CRUD database operation</li>
<li>Adding portfolio's linked to user</li>
<li>Only show portfolio's linked to user</li>
<li>Show general KPI's in portfolio overview</li>
<li>KPI's calculated based on positions in portfolio</li>
<li>Adding position to portfolio</li>
<li>API connection with Polygon.io to get live stock data</li>
<li>Chart.js integration for visual representation of portfolio metrics</li>
</ol>
</p>
</div>
</div>
<div class="row">
<div class="col-8">
<h2>Select your portfolio</h2>
</div>
<div class="col-4 text-end">
<!-- Button trigger modal -->
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#addPortfolioModal">
Add new portfolio
</button>
<!-- Modal -->
<div class="modal fade" id="addPortfolioModal" tabindex="-1" role="dialog"
aria-labelledby="addPortfolioModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addPortfolioLabel">Modal title</h5>
<button type="button" class="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form name="portfolio_form" method="post">
{% csrf_token %}
<p>
{{ portfolio_form }}
</p>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close
</button>
<input type="submit" value="Submit"/>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div>
{% for portfolio in portfolios %}
<div class="row pt-4">
<div class="col-6">
<a class="btn btn-primary" href="{% url 'portfolio_detail' portfolio.pk%}">Go to {{ portfolio }}</a>
</div>
</div>
{% if portfolio.total_positions > 0 %}
<div>
{% load static %}
<div class="col-4 text-start" style="width: 50%;">
<canvas id="pie-chart{{ portfolio.pk }}"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<script>
var randomNum_{{ portfolio.pk }} = () => Math.floor(Math.random() * (235 - 52 + 1) + 52);
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
countPositions = {{ portfolio.total_positions|safe }};
var barColors_{{ portfolio.pk }} = [];
for (let i = 0; i < countPositions; i++) {
randomGeneratedColor = rgbToHex(randomNum_{{ portfolio.pk }}(), randomNum_{{ portfolio.pk }}(), randomNum_{{ portfolio.pk }}());
while (barColors_{{ portfolio.pk }}.includes(randomGeneratedColor)) {
randomGeneratedColor = rgbToHex(randomNum(), randomNum(), randomNum());
}
barColors_{{ portfolio.pk }}.push(randomGeneratedColor);
}
var config_{{ portfolio.pk }} = {
type: 'pie',
data: {
datasets: [{
data: {{ portfolio.data_for_chart_array|safe }},
backgroundColor: barColors_{{ portfolio.pk }},
label: 'Stock ticker'
}],
labels: {{ portfolio.labels_array|safe }}
},
options: {
responsive: true
}
};
window.onload = function() {
var ctx = document.getElementById('pie-chart{{ portfolio.pk }}').getContext('2d');
window.myPie = new Chart(ctx_{{ portfolio.pk }}, config_{{ portfolio.pk }});
};
</script>
</div>
</div>
{% endif %}
<div class="row">
<div class="col-4 pt-2">
<b>Total amount invested:</b> {{ portfolio.total_amount_invested }}
<br><b>Positions:</b> {{ portfolio.total_positions }}
<br><b>Profit:</b> {{ portfolio.total_profit }}
<br><b>Profit in %:</b> {{ portfolio.total_profit_percentage }} %
</div>
</div>
</div>
{% endfor %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"></script>
</div>
</body>
{% endif %}
{% endblock %}
Somehow only the last chart gets rendered instead of all three.

You have a loop there, and in each iteration of the loop, you're using window.onload = function() {…};
Basically, you're overwriting the onload function at each iteration of the loop, so only the last overwrite survives.
What you want is to add load event listeners on the window instead of continually overwriting one single listener.
To achieve this, I would recommend replacing this:
window.onload = function () {…}
with this:
window.addEventListener('load', function() {…})`
See addEventListener
Another way of seeing that window.onload = function () {…} is problematic is the following snippet:
window.onload = function () { alert('one') };
window.onload = function () { alert('two') };
Q: What do you expect to happen on page load here?
A: Similarly to your case, only the second function (alerting "two") will be called.

Related

How to change contents on clicking the selected div using Javascript?

I am new to Javascript. I wish to change the contents of div on selecting a particular div element.
If I select a div, the text associated with that div contents only should display. I am using a loop to display the contents. It's like a chat application. If a user clicks on a contact the messages associated with that user should display. I am using a loop to display messages and users.
What I have tried is:
Twig code:
{% set msg = query().from('messages').get() %}
{% set to_add =to_name %}
{% set to_details = query().from('users_users').where('id', to_add).get() %}
<div class="container">
<h3 class=" text-center">Messaging</h3>
<div class="messaging">
<div class="inbox_msg">
<div class="inbox_people">
<div class="headind_srch">
<div class="recent_heading">
<h4>{{auth_user().first_name}}</h4>
</div>
<div class="srch_bar">
<div class="stylish-input-group">
<input type="text" class="search-bar" placeholder="Search" >
<span class="input-group-addon">
<button type="button"> <i class="fa fa-search" aria-hidden="true"></i> </button>
</span> </div>
</div>
</div>
<div class="inbox_chat">
{% set newArray = [] %}
{% for msg in msg_details %}
{% if msg.to_name not in newArray %}
<div class="chat_list active_chat" onclick=viewMessage(this)>
<div class="chat_people">
<div class="chat_img"> <img src="https://ptetutorials.com/images/user-profile.png" alt="sunil"> </div>
<div class="chat_ib">
<h5>{{msg.to_name}} <span class="chat_date">{{msg.time}}</span></h5>
</div>
</div>
</div>
{% set newArray = newArray|merge([msg.to_name]) %}
{% endif %}
{% endfor %}
</div>
</div>
<div class="mesgs">
<div class="msg_history">
<div class="incoming_msg">
<div class="incoming_msg_img">
<img src="https://ptetutorials.com/images/user-profile.png" alt="sunil">
</div>
<div class="received_msg">
<div class="received_withd_msg">
<p>{{msg.message}}</p>
<span class="time_date">{{msg.time}}</span></div>
</div>
</div>
<div class="outgoing_msg">
<div class="sent_msg">
<p></p>
<span class="time_date"></span> </div>
</div>
</div>
</div>
</div>
</div></div>
Javascript code:
<script>
function copyText(elementId){
var x = document.getElementById(elementId).textContent;
if(document.getElementById('select-text').value.length == 0)
{
document.getElementById("btn").disabled = false;
document.getElementById(elementId).style.backgroundColor = "lightblue";
document.getElementById('select-text').focus();
document.getElementById('select-text').value += x;
}
else{
document.getElementById('select-text').readOnly = true;
}
}
</script>
What my output looks like
Output
My output shows all the messages, I want to show only the particular user message. How to do so?
What my table looks like:
Table
First of all you have to fetch the data from your table using a query something like to ensure you will get one record from each from_id.
SELECT * FROM msg GROUP BY from_id;
Then you can loop in twig to create the left side elements like,
{% for distinctMsg in distinctMsgs %}
<div class="chat_list active_chat" data-userId="{{ distinctMsg.fromId }}">
<div class="chat_people">
<div class="chat_img"> <img src="https://ptetutorials.com/images/user-profile.png" alt="sunil"> </div>
<div class="chat_ib">
<h5>{{distinctMsg.to_name}} <span class="chat_date">{{distinctMsg.time}}</span></h5>
</div>
</div>
</div>
{% endfor %}
Then in your javascript:-
$(".chat_list").on("click", function(){
// have a look here https://stackoverflow.com/questions/5309926/how-can-i-get-the-data-id-attribute
var userId = $(this).data( "userId" );
// Maybe display a loading icon in the right side div
$.ajax({
type: "POST",
url: "/api/msg/user/history", // your controller url
dataType: 'json',
headers: {
// if you have any headers to set
"Authorization": "Bearer {{ token }}"
},
data:{
"userId" : userId
}
})
.done(function (response) {
// stop displaying the loading icon
// process the response and display message history in right side
})
.fail(function (xhr, status, error) {
// show proper error message
});
});
NOTE: You will have to have jQuery in your page for Ajax functions to work.

Django AJAX Infinite Scroll Error on page reload

I'm trying to implement infinite scroll with django and jquery(Waypoint).
I have a ListView with a pagination of 5, but when waypoint loads second page, AJAX requests are no longer performed so I added the AJAX function on the onAfterPageLoad which helps bring back AJAX function to the newly loaded page.
That's fine, but then it introduces a bug to my code making the page loaded initially (First Page) no longer perform AJAX functions very well. It makes AJAX on the first page run 3 times if I just loaded a third page and makes AJAX on the second page run twice and so on depending on the number of pages loaded already.
I don't know if there are any cool ways to achieve this because I tried using just jquery without waypoint, but still get same errors as I get when using just waypoint making it an error. This is kinda tricky so far.
{% extends "base.html" %}
{% block content %}
{% load static %}
<div class="container" style="max-width:700px">
<div class="px-3 pt-md-5 pb-md-4 mx-auto text-center">
<h1 class="display-4">All Groups</h1>
<p class="lead">List of groups</p>
</div>
<div class="">
<div class="row infinite-container">
{% for group in groups %}
<div class="col-md-12 infinite-item">
<!-- <img class="img-fluid" src="https://picsum.photos/700"> -->
<a class="text-dark" href="{{ group.get_absolute_url }}">
<div class="card mb-4 box-shadow">
<div class="card-body">
<h2 style="font-size:18px;font-weight:bold;min-height:42px;">
{{group.name|truncatechars:50}}</h2>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">{{group.owner}}</small>
<small class="text-muted">{{group.date_created}}</small>
</div>
<p><a class='follow-btn' data-href='{{ group.get_group_follow_api_url }}'
data-likes='{{ page.likes.count }}'
href='{{ group.get_group_follow_url }}'>{{ group.follows.count }}
{% if request.user.profile in group.follows.all %}
Unfollow
{% else %}
Follow
{% endif %}
</a></p>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
<!-- Comment for loading spinner -->
{% comment %}
<div class="d-flex d-none position-fixed" style="top:35vh;left:46vw">
<button class="btn btn-primary loading">
<span class="spinner-border spinner-border-sm"></span>
Loading...
</button>
</div>
{% endcomment %}
<!-- End of comment for loading spinner -->
<div class="row">
<div class="col-12">
{% if page_obj.has_next %}
<a class="infinite-more-link" href="?page={{ page_obj.next_page_number }}"></a>
{% endif %}
</span>
</div>
</div>
</div>
</div>
<script src="{% static "js/jquery.waypoints.min.js" %}"></script>
<script src="/static/js/infinite.min.js"></script>
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0],
offset: 'bottom-in-view',
onBeforePageLoad: function () {
$('.loading').show();
},
onAfterPageLoad: function () {
$('.loading').hide();
$(document).ready(function(){
function updateText(btn, newCount, verb){
btn.text(newCount + " " + verb)
}
$(".follow-btn").click(function(e){
e.preventDefault()
var this_ = $(this)
var badge = this_.find('.unlike-update')
var followUrl = this_.attr("data-href")
var followCount = parseInt(this_.attr("data-follows")) | 0
var addFollow = followCount + 1
var removeFollow = followCount - 1
if (followUrl){
$.ajax({
url: followUrl,
method: "GET",
data: {},
success: function(data){
console.log(data)
var newFollows;
if (data.followed){
// updateText(this_, addLike, "Unlike")
// updateText(this_, data.likescount, badge)
updateText(this_, data.followscount, "Unfollow")
} else {
// updateText(this_, removeLike, "Like")
updateText(this_, data.followscount, "Follow")
// remove one like
}
}, error: function(error){
console.log(error)
console.log("error")
}
})
}
})
})
}})
</script>
{% include 'group/snippets/group_follow.html' %}
{% endblock %}
class GroupListView(LoginRequiredMixin, ListView):
model = Group
paginate_by = 5
context_object_name = 'groups'
template_name = 'group/group_list.html'
ordering = ['-date_created']

Why is Javascript not working with Django HTML?

I am trying to put some javascript code in my Django website. The javascript code is supposed to check if the buttons are being pressed and alert the user if they are. This only works for the first button, however. Because I am looping over all the buttons to display them, I think javascript is not seeing it as HTML code. Here is the javascript code.
const card = document.querySelector('.card');
card.addEventListener('click', e => {
if (e.target.classList.contains('btn')) {
alert('hi')
}
})
Here is the HTML/Django code :
{% extends "/Users/marco/Documents/codes/Python/Django/vracenmasse/templates/base_generic.html" %}
{% load static %}
{% block content %}
<center><h1>Nos Produits</h1></center>
<hr>
<div class="row" style = "border: 50px solid white;">
{% for product in products %}
<div class="card" style="width: 18rem;">
<img class="card-img-top" src="../../../media/{{ product.product_picture.name }}" alt="{{ product.product_picture.name }}">
<div class="card-body">
<center>
<h5 class="card-title">{{ product.name }}</h5>
<p class="card-text">{{ product.product_price|floatformat:2 }}$ / unité</p>
<p class="card-text">Catégorie : {{ product.type_of_product }}</p>
{% if product.new_product == "yes" %}
<b><p class="card-text"><font size="3.5" color="Orange">Nouveaux!</font></p></b>
{% endif %}
{% if product.availability == "Out Of Stock"%}
<b><p class="card-text"><font size="1.5" color="red">En rupture de stock</font></p></b>
<hr>
<button type="button" class="btn btn-primary" disabled>Ajouter au Panier</button>
{% else %}
<p style="font-size:15;">Quantité : </p><input type="number" step="12" min="0" />
<span class="validity"></span>
<hr>
<button type="button" class="btn btn-outline-primary" id='realbutton'>Ajouter au Panier</button>
{% endif %}
<div class="alert alert-success alert-dismissible fade show" role="alert" style ="visibility:hidden;">
<center>Produit ajouté au panier!</center><button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</center>
</div>
</div>
{% endfor %}
</div>
</div>
<script src="{% static "products/buttonalerts.js" %}"></script>
{% endblock %}
The first button is the only thing that works. We can see on line #6 there is the for loop. It iterates over all the products and it adds its own "Add To Cart" Button. The alert only works for the first button though. Thank you.
The problem is that you are creating a “div” element for each product in your list, but the querySelector() method only returns the first element with the specified class.
You need to use the querySelectorAll() method to get a list with all the elements then apply the event listener to each of them:
const cards = document.querySelectorAll('.card');
for (let i = 0; i < cards.length; i++) {
cards[i].addEventListener('click', e => {
if (e.target.classList.contains('btn')) {
alert('hi')
}
}
)
Another solution would be to query directly for the elements that contain the “btn” class and add the event listener to those:
const buttons = document.querySelectorAll(".btn");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
alert('hi');
}
)

Using Django Models to assign HTML div through jQuery

for the last few days I've been trying to create a method in jquery using django models and I have found myself to be very out of my depth and would appreciate and explanation I can get. So currently I have a django model that has the following information : name, date, location, semester. I use the first 3 pieces of information in displaying my html, however, I want to use 'semester' to see what div tag my items go into.
The semester tag can either return 'Fall' or 'Spring' values is there a way I can use this to assign the components to the correct div. So if semester is Fall then it should go into the div with the id 'fall-races' and if its spring it should go to 'spring-races'
Currently I only have a jquery working where I get all the elements and assign it to the other div.
Thank you for your help and any possible advice.
<div class="flex-column">
<div class="header shadow-lg">
<h1 class="text-center py-3">
Fall Schedule
</h1>
</div>
<div id="fall-races">
{% for race in race %}
<div class="regatta-card my-2 mx-2 ">
<h2 class="center-text py-2">{{ race.date }}</h2>
<h1 class="text-center my-2">{{ race.name }}</h1>
<h3 class="text-center mb-3">{{ race.location}}</h3>
<h6 class="text-center">Recap</h6>
</div>
{% endfor %}
</div>
<input type="button" onclick="findChildren()" value="Click it" />
<div class="header shadow-lg">
<h1 class="text-center py-3">
Spring Schedule
</h1>
</div>
<div id="spring-races">
</div>
</div>
<script>
function findChildren() {
var objChl = document.getElementById('fall-races').children;
var msg = document.getElementById('spring-races');
msg.innerHTML = '';
for (i = 0; i < objChl.length ; i++) {
msg.appendChild(objChl[i]);
}
}
</script>
Maybe you want something like this:
Html:
<!-- Some html stuff -->
<div id="fall-races">
{% for race in race %}
<div class="regatta-card my-2 mx-2 ">
<h2 class="center-text py-2">{{ race.date }}</h2>
<h1 class="text-center my-2">{{ race.name }}</h1>
<h3 class="text-center mb-3">{{ race.location}}</h3>
<h6 class="text-center">Recap</h6>
<input type="hidden" value="{{race.semester}}" />
</div>
{% endfor %}
</div>
<!-- Some html stuff -->
JS:
<script>
function findChildren() {
var parent = document.getElementById('fall-races');
var objChl = parent.children;
var msg = document.getElementById('spring-races');
msg.innerHTML = '';
for (i = 0; i < objChl.length ; i++) {
const element = objChl[i];
var value = element.getElementsByTagName("input")[0].value;
if (value==="Spring"){
msg.appendChild(element);
parent.removeChild(element);
}
}
}
</script>
This is like the simplest way to achieve that, hope it helps you.

Uploading files in Django using jQuery

I am trying to upload multiple files for a blog post object, however, it seems that my Ajax form is not working properly and I cannot see the files uploading.
Since there is no post object at the time of creating the images, I am trying to upload the files then get them in my view and after saving the post I am trying to save those files by assigning the id of that post to them. Currently, my issue is it seems my files are not uploading and I cannot get them at all.
I am not getting any error and therefore, I cannot find an issue.
Below is my file upload and post create view:
#login_required
def post_create(request):
data = dict()
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(False)
post.author = request.user
#post.likes = None
post.save()
for image in request.FILES.getlist('file'):
instance = Images(post=Post.objects.get(post.id),image=image)
instance.save()
data['form_is_valid'] = True
posts = Post.objects.all()
posts = Post.objects.order_by('-last_edited')
data['posts'] = render_to_string('home/posts/home_post.html',{'posts':posts},request=request)
else:
data['form_is_valid'] = False
else:
form = PostForm
context = {
'form':form
}
data['html_form'] = render_to_string('home/posts/post_create.html',context,request=request)
return JsonResponse(data)
class PostImageUpload(LoginRequiredMixin, View):
def get(self, request):
images = Images.objects.all()
return render(self.request, 'home/posts/post_create.html', {'images':images} )
def post(self, request):
data = dict()
form = ImageForm(self.request.POST, self.request.FILES)
if form.is_valid():
image = form.save(False)
image.save()
data = {'is_valid': True, 'name': image.file.name, 'url': image.file.url}
else:
data['is_valid'] = False
return JsonResponse(data)
This my javascript code:
$(document).ready(function(){
$(".js-upload-images").click(function () {
$("#fileupload").click();
});
$("#fileupload").fileupload({
change : function (e, data) {
if(data.files.length >= 4){
alert("Sorry, you can only upload up to 4 images")
return false;
}
},
dataType: 'json',
sequentialUploads: true,
start: function (e) {
$("#modal-progress").show();
},
stop: function (e) {
$("#modal-progress").hide();
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
var strProgress = progress + "%";
$(".progress-bar").css({"width": strProgress});
$(".progress-bar").text(strProgress);
},
done: function (e, data) {
if (data.result.is_valid) {
$("#image_list tbody").prepend(
"<tr><td><a href='" + data.result.url + "'>" + data.result.name + "</a></td></tr>"
)
}
}
});
});
HTML Code
{% load crispy_forms_tags %}
{% load static %}
<script src="{% static 'js/image_upload.js' %}"></script>
<script src="{% static 'js/jquery-file-upload/js/vendor/jquery.ui.widget.js' %}"></script>
<script src="{% static 'js/jquery-file-upload/js/jquery.iframe-transport.js' %}"></script>
<script src="{% static 'js/jquery-file-upload/js/jquery.fileupload.js' %}"></script>
<form method="POST" data-url="{% url 'home:post-create' %}" class="post-create-form">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title" >Create a 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|crispy }}
<div class="my-3 mx-3">
<button type="button" class="btn btn-sm mr-auto btn-primary js-upload-images">
<span><i class="fas fa-camera"></i></span>
</button>
<input id="fileupload" type="file" name="file" multiple
style="display: none;"
data-url="{% url 'home:post-image-upload' %}"
data-form-data='{"csrfmiddlewaretoken": "{{ csrf_token }}"}'>
<table id="image_list" class="table table-bordered my-2">
<tbody>
{% for image in images %}
<tr>
<td>{{ image.file.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="modal fade" id="modal-progress" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Uploading...</h4>
</div>
<div class="modal-body">
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: 0%;">0%</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Post</button>
</div>
</form>
The fileupload framework that I am using is working as well as I can see in the terminal Django is successfully completing the get request.
I appreciate all the help in advance!

Categories