Chart.js chart by passing data through the controller MVC - javascript

I am trying to display a chart by passing to it data from a controller. I am using chart.js
Model:
public class DatapointLine
{
public DatapointLine(double x, double y)
{
this.X = x;
this.Y = y;
}
// setting the name to be used when serializing to JSON.
[DataMember(Name = "x")]
public Nullable<double> X = null;
//setting the name to be used whenserializing to JSON.
[DataMember(Name = "y")]
public Nullable<double> Y = null;
}
Controller:
public JsonResult BarChart()
{
List<DatapointLine> dataPoints = new List<DatapointLine>{
new DatapointLine(10, 22),
new DatapointLine(20, 36),
new DatapointLine(30, 42),
new DatapointLine(40, 51),
new DatapointLine(50, 46),
};
ViewBag.DataPoints = JsonConvert.SerializeObject(dataPoints);
return Json(dataPoints, JsonRequestBehavior.AllowGet);
}
Script:
<script type="text/javascript">
$(function() {
var data = getData();
AutoFollow(data);
});
function getData() {
var dateValue = [];
var countValue = [];
$.ajax({
url: "/Supernethome/BarChart",
dataType: 'json',
async: false
}).done(function(data) {
data.forEach(function(data) {
dateValue.push(data.X);
countValue.push(data.Y);
});
});
return {
dateValue: dateValue,
countValue: countValue
};
}
$(document).ready(function () {function AutoFollow(data) {
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx,
{
type: 'bar',
data: {
labels: data.dateValue,
datasets: [
{
label: 'AutoFollow',
data: data.countValue,
backgroundColor: "rgba(153,255,51,1)"
}, {
label: 'Manual',
data: [30, 29, 5, 5, 20, 3, 10],
backgroundColor: "rgba(255,153,0,1)"
}
]
}
});
}
});
I am generating the views for the charts in partial views and then referencing the partial views in a main view.
I am getting the following errors:
chartjs.init.js:3 Uncaught TypeError: Cannot read property 'getContext' of null
at HTMLDocument.<anonymous> (chartjs.init.js:3)
at f (jquery.js:1026)
at Object.fireWith [as resolveWith] (jquery.js:1138)
at Function.ready (jquery.js:427)
at HTMLDocument.xt (jquery.js:97)
The error is eating a lot of time,Need Help.

Looks like two different errors to me...
Error 1 : Graph container element not found
Checkout this thread as the issue sounds the same:
morris.js Graph container element not found
Error 2 : Cannot read property 'getContext'
This looks like a red herring. This exception is not being thrown by morris.js but by chartjs. However it may be that the exception thrown by this code is stopping the morris.js code from being executed successfully. As such it's worth testing the code in isolation i.e load up a view with nothing in it except the required morris scripts/assets and your inline script. No additional scripts or JavaScript libraries. Something like this :
Example Test View
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
</head>
<body>
<div id="mychart"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<script>
$(document).ready(function () {
$.getJSON("/Supernethome/BarChart", function (data) {
new Morris.Area({
element: 'mychart',
data: data,
xkey: 'X',
ykeys: ['Y'],
pointSize: 2,
hideHover: 'auto',
resize: true
});
});
});
</script>
</body>
</html>

Related

Inserting Vanilla JS into VueJS framework

