Highcharts in Django using ajax - javascript

I am new to Django. I am making polls application and want to use highcharts to display graphs. I have a template which displays all the polls. I want to show the corresponding results of poll beside it in form of chart. Part of the template:
{% for question in poll_questions %}
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-sm-12">
{{question.question_text}}
{% for choice in question.poll_choices_set.all %}
<div>
<label><input type="radio" name="{{question.pk}}" value="{{ choice.pk }}">&nbsp {{choice.choice_text}}</label>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div id="{{ question.pk }}" class="chart" style="height: 400px; width: 400px"></div><br>
<script>
var question_id = '{{ question.pk }}'
</script>
<script src="{% static 'er_interface/polls.js' %}"></script>
</div>
{% endfor %}
Javascript file- polls.js:
$.getJSON('/er/poll/'+question_id ,function(data) {
$('#'+question_id).highcharts({
chart: {
type: 'pie'
},
title: {
text: question_id
},
tooltip: {
pointFormat: ':<b>{point.y}</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: false
},
showInLegend: true
},
series: {
dataLabels: {
enabled: true,
formatter: function() {
return Math.round(this.percentage*100)/100 + ' %';
},
distance: 0,
}
}
},
series: data
});
});
The problem is that graph is shown just for the last poll question. Thanks

This is because in each of the for loops, question_id is overwritten. You might want to do something like this instead:
$('.chart').each(function() {
var that = this;
var question_id = $(this).attr('id');
$.getJSON('/er/poll/'+question_id ,function(data) {
$(that).highcharts({
...
That way, after rendering the page, you loop through each of the divs and render the appropriate chart.
Even better: use data-id="{{ book.id }}" and retrieve it with $(this).data('id') do avoid using the id attribute for something it's not really meant for.

Related

Javascript doesn't execute when the html is called by a hx-get

I have the following html's that i load in a Django app:
pie_chart.html
{% load i18n %}
<div id="chartcontainer" >
<div style= "width: 400px;" "height: 400px;">
<canvas id="pie-chart"></canvas>
<b>Balance: </b> {{balance}}<br>
</div>
<div>
<h5>Detail group:
<select name="group" id="group"
hx-get="groupdetail/"
hx-trigger="click changed delay:1s"
hx-target="#groupchart"
hx-include="[name='year']"
hx-swap="innerHTML"
>}
{% for l in labels %}
{% if l == groupcode %}
<option value={{l}} selected>{{l}}</option>
{% else %}
<option value={{l}}>{{l}}</option>
{% endif %}
{% endfor %}
</select>
</h5>
</div>
<div id=groupchart>
{% include 'bar_chart.html' %}
</div>
</div>
<script>
var config_pie = {
type: 'pie',
data: {
datasets: [{
data: {{ data|safe }},
backgroundColor: {{ backgroundcolor|safe }},
label: 'Amount_EUR',
}],
labels: {{ labels|safe }}
},
options: {
responsive: true
}
};
var ctx_pie = document.getElementById('pie-chart').getContext('2d');
window.myPie = new Chart(ctx_pie, config_pie);
</script>
bar_chart.html
{% load i18n %}
<div>
<div style= "width: 400px;" "height: 400px;">
<canvas id="group-chart"></canvas>
<b>Balance: </b> {{groupbalance|safe}} <br>
<p id="data"> her should com the config_bar var</p>
</div>
</div>
<script>
var config_bar = {
type: 'bar',
data: {
datasets: [{
data: {{ groupdata|safe }},
backgroundColor: {{ backgroundcolor|safe }},
label: 'Amount_EUR',
}],
labels: {{ grouplabels|safe }}
},
options: {
indexAxis: 'y',
responsive: true
}
};
var ctx_bar = document.getElementById('group-chart').getContext('2d');
window.myBar = new Chart(ctx_bar, config_bar);
document.getElementById("data").innerHTML = config_bar;
</script>
When the bar_chart.html is called by the {% include 'bar_chart.html' %}, the script is executed and the chart is properly displayed.
When the bar_chart.html is called by the Django view called with hx-get="groupdetail/", the script is not executed. The html is displayed, but the graph is not build, because the script is not executing.
Already 2 days now I'm breaking my head on this and searching internet for an answer.
If someone could tell me why this is happening, it would be great.
I tried to put the html that has to be replace in and without , change hx-swap="innerHTML" for hx-swap="outerHTML", but nothing changes.
I found what the problem was. When loading the 'bar_chart.html' with hx-get I didn't define the 'backgroundcolor' variable. This missing variable blocked the execution of the script.
I will also look further how I can use the suggestion made by 'mariodev'.
Thanks for the suggestion.

Drag and drop a task with FullCalendar.js in symfony project

I am trying to implement a drag and drop using FullCalendar.js.
I saw in documentation to enable option editable to true for moving event but this don't work.
On the render, you can see my task, i just want to move my task to monday (for exemple).
Do you have suggestion for where error come from ?
Or an issue ?
Thanks !
this is my code
{% extends 'base.html.twig' %}
{% block title %}Calendrier{% endblock %}
{% block body %}
<div>
<a class="btn btn-secondary" href="{{ path('home') }}">
<i class="fas fa-long-arrow-alt-left"></i> Retour</a>
</div>
<div class="row mt-5">
{% if is_granted("ROLE_MANAGER") %}
<div class="col d-flex my-3 justify-content-end">
{{ form_start (form, {'attr': {'class': 'd-flex'}}) }}
{{ form_widget(form.technicien, {'attr': {'class': 'd-inline me-2'}}) }}
{{ form_widget(form.techniciens, {'attr': {'class': 'd-none'}}) }}
<input id="submitBtn" class="btn btn-success d-inline" type="submit" value="Filtrer">
{{ form_end(form) }}
</div>
{% endif %}
<div class="col-12">
<div id="calendar" class="d-flex justify-content-center">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
</div>
</div>
{{ include('loading_spinner.html.twig') }}
<script>
var mainRole = '{{mainRole}}';
if(mainRole == 'MANAGER') {
var idTechniciens = '{{partnerTechniciens}}';
var idTechniensArray = idTechniciens.split(' ');
var technicienSelect = $('#ticket_intervention_user_technicien');
var options = $('#ticket_intervention_user_technicien option');
var optionsToHide = $.map(options ,function(option) {
var extra = new Array();
if(!idTechniensArray.includes(option.value)) {
extra.push(option);
}
return extra;
});
optionsToHide.forEach(function(option) {
option.style.display = 'none'; });
}
</script>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src='fullcalendar/main.js'></script>
<script>
window.onload = () => {
calendar('#calendar', {
'events': {{ data|raw }},
'enventStartEditable': true,
'editable': true,
//'eventResizableFromStart': true,
'nowIndicator': true,
eventDidMount: function(info) {
let tooltip = new bootstrap.Tooltip(info.el, {
title: ('Client : ' + info.event.extendedProps.description),
placement: 'top',
trigger: 'hover',
container: 'body'
});
}
});
}
</script>
{% endblock %}
This is the render of my calendar:
in my app.js :
import { calendar } from "./full-calendar";
global.calendar = calendar;
How can i do it ?
ok i change the render with the documentation and it's work
window.onload = () => {
let calendarElt = document.querySelector("#calendar")
let calendar = new FullCalendar.Calendar(calendarElt, {
initialView: 'timeGridWeek',
locale: 'fr',
timeZone: 'Europe/Paris',
headerToolbar: {
start: 'prev,next today',
center: 'title',
end: 'dayGridMonth,timeGridWeek'
},
events: {{ data|raw }},
editable: true,
nowIndicator: true,
eventResizableFromStart: true,
eventDidMount: function(info) {
let tooltip = new bootstrap.Tooltip(info.el, {
//title: ('Client : ' + info.event.extendedProps.description),
placement: 'top',
trigger: 'hover',
container: 'body'
});
}
})

Creating a Highchart for every data set

I'm sure that someone already encountered this problem and posted here, but I just couldn't find it. So sorry for the dupe.
I am making a dashboard where there has to be a chart for every data set from the database.
I tried doing it with sending the data to a directive and draw the chart from within the directive's controller with the data passed to the directive.
This is the partial view
<div class="row">
<div class="col col-md-9">
<input type="text" class="form-control" ng-model="nameFilter" placeholder="Search for...">
</div>
</div>
<div class="row" ng-if="loading">
<div class="col col-md-6 col-md-offset-3">
<img src="../img/loader.gif"/>
</div>
</div>
<div class="row" ng-repeat="app in apps | filter: nameFilter">
<app-card name="app.name" android="app.android" ios="app.ios" date="app.date"></app-card>
</div>
The partial sends data to the directive
var app = angular.module('app');
app.directive('appCard', function() {
return {
restrict: 'E',
scope: {
name: '=name',
android: '=android',
ios: '=ios',
date: '=date'
},
templateUrl: 'http://testsite.nl/dashboard/partials/directives/app-card.html'
};
});
The directives template
<div class="panel panel-info app-card" ng-controller="appCardController">
<div class="panel-heading">
<div class="row">
<h4 class="col col-md-5">{{name}}</h4>
</div>
</div>
<div class="panel-body">
<div class="col col-md-12">
<h3>Android downloads: {{android}}</h3>
<h3>iOS downloads: {{ios}}</h3>
<div id="container" style="min-width: 310px; height:250px; margin: 0 auto;"></div>
</div>
</div>
</div>
And the controller looks like this
app.controller('appCardController', ['$scope', function($scope){
console.log("Downloads: " + $scope.android);
Highcharts.chart('container', {
chart: {
type: 'area'
},
title: {
text: 'Aantal downloads'
},
xAxis: {
allowDecimals: false,
categories: $scope.date
},
yAxis: {
title: { text:'Donwloads' },
floor: 0,
ceiling: getMaxDownloadCount()
},
tooltip: {
pointFormat: '{series.name} <b>{point.y:,.0f}</b>'
},
series: [{
name: 'Android',
data: convertAndroidArrayToInts($scope.android)
},
{
name: 'iOS',
data: convertIosArrayToInts($scope.ios)
}]
});
function convertAndroidArrayToInts(array){
var androidArray = new Array();
for(var i =0; i < array.length; i++){
androidArray.push(parseInt(array[i]));
}
return androidArray
}
function convertIosArrayToInts(array){
var iosArray = new Array();
for(var i =0; i < array.length; i++){
iosArray.push(parseInt(array[i]));
}
console.log(iosArray);
return iosArray
}
}]);
It shows the directive templates with the correct name and array values, the only problem is that there is only one chart drawn, which is the chart beloning to the last item in the list, but it is located in the first template.
How would I be able to make it so every template contains a chart beloning to its data?
I answered a very similar question related to using angular-chart, see my plunker here: https://plnkr.co/edit/8fJ4u0U2fL4lYTioCbG0?p=preview
The question/answer here: angular ng-repeat with directive
The actual issue you face is that the ID is container, and you cannot have duplicate "id" attributes in the DOM, so it will only find the first one.
Assuming the "name" is unique, change the controller so it uses the directives scope.name on Highchart.chart and change the directive template so the <div id="container"> becomes <div id="{{name}}"
After this you should have unique ID's and it will work
Hope that makes sense.

reactive.js + displaying boards

I can't seem to understand why I can't spam click my "create board" button and keep creating boards
when I add a board the new empty board dict should be prepended to the front of the array but only one board will show up then no more..Thanks for the help!!!
<div id="target"></div>
<script id="template" type="text/ractive">
<button class="btn btn-primary" on-click="add_board"><i class="fa fa-plus"></i> Board</button>
<br><br>
editing: {% editing %}
{% #board_list:title %}
{% title %}
{% /board_list %}
<div class="board_list">
{% #board_list:name %}
<div class="board">
{% #if editing %}
<textarea id="editarea" on-blur="editdone" data-areaid="3" value="{% text %}"></textarea>
{% else %}
<div on-click="startedit"><p>{% text %}</p></div>
{% /if %}
</div>
{% /board_list %}
</div>
</script>
<script src='http://cdn.ractivejs.org/latest/ractive.min.js'></script>
<script>
$(function() {
// Ractive object
var MAIN = new Ractive({
el: '#target',
template: '#template',
delimiters: ['{%', '%}'],
tripleDelimiters: ['{%%', '%%}'],
data: {
editing: false,
board_id: -1,
text: "Edit Me",
board_list: [],
loading: false
},
});
MAIN.on("add_board", function() {
board_list = MAIN.get('board_list');
alert(board_list);
var empty_board = {title: ''};
board_list.splice(0, 1, empty_board);
MAIN.set('board_list', board_list);
});
MAIN.on("startedit", function() {
MAIN.set("editing", true);
$("#editarea").focus();
});
MAIN.on("editdone", function() {
MAIN.set("editing", false);
var text = MAIN.get("text");
alert(text);
if (text.trim().length > 0) {
//update_board()
alert('not empty');
}
else {
//delete_board()
alert('empty');
}
});
The second argument in splice is the number of elements to remove, you currently have it set to remove 1. If you change it to 0 then it will add more.
board_list.splice(0, 0, empty_board);
https://jsfiddle.net/6qz84476/

Why is HighCharts only writing to one of my dynamic div's?

I'm trying to create some charts using highcharts, and doing so with dynamic div's.
It's successful on the first chart, but the rest are blank. Viewing the html source I would assume everything is ok.
Here are my dynamically created div's:
<!-- Break Down of all Signature names in each class -->
<% n = 0 %>
<% #sig_class_name_info.each do |scn_chart| %>
<div>
<%= scn_chart[:sig_class_name] %>
</div>
<table class="table">
<tr>
<td style='width:100%'>
<div class="panel panel-warning well">
<div class="panel-heading">
<h3 class="panel-title"></h3>
</div>
<div id="alert_name_bar_chart<%=+n%>" class="panel-body text-center">
</div>
</div>
</td>
</tr>
</table>
<%n = n+1%>
<% end %>
This is the html source created:
<!-- Break Down of all Signature names in each class -->
<div>
web-application-attack
</div>
<table class="table">
<tr>
<td style='width:100%'>
<div class="panel panel-warning well">
<div class="panel-heading">
<h3 class="panel-title"></h3>
</div>
<div id="alert_name_bar_chart0" class="panel-body text-center">
</div>
</div>
</td>
</tr>
</table>
<div>
attempted-admin
</div>
<table class="table">
<tr>
<td style='width:100%'>
<div class="panel panel-warning well">
<div class="panel-heading">
<h3 class="panel-title"></h3>
</div>
<div id="alert_name_bar_chart1" class="panel-body text-center">
</div>
</div>
</td>
</tr>
</table>
<div>
misc-attack
</div>
<table class="table">
<tr>
<td style='width:100%'>
<div class="panel panel-warning well">
<div class="panel-heading">
<h3 class="panel-title"></h3>
</div>
<div id="alert_name_bar_chart2" class="panel-body text-center">
</div>
</div>
</td>
</tr>
</table>
<div>
policy-violation
</div>
<table class="table">
<tr>
<td style='width:100%'>
<div class="panel panel-warning well">
<div class="panel-heading">
<h3 class="panel-title"></h3>
</div>
<div id="alert_name_bar_chart3" class="panel-body text-center">
</div>
</div>
</td>
</tr>
</table>
Here is the highcharts script:
<script type="text/javascript">
var charts = [];
$(function () {
var getChartConfig = function(renderId, title, data, x_axis) {
var config = {};
config.chart = {
renderTo: renderId,
defaultSeriesType: 'column',
margin: [50, 50, 100, 80]
};
new Highcharts.Chart ({
chart: {
type: 'bar',
renderTo: renderId
},
title: {
text: title,
style: {
color: '#52508d'
}
},
xAxis: {
categories: x_axis
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function () {
location.href = this.series.options.url;
}
}
}
}
},
series: [{
data: data,
showInLegend: false,
name: 'Alerts',
url: '/ips_alert_classes?query=',
color: '#2a2874'
}]
});
}
<% n = 0%>
<% #sig_class_name_info.each do |i| %>
charts.push(new Highcharts.Chart(
getChartConfig("alert_name_bar_chart<%=+n%>",<%= raw i[:sig_class_name].to_json.html_safe %>,<%= raw i[:event_name_count].to_json.html_safe %>,<%= raw i[:event_name].to_json.html_safe %>)
))
<% n = n+1%>
<% end %>
});
</script>
Here is the html source from this highcharts script:
<script type="text/javascript">
var charts = [];
$(function () {
var getChartConfig = function(renderId, title, data, x_axis) {
var config = {};
config.chart = {
renderTo: renderId,
defaultSeriesType: 'column',
margin: [50, 50, 100, 80]
};
new Highcharts.Chart ({
chart: {
type: 'bar',
renderTo: renderId
},
title: {
text: title,
style: {
color: '#52508d'
}
},
xAxis: {
categories: x_axis
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function () {
location.href = this.series.options.url;
}
}
}
}
},
series: [{
data: data,
showInLegend: false,
name: 'Alerts',
url: '/ips_alert_classes?query=',
color: '#2a2874'
}]
});
}
charts.push(new Highcharts.Chart(
getChartConfig("alert_name_bar_chart0", "web-application-attack", [919,1,1,1], ["drop - WP-Admin attempt","Snort Alert [1:16431:5]","Snort Alert [1:19438:9]","drop - SQL use of concat function with select - likely SQL injection"])
))
charts.push(new Highcharts.Chart(
getChartConfig("alert_name_bar_chart1", "attempted-admin", [1,10,4], ["Snort Alert [1:31976:4]","drop - SERVER-OTHER Wordpress linenity theme LFI attempt","drop - OS-OTHER Bash CGI environment variable injection attempt"])
))
charts.push(new Highcharts.Chart(
getChartConfig("alert_name_bar_chart2", "misc-attack", [1], ["drop - SQL union select - possible sql injection attempt - GET parameter"])
))
charts.push(new Highcharts.Chart(
getChartConfig("alert_name_bar_chart3", "policy-violation", [2], ["drop - POLICY-OTHER Adobe ColdFusion admin interface access attempt"])
))
});
</script>
As you can see the charts.push(new Highcharts.Chart() script seems to be working as expected.
What would cause the first chart to display just fine, but the rest be blank?
UPDATE:
This is the working HighCharts setup.
<script type="text/javascript">
var charts = [];
$(function () {
var getChartConfig = function (renderId, title, data, x_axis) {
var config = {
chart: {
renderTo: renderId,
defaultSeriesType: 'bar'
},
title: {
text: title,
style: {
color: '#52508d'
}
},
xAxis: {
categories: x_axis
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function () {
//location.href = this.series.options.url;
}
}
}
}
},
series: [{
data: data,
showInLegend: false,
name: 'Alerts',
url: '/signatures?query=',
color: '#2a2874'
}]
};
return config;
}
<% n = 0%>
<% #sig_class_name_info.each do |i| %>
charts.push(new Highcharts.Chart(
getChartConfig("alert_name_bar_chart<%=n%>"
,<%= raw i[:sig_class_name].to_json.html_safe %>
,<%= raw i[:event_name_count].to_json.html_safe %>
,<%= raw i[:event_name].to_json.html_safe %>)
))
<% n = n+1%>
<% end %>
});
</script>
One important thing: you create every chart twice, first time when calling getChartConfig and second time before pushing to the charts array. So what I suggest to change:
Push chart only:
charts.push(getChartConfig(...))
and in getChartConfig method, return created chart:
return new Highcharts.Chart ({ ... });

Categories

Resources