Dynamic ChartJS Chart Updated in Rails with AJAX - javascript

I am using Rails 5 and the latest ChartJS library (http://www.chartjs.org/docs/).
What I want to accomplish is to GET the latest 20 items from the SensorReading model and update the Chart with setInterval and AJAX.
I've built the AJAX call and I've loaded the Chart but I fail in two things.
First of of all I get a Syntax Error when reading data from Rails:
SyntaxError: expected expression, got '&'
var labels = ["2016-07-03 10:33:49 +0300", "2016-07-03 10:3
No matter what I tried, they keep to appear with " instead of quotes.
Secondly I am unable to update the Chart, as I need a handler available for the Chart itself to call .update() on it.
index.html.erb
<h1>Dashboard</h1>
<div class="ui divider"></div>
<div class="ui padded grid">
<div class="four wide column">
<div class="ui statistic">
<div class="value">
<%= #temperature %>.C
</div>
<div class="label">
TEMPERATURE
</div>
</div>
<br>
<div class="ui statistic">
<div class="value">
<%= #humidity %>%
</div>
<div class="label">
HUMIDITY
</div>
</div>
</div>
<div class="twelve wide column">
<div class="ui segment">
<div class="line-chart" style="max-height: 400px; display:block;">
<canvas id="updating-chart"></canvas>
</div>
</div>
</div>
</div>
<br>
<script>
var labels = <%= #sensor_readings.map(&:created_at) %>;
var canvas = document.getElementById('updating-chart'),
ctx = canvas.getContext('2d'),
startingData = {
labels: labels,
datasets: [
{
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
data: <%= #sensor_readings.map(&:temperature) %>
},
{
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
data: <%= #sensor_readings.map(&:humidity) %>
}
]
},
latestLabel = startingData.labels[6];
// Reduce the animation steps for demo clarity.
var myLiveChart = new Chart(ctx , {
type: "line",
data: startingData,
animationSteps: 15
});
setInterval(function(){
// Add two random numbers for each dataset
//myLiveChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
myLiveChart.update(); // Calling update now animates the position of March from 90 to 50.
}, 5000);
</script>
dashboard.js
var ready = function(){
setInterval(refreshSensorReadings, 3000);
function refreshSensorReadings(){
console.log("--> Called");
$.rails.ajax({
type: "GET",
dataType: 'script',
url: "/sensor_readings_chart_data.js",
success: function(result){
//$('.line-chart').html(result);
console.log(result);
}
});
};
};
$(document).on('turbolinks:load', ready);
route
get 'sensor_readings_chart_data', to: 'sensor_readings#chart_data'
sensor_readings_controller.rb
def chart_data
#sensor_readings = SensorReading.last(20)
respond_to do |format|
format.json { head :no_content }
format.js
end
end
Any advice will be appreaciated.

Try the html_safe method:
Marks a string as trusted safe. It will be inserted into HTML with no
additional escaping performed. It is your responsibilty to ensure that
the string contains no malicious content. This method is equivalent to
the raw helper in views. It is recommended that you use sanitize
instead of this method. It should never be called on user input.
datasets: [
{
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
data: <%= #sensor_readings.map(&:temperature).html_safe %>
},
{
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
data: <%= #sensor_readings.map(&:humidity).html_safe %>
}
]

Related

How to make one datefilter for multiple charts on one webpage?

can someone help me with the code? I am a newbie programmer. I make a web to monitor something. Right now the data monitoring is still a dummy data. The data is on API http://66.42.52.94:8000/api/data/ . So there are so many sensors data (which are: accelero_x, accelero_y, accelero_z, vibration, moisture, gyro_x, gyro_y, gyro_z, displacement) that I have to display on the chart. And this one is the code for displaying the chart of accelerometer sensor x-axes.
code for js file :
$(document).ready(function(){
$.datepicker.setDefaults({
dateFormat: 'yy-mm-dd'
});
$("#firstdatepicker").datepicker();
$("#lastdatepicker").datepicker();
$("#filter").click(function() {
var from_date = $("#firstdatepicker").val();
var to_date = $("#lastdatepicker").val();
if (from_date != '' && to_date != '') {
console.log(from_date, to_date);
var endpoint = '/api/data';
$.ajax({
method: "GET",
url: endpoint,
data: {
from_date: from_date,
to_date: to_date
},
success: function(data){
console.log(data); //get all data
//get data by fields
var accelero_x = [], time = [];
for (var i=0; i<data.length; i++){
accelero_x.push(data[i].accelero_x);
time.push(data[i].timestamp);
}
console.log(accelero_x);
console.log(time);
//plot the chart
var ctx = document.getElementById("acceleroxChart").getContext('2d');
var acceleroxChart = new Chart(ctx, {
type: 'line',
data: {
labels: time,
datasets: [{
label: 'Accelero-x (mV/g)',
data: accelero_x,
fill: false,
borderColor: "#80b6f4",
pointBorderColor: "#80b6f4",
pointBackgroundColor: "#80b6f4",
pointHoverBackgroundColor: "#80b6f4",
pointHoverBorderColor: "#80b6f4",
pointBorderWidth: 10,
pointHoverRadius: 10,
pointHoverBorderWidth: 1,
pointRadius: 1,
borderWidth: 4,
}]
},
options: {
reponsive: true,
scales: {
yAxes: [{
ticks: {
beginAtZero:false,
stepSize:0.5
},
scaleLabel: {
display: true,
labelString: 'Accelero sb.x (mV/g)'
}
}],
xAxes: [{
display: true,
type: "time",
time: {
minUnit: "hour",
unit: "hour",
unitStepSize: 6,
min: moment(from_date).toDate(),//Date object type
max: moment(to_date).toDate()//Date object type
},
scaleLabel: {
display: true,
labelString: 'Time'
}
}]
}
}
});
},
error: function(error_data){
console.log(error_data)
}
});
} else {
alert("Please Select Date");
}
});
});
code for html:
{% extends 'base.html' %}
{% load static %}
{% block content %}
<!-- Begin Page Content -->
<div class="container-fluid">
<!--card header-->
<div class="card-header">
<h6 class="m-0 font-weight-bold text-primary">Accelero X-axes</h6>
<!--Breadcrumbs-->
<ol class="breadcrumb">
<li style="margin-left: 10px">
data from <input type="text" id="firstdatepicker" name="firstdatepicker" value="2020-03-15">
to <input type="text" id="lastdatepicker" name="lastdatepicker" value="2020-03-16">
<input type="button" name="filter" id="filter" value="Filter" class="btn btn-info">
</li>
</ol>
<!--End of breadcrumbs date picker-->
<!--Kelembaban tanah Area Chart-->
<div class="card-body">
<div class="chart-area">
<canvas id="acceleroxChart"></canvas>
</div>
</div> <!--end of area chart-->
</div> <!--end of card header-->
</div>
<!--end of page content (container-fluid) -->
</div>
<!-- End of Main Content -->
</div>
<!-- End of Content Wrapper -->
</div>
<!-- End of Page Wrapper -->
<!-- Scroll to Top Button-->
<a class="scroll-to-top rounded" href="#page-top">
<i class="fas fa-angle-up"></i>
</a>
<!-- Page level chart.js yang digunakan halaman ini-->
<script src="{% static 'js/demo/accelero-x.js' %}"></script>
<!-- <script src="{% static 'js/demo/chart-pie-demo.js' %}"></script> -->
<!-- date picker js-->
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery.ui.datepicker.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<!--buat kalender-->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/themes/sunny/jquery-ui.min.css"></link>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css"></link>
{% endblock content %}
So x-axes of the chart for time and y-axes of the chart for sensors data. And the data can be filtered by the datefilter that I picked like this :
These codes only display a chart for accelerometer sensors x-axes on one webpage. The problem is I want to display all of the charts of each sensor (so by the end there will be 9 charts) on one single page with just one datefilter for all the charts. Can someone help me with the code? Thank you before.
I solved it! It doesn't need any special code.
Just call the other charts to the html part. No need to iterate the breadcrumbs part on html.
<div class="card-body">
<div class="chart-area">
<canvas id="acceleroxChart"></canvas>
</div>
</div> <!--end of area chart-->
<br>
<h2>Chart2</h2>
<br>
<div class="card-body">
<div class="chart-area">
<canvas id="theotherchartname"></canvas>
</div>
</div>
.........and so on....
You can still use the the same structure code as the js file above for the other charts sensor, just change the data you need.

Charts.js & Bootstrap Accordion

I have been trying to understand why Charts.js wouldn't draw a chart if it's inside a Bootstrap Accordion.
After many attempts I managed to understand why it's not drawing the chart! it's because the panel is basically hiding the canvas so Charts.js doesn't draw it.
Can anyone help me come up with a solution to this?
I've also tried to draw the chart only when the Bootstrap event shown.bs.collapse is active, doesn't work.
Here's a jsfiddle I've created to illustrate the problem:
If you want to reproduce the problem simply add in to the class in line <div id="collapseOne" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
I got it to work with the following code:
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<canvas id="q_chart" width="400" height="400"></canvas>
</div>
</div>
</div>
</div>
and javascript
var ctx = document.getElementById("q_chart").getContext("2d");
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
}, {
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}]
};
var MyNewChart=null;
$('#collapseOne').on('shown.bs.collapse', function () {
setTimeout(function() {
if(MyNewChart == null)
MyNewChart = new Chart(ctx).Bar(data);
},200);
});
I did some testing and found that you can call MyNewChart.update() instead of recreating the chart on a timeout as the verified answer does. The first transition is a bit rough (maybe because a transition is happening as it's created) but after that the transitions are smooth.
Example JSFiddle: https://jsfiddle.net/akh9cnx5/
<div class="container">
<button type="button" class="col-12 btn btn-light mb-2" data-toggle="collapse" data-target="#viewgraph" aria-expanded="false" aria-controls="viewgraph">View Graph</button>
<div id="viewgraph" class="collapse mb-2">
<div class="card card-body">
<div class="d-flex justify-content-center">
<div class="col-12 col-sm-8">
<canvas id="graph"></canvas>
</div>
</div>
</div>
</div>
</div>
and javascript
var ctx = document.getElementById('graph').getContext('2d');
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
}, {
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}]
};
var myNewChart = new Chart(ctx, {
type: 'bar',
data: data
});
$("#viewgraph").on('shown.bs.collapse', function () {
myNewChart.update();
});
For anyone coming to this later, I had the same issue but preferred not to rely on a timer delay as offered in the accepted answer. The following is what worked for me using Bootstrap 3.3.7 with the shown event:
shown.bs.collapse
$('#accordion').on('shown.bs.collapse', function (e) {
switch (e.target.id)
{
case "panel1": ShowChart1(); break;
case "panel2": ShowChart2(); break;
case "panel3": ShowChart3(); break;
case "panel4": ShowChart4(); break;
default: ShowDefault();
}
})
Hopes this helps
btw - this is the above code working: greatdata.com/stats/newyork_ny