With the concept of VueJS is a kind of framework of JS, then it should have no problem inserting the vanilla JS into it. The following is the attempt to insert the pure JS code into vue-cli project(webpack structure).
[Before Start] I tried the example code as the following link(official document)
URL: https://codepen.io/Tobyliao/pen/ZEWNvwE?editable=true%3Dhttps%3A%2F%2Fdocs.bokeh.org%2F
it works.
[Question] As I tried to imlement into Vue project. It fails. picture1 is the directory structure.
I tried to place the src url include into ./public/html's tag as following:
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.2.1.min.js"></script>
Create a componet in './src/components/BokehPlot.vue'
inside the code, I insert
<template>
<h1>Measurement Plotting</h1>
</template>
<script src='./main.js'>
export default {
}
</script>
Then finally place all the Bokeh code into './src/component/main.js'. It is the pure JS code I want to import into the structure.
[Result]
I can see the plot in the background, but it kept on showing the error message like picture2.
You have many options here, I went ahead and simply made a mixin to utilize the component lifecycle that Vue provides. source
Here are the relevant parts:
BokehPlot.vue
<template>
<h1>治具量測</h1>
</template>
<script>
import Chart from "#/mixins/Chart";
export default {
mixins: [Chart],
};
</script>
Chart.js
export default {
data() {
return {
plot: null,
xdr: null,
ydr: null
};
},
beforeMount() {
// create some ranges for the plot
this.xdr = new Bokeh.Range1d({ start: -1, end: 100 });
this.ydr = new Bokeh.Range1d({ start: -0.5, end: 20.5 });
// make the plot
this.plot = new Bokeh.Plot({
title: "BokehJS Plot",
x_range: this.xdr,
y_range: this.ydr,
plot_width: 400,
plot_height: 400,
background_fill_color: "#F2F2F7"
});
},
mounted() {
this.loadData();
},
methods: {
loadData() {
// create some data and a ColumnDataSource
let x = Bokeh.LinAlg.linspace(-0.5, 20.5, 10);
let y = x.map(function (v) {
return v * 0.5 + 3.0;
});
let source = new Bokeh.ColumnDataSource({ data: { x: x, y: y } });
// add axes to the plot
let xaxis = new Bokeh.LinearAxis({ axis_line_color: null });
let yaxis = new Bokeh.LinearAxis({ axis_line_color: null });
this.plot.add_layout(xaxis, "below");
this.plot.add_layout(yaxis, "left");
// add grids to the plot
let xgrid = new Bokeh.Grid({ ticker: xaxis.ticker, dimension: 0 });
let ygrid = new Bokeh.Grid({ ticker: yaxis.ticker, dimension: 1 });
this.plot.add_layout(xgrid);
this.plot.add_layout(ygrid);
// add a Line glyph
let line = new Bokeh.Line({
x: { field: "x" },
y: { field: "y" },
line_color: "#666699",
line_width: 2
});
this.plot.add_glyph(line, source);
Bokeh.Plotting.show(this.plot);
}
}
};
Many decisions to still make, but hopefully that will get you pointed down the right path.
See working example:
https://codesandbox.io/s/bokehjs-forked-4w20k?fontsize=14&hidenavigation=1&theme=dark

Bind plotly on click to vue.js data

I am creating project with vue.js and plot.ly javascript graph library.
How can I bind in "pts" to vue's data's "TestSentences"?
Here is my code ,
thank you to everyone who contributed
My goal is to create an interactive dashboard using this variable. In this way, I can change the data by clicking anywhere on the chart.
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="app">
<div id="grafik"></div>
</div>
<!-- Vue-->
<script>
var app = new Vue({
el: '#app',
data: {
TestSentences: "",
},
methods: {
grafikCiz() {
var trace1 = {
x: [1, 2, 3],
y: ["book", "pencil", "bag"],
mode: 'markers',
marker: {
color: ['#6886c5', '#f40552', '#1b1b2f'],
size: [10, 20, 30]
}
};
var data = [trace1];
var layout = {
height: 400,
width: 400,
};
Plotly.newPlot('grafik', data, layout);
},
},
mounted: function () {
this.grafikCiz();
},
});
</script>
<!-- Vue -->
<script>
var my_graph = document.getElementById('grafik');
my_graph.on('plotly_click', function (data) {
for (var i = 0; i < data.points.length; i++) {
pts = 'x = ' + data.points[i].x + '\ny = ' + data.points[i].y + '\n\n';
};
alert('Closest point clicked:\n\n' + pts);
});
</script>
Use plolty wrapper for vue.js https://github.com/David-Desmaisons/vue-plotly
You can add ref to the component
<vue-plotly v-show="display" :data="graphData" :layout="calculatedLayoutSizes" id="3dPlot"
:display-mode-bar="false" ref="crazyPlotly"></vue-plotly>
then use the ref within your mount point or similar method
this.$refs.crazyPlotly.$on('click', d => {
console.log(d);
});
"d" is an obj with values like x and y datapoint, index...etc
source: https://github.com/statnett/vue-plotly/issues/23
As Alagappan A already pointed out, https://github.com/David-Desmaisons/vue-plotly can make working with plotly in javascript much easier. For me it was sufficient to just:
<vue-plotly :data="data" :layout="layout" #click="temp"> </vue-plotly>
which can directly be utilized in a method:
methods: {
temp (value) {
console.log(value)
}
}

How to export c3js to PDF when it loads data from json get request?

I'm working on dashaboard stats on one of my projects, and I'm using the C3js to add charts to my dashboard, and all it works fine,
but when I wanna generate a pdf of this dashboard using Rotativa Plugin and wkhtmltopdf, but it doesn't work properly, it generates a pdf with data but not showing charts.
This happen only when I'm using Json, but when I insert data directly it works fine.
Conf Application:
Server-Side : ASP.Net MVC5
Client-Side :Javascript, Rotativa, C3js
Exemple
Controller :
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Print()
{
return new ViewAsPdf("Index");
}
public JsonResult Data()
{
var d1 = new ArrayList() { "Data1", 1, 4, 7, 8, 10 };
return Json( d1 , JsonRequestBehavior.AllowGet);
}
}
The View :
<div id="chart" style="width:500px;height:300px"></div>
#section scripts{
<script>
var chart = c3.generate({
bindto: '#chart',
data: {
url: "http://localhost:58762/Home/Data",
mimeType: 'json',
type: 'bar',
}
});
</script>
}
Render in web page (Index Action) :
But, when I execute the Print action, It's samply blank without chart.
Update
I'm also trying to resolve the problem in a different way but without success :
I retreive data with a simple ajax request and store the data in a variable, after I draw chart without calling ajax into the c3 chart function. the html render is absolutly fine, but the pdf print is not working. there is my file bellow :
<div id="chart" style="width:500px;height:300px">Example 1</div>
#section scripts{
<script>
dataT= $.ajax({
method:'GET',
url: 'http://localhost:58762/Home/Data',
context: document.body,
global: false,
async: false,
success: function (result) {
return result;
}
}).responseJSON;
datad = [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 130, 100, 140, 200, 150, 50]
];
console.log('Datad: ' + datad);
console.log('DateT: ' + dataT);
var chart = c3.generate({
data: {
columns: dataT,
type: 'bar'
},
bar: {
width: {
ratio: 0.5
}
}
});
</script>
}
It might be any of these:
The JavaScript default execution timeout is set by default to 200 and your
request to data might be taking longer.
Your server does not support cross origin requests, wkhtmltopdf runs
the AJAX request from the filesystem, so if your server does not
support CORS then it will just block the incoming request for data.
C3JS simply does not work in the rendering engine of wkhtmltopdf
These are the things that I would try:
For (1): check in the documentation how to increase this number
For (2): check this issue in Rotativa GitHub's page:https://github.com/webgio/Rotativa/issues/79#issuecomment-75938649
For (3): to debug this problem and be sure of this, just remove the call to c3 and make a simple print of the data returned by the server (you will have to make your own AJAX call probably). If you see the data, then add the call to C3. If you don't see the chart, then it means that C3 has some sort of incompatibility with the rendering engine (wkhtmltopdf).
After trying a lot of solution and exploring a lot of topics, I come out with the conclusion that wkhtmltopdf is not able to resolve the Ajax call url, so I changed my thinking way and pass all data from the controller, and All it works like sharpe.
This is my solution :
View & Script :
#{
ViewBag.Title = "Home Page";
}
<div id="chart" style="width:500px;height:300px">Lorem Ipsum</div>
#section scripts{
<script>
var infos = #Html.Raw(Json.Encode(ViewBag.data));
var chart = c3.generate({
data: {
columns:infos,
mimeType: 'json',
type: 'bar'
},
bar: {
width: {
ratio: 0.5
}
}
});
</script>
}
Controller :
public ActionResult Print()
{
var data = Data().Data;
ViewBag.data = data;
return new ViewAsPdf("Index");
}
public JsonResult Data()
{
var d1 = new ArrayList() { "data1", 30, 200, 100, 400, 150, 250 };
var d2 = new ArrayList() { "data2", 130, 100, 140, 200, 150, 50 };
var d = new ArrayList() { d1, d2 };
return Json( d, JsonRequestBehavior.AllowGet);
}
Result :