Chartjs line graph goes over panel

I am currently using chartjs for graphs and I want to be able to display these in a bootstrap panel. I have managed to get the graph on however the graph seems to go over the panel body a little bit.
<div class="panel panel-default" draggable="true">
<div class="panel-heading">
<h3 class="panel-title">
Panel title
<span class="glyphicon glyphicon-pencil panel-icons"></span>
<span class="glyphicon glyphicon-zoom-in panel-icons"></span>
<span class="glyphicon glyphicon-trash panel-icons"></span>
</h3>
</div>
<div class="panel-body">
<canvas id="chartjs"></canvas>
</div>
</div>
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
},
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
function loadChart() {
var context = $('#chartjs').get(0).getContext("2d");
window.myLine = new Chart(context).Line(data, {
responsive: true,
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
});
}
window.onload = loadChart();
this is what it currently looks like
Chart Image.
I am unsure on how to get the graph within the bounds of the panel. If more information is needed I would be happy to provide more.
Just add some padding-right to the canvas element. That should give you enough space to fit the canvas right in to the panel-body.
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
},
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
function loadChart() {
var context = $('#chartjs').get(0).getContext("2d");
window.myLine = new Chart(context).Line(data, {
responsive: true,
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
});
}
window.onload = loadChart();
canvas {
padding: 0 30px 0 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<div class="col-sm-6">
<div class="panel panel-default" draggable="true">
<div class="panel-heading">
<h3 class="panel-title">
Panel title
<span class="glyphicon glyphicon-pencil panel-icons"></span>
<span class="glyphicon glyphicon-zoom-in panel-icons"></span>
<span class="glyphicon glyphicon-trash panel-icons"></span>
</h3>
</div>
<div class="panel-body">
<canvas id="chartjs"></canvas>
</div>
</div>
</div>

Chart.js not showing in my view

I have an app running with angular.js and one of my view should load an chart. I'm using chart.js for it, but for some reason it's not working and no error is shown on my console.
Here is my function that creates the chart:
$scope.writeBatteryChart = function(){
console.log("Function was called");
var data = {
labels: ["10:30am", "11:00am", "11:30am", "12:00pm", "12:30pm", "01:00pm", "01:30pm"],
datasets: [
{
label: "Battery",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
}
]
};
var options = {
responsive: true,
bezierCurve : false
}
var ctx = document.getElementById("myChart").getContext("2d");
var myLineChart = new Chart(ctx).Line(data, options);
}
Here's the HTML where the chart should appear:
<div class="row">
<div class="col-md-8 col-sm-8 col-xs-8" style="padding-left: 0px;">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Battery <i class="fi-battery-half"></i></h3>
</div>
<div class="panel-body">
<canvas id="myChart" height="300"></canvas>
</div>
</div>
</div>
<div class="col-md-4 col-sm-4 col-xs-4" style="padding-left: 0px; padding-right: 0px;">
</div>
</div>
and I'm also loading the latest version of the Chart.js:
<!--Chart plugin -->
<script src="bower_components/Chart.js/Chart.js" language="javascript"></script>
One thing that I noticed is that the canvas is being rendered with 0 dimensions:
Does someone can see where I'm doing something wrong?
The problem: I was calling the function before the canvas be loaded on the view, so I used a setTimeout() to call the writeBatteryChart() with a delay in order to wait the canvas be rendered.
You have to add your rendered things inside of canvas. Hope it will help Link

Chartjs not showing up in Foundation5 Tabs

I'm using Chartjs with Foundation I have three charts that I want to be displayed on three tabs.
The chart only shows in the first tab. How can I get it to show in the second and third tabs? Can someone demonstrate a solution with a JSFiddle?
HTML:
<ul id="view-tabs" class="row tabs" data-tab>
<li class="tab-title active small-12 medium-4 day">View Day</li>
<li class="tab-title small-12 medium-4 week">View Week</li>
<li class="tab-title small-12 medium-4 month">View Month</li>
</ul>
<div class="tabs-content">
<div class="content day-data active" id="day">
<canvas id="day-graph" width="300" height="300"></canvas>
</div>
<div class="content week-data" id="week">
<canvas id="week-graph" width="300" height="300"></canvas>
</div>
<div class="content month-data id="month">
<canvas id="month-graph" width="300" height="300"></canvas>
</div>
</div>
Chartjs js
- this script just shows the #day-graph but it won't show if I place it in the 2nd or 3rd tab.
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
var lineChartData = {
labels : ["January","February","March","April","May","June","July"],
datasets : [
{
label: "Visitors",
fillColor : "rgba(148,194,116, 0.5)",
strokeColor : "rgba(220,220,220,1)",
pointColor : "rgba(220,220,220,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(220,220,220,1)",
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
}
]
}
window.onload = function(){
var ctx = document.getElementById("day-graph").getContext("2d");
window.myLine = new Chart(ctx).Line(lineChartData, {
responsive: true
});
}
As u can see, u're executing new instance of Chart within window.onload function. This is the first reason.
Take a closer look - at the execution:
var ctx = document.getElementById("day-graph").getContext("2d");
You took the context over one canvas object, but in tabs you have three views. Sciprt cannot figure out that it should exec other charts.
U need to repeat the approach 3 times - the number of charts inside the tabs, for example:
//some previous code
var ctx = document.getElementById("week-graph").getContext("2d");
window.myLine = new Chart(ctx).Line(lineChartData, {
responsive: true
});
But for the optimalization purposes u should execucute the instance IF the tab in currently avaiable. And this thing u should do by yourself.
http://foundation.zurb.com/docs/components/tabs.html#callbacks

Categories