Chart js old chart data not clearing

This question has been asked many times and I went through most of them but non of them helped me finding a solution.
I am generating couple of bar charts using a for loop as a part of reporting functionality.
I am using node.js with Express Handlebars.
My page looks like:
<div class="row report-charts">
<div class="col-md-12">
{{#buildings}}
<div class="col-md-6">
<h4>{{Name}}</h4>
<canvas id="{{idBuildings}}" width="200" height="80"></canvas>
</div>
{{/buildings}}
</div>
</div>
My js code looks like:
$('.case-report-btn').click(function(){
$.ajax({
type: 'post',
url: '/reports/cases/filter',
data : {
StartDate : $('.start-ms-time-hidden').val(),
EndDate : $('.end-ms-time-hidden').val(),
ReportKey : $('.cases-filter-type').val()
},
dataType: 'json',
success: function(res) {
$('.report-charts').show();
for(key in res) {
var innerObj = res[key]; //gives the inner obj
var ctx = document.getElementById(key); //the idBuildings
var labels = [];
var data = [];
var buildingName = innerObj.Name;
for(innerKey in innerObj) {
if(innerKey != 'Name' && innerKey != 'Total') {
labels.push(innerKey);
data.push(innerObj[innerKey]);
}
}
var options = {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: buildingName,
data: data,
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
fixedStepSize: 1
}
}]
}
}
}
var myChart = new Chart(ctx, options);
}
$('#pleaseWaitDialog').modal('hide');
},
error: function(err) {
$('#pleaseWaitDialog').modal('hide');
bootbox.alert('Error: ' + err);
}
});
});
So basically, I am using for loop to generate multiple charts on the page. Inside the loop I declared the chart variable, every time I change the report parameters and hit the button, the new chart is generated. But when I hover over it, the old one still shows up.
Now I am not sure where I should be putting the myChart.destroy() or myChart.clear() methods. I also tried moving the myChart declaration outside the for loop but it didn't help either.
Any suggestions on how to handle this?
I think there are a few ways to do it. You can update your chart data if the chart already exist. Here two functions you can use:
function removeData(chart) {
chart.data.labels.pop();
chart.data.datasets.forEach((dataset) => {
dataset.data.pop();
});
chart.update();
}
function addData(chart, label, data) {
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
}
First you have to remove all your data and then add the new data.
If you want to destroy the chart and create it again you have to save your variable as global. To do this you have yo declare your variable like window.myChart and then before create the new chart, something like this:
if (window.myChart) window.myChart.destroy();
window.myChart = new Chart(ctx, options);
Another way you can try is removing your canvas and creating another one. Something like this:
$('#your_canvas').remove();
$('#your_canvas_father').append('<canvas id="your_canvas"></canvas>');

Javascript browser inspector console vs source code

This is my problem:
I was playing with ECharts JavaScript library, I wanted to retrieve the image data (I know there is a save as image toolbox). When I try to access the function getDataUrl, or getConnectedDataUrl, I get the following error:
"myChart.getDataUrl is not a function"
But when I try to do the same on the browser (or Firebug) console, I get the info I want. When I call get_data() on the console also get the error I mention before. I'm confused.
What am I doing wrong?
There is the example code:
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button type="button" onclick="get_data()">holi</button>
<div id="main" style="width:400px;height:300px;"></div>
<script src="echarts.min.js"></script>
<script type="text/javascript">
// based on prepared DOM, initialize echarts instance
var myChart = echarts.init(document.getElementById('main'));
// specify chart configuration item and data
var option = {
title: {
text: 'Test'
},
tooltip: {},
legend: {
data:['Cosas']
},
xAxis: {
data: ["asdf","qwerty","lol"]
},
yAxis: {},
series: [{
name: 'Cosas',
type: 'bar',
data: [1, 3, 5]
}],
toolbox: {
show : true,
feature : {
mark : {show: false},
saveAsImage : {show: true, title: "save"}
}
}
};
// use configuration item and data specified to show chart
myChart.setOption(option);
function get_data(){
return myChart.getConnectedDataUrl();
};
</script>
</body>
</html>
You just misspelled the function names. They are called getDataURL() and getConnectedDataURL() (with uppercase URL).

Categories