I am plotting a graph in XPages using QJplot. It was working fine before until I copied the entire process ie, buttons that extract data, buttons that plot data etc. Now for some reason I keep getting an error that says "An error has occurred while updating some of the page. $ is not a function." When I do a preview in the browser and look on the error console it highlights this "$(document).ready(function(){"
In the working version the code below is identical, the only difference is the values are extracted from a different view.
`<xp:scriptBlock id="scriptBlock" rendered="true">
<xp:this.value>
<![CDATA[$(document).ready(function(){
var line1 = #{javascript:return compositeData.Temp};
var line2 = #{javascript:return compositeData.MaxTemp};
var line3 = #{javascript:return compositeData.MinTemp};
var ticks1 = #{javascript:return compositeData.Time};
var plot1 = $.jqplot('chart3', [line1,line2,line3], {
title: '#{javascript: return compositeData.title}',
animate:#{javascript:return compositeData.animated},
seriesDefaults: {
renderer: $.jqplot.LineRenderer,
rendererOptions:{lineMargin: 25},
pointLabels:{show:true, stackedValue: true}
},
series:[
{label:'#{javascript:return compositeData.legendTemp}'},
{label:'#{javascript:return compositeData.legendMaxTemp}'},
{label:'#{javascript:return compositeData.legendMinTemp}'},
],
legend: {
show: #{javascript:return compositeData.legendShow},
placement: '#{javascript:return compositeData.legendPlacement}'
},
axes: {
xaxis:{
label: "Time (min)",
pad: 0,
renderer:$.jqplot.CategoryAxisRenderer,
ticks: ticks1
},
yaxis:{
label: "Temperature (Deg. C)"
}
}
});
});
]]>
</xp:this.value>
</xp:scriptBlock>`
The code which extracts values from the view is working too. The button that doesn't work is the one that is supposed to plot the graph.
<xp:button
value="Replot Chart" id="refresh_code_plot">
<xp:eventHandler
event="onclick" submit="true" refreshMode="partial"
refreshId="code_plot">
</xp:eventHandler>
</xp:button>
code_plot is the custom control which contains scriptBlock. It is linked to the XPages via this code
<xc:code_plot
id="code_plot" legendMaxTemp="Upper Threshold"
legendMinTemp="Lower Threshold" legendTemp="Data"
title="Logger Data (Temperature)" cssStyle="width:900px;">
<xc:this.animated><![CDATA[#{javascript:getComponent("animated").getValue();}]]></xc:this.animated>
<xc:this.Temp><![CDATA[#{javascript:getComponent("extracted_Temp").getValue();}]]></xc:this.Temp>
<xc:this.MaxTemp><![CDATA[#{javascript:getComponent("extracted_MaxTemp").getValue();}]]></xc:this.MaxTemp>
<xc:this.MinTemp><![CDATA[#{javascript:getComponent("extracted_MinTemp").getValue();}]]></xc:this.MinTemp>
<xc:this.legendShow><![CDATA[#{javascript:"true";}]]></xc:this.legendShow>
<xc:this.stackedSeries><![CDATA[#{javascript:getComponent("stackSeries").getValue();}]]></xc:this.stackedSeries>
<xc:this.ticks><![CDATA[#{javascript:getComponent("extracted_Time").getValue();}]]></xc:this.ticks>
<xc:this.Time><![CDATA[#{javascript:getComponent("extracted_Time").getValue();}]]></xc:this.Time>
<xc:this.legendPlacement><![CDATA[#{javascript:getComponent("extracted_Time").getValue()}]]></xc:this.legendPlacement>
</xc:code_plot>
Are there any obvious bugs in this?
Seems you are indeed missing your jquery link. Also simplify:
old:
{label:'#{javascript:return compositeData.legendTemp}'}
new:
{label:'#{compositeData.legendTemp}'}
Are you including jQuery on the main page? it doesn't recognize its namespace.
I fixed this bug. Because I am using different custom controls I didn't define one or two variables in the custom properties and this is why the button which is responsible for plotting the graph wasn't working. One of the other problems I had was in the extracted_Data ids. It is case sensitive and when I was transferring the buttons etc.. the case of some of the ids changed.
Related
I have been trying to solve this problem with ChartJS for a few days now, and I am completely stumped
My program shows the user a set of input elements they use to select data needing to be charted, plus a button that has an event to chart their data. The first chart works great. If they make a change to the data and click the button a second, third, or more time, all the data from the previous charts is plotted, PLUS their most recent selection.
It is behaving exactly like you might expect if the chart.destroy() object is not working, or perhaps would work if I created the chart object using a CONST (and could therefore add new data but not delete the beginning data).
I have tried all combinations of the browsers, chartjs and jquery libraries below:
Three different browsers:
• Chrome: Version 107.0.5304.121 (Official Build) (64-bit)
• Microsoft Edge: Version 107.0.1418.56 (Official build) (64-bit)
• Firefox: 107.0 64-bit
I have tried at least three different versions of Chart.js, including
• Versions 3.9.1
• 3.6.2
• 3.7.0
Jquery.js
• v3.6.1
• v1.11.1
Other things I have tried:
"use strict" (no luck)
In addition to destroying the chart object, removed the div containing the canvas, and appending it again.
using setTimeout() function before updating the chart after destroying it (because I thought maybe giving the destroy method more time might help)
type here
Software:
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/chart.js"></script>
<script type="text/javascript" src="js/dropdownLists.js"></script>
<script type="text/javascript" src="js/chartDataFunctions.js"></script>
<script type="text/javascript" src="js/chartJSFunctions.js"></script>
<body>
<div class = metadatasetup4" id = "buttons">
<button class="download" id="getchart" value="Get Chart">Chart</button>
<button class="download" id="downloadchart" value="Download">Download</button>
</div>
<div id = "bigchartdiv" class="bigchart">
<canvas id="myChart"></canvas>
</div>
</body>
<script>
$(window).on('load',function(){
//NOTE 1: In of my attempts to troubleshoot I tried strict mode (it didn't work)
//"use strict";
let data = {
labels: lbl,
datasets: [
]
};
let config = {
type: 'line',
data: data,
options: {
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
min:0,
pointStyle:'circle',
},
y1: {
type: 'linear',
display: true,
position: 'right',
suggestedMax: 25,
min: 0,
pointStyle: 'cross',
// grid line settings
grid: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
},
}
}
};
// NOTE 2: The next line below, beginning with "var bigChartHTML =" was one of my later attempts to
// solve the problem. It didn't work, but my thought process was that if I removed
// the div containing the canvas, AND destroyed the chart object, that appending a "fresh"
// chart div to the body might be a work-around. This did not work.
var bigChartHTML = '<div id = "bigchartdiv" class="bigchart"><canvas id="myChart"></canvas></div>'
let ctx = document.getElementById('myChart').getContext('2d');
let bigChart = null;
// The getChartData() function below uses Ajax to populate various dropdown lists
// which enable the user to select the data is to be charted.
// There are no chartjs-related operations in getChartData()
getChartData();
$('#buttons').on('click','#getchart',function(){
if (bigChart!=null) {
//removeData(bigChart);
bigChart.destroy();
//bigChart = 1;
}
$("#bigchartdiv").empty(); //for this and next 2 lines, see NOTE 2 above
$("#bigchartdiv").remove();
$(bigChartHTML).insertAfter("#chartcontrols");
bigChart = new Chart(document.getElementById('myChart'),config);
//NOTE 3: I thought maybe bigChart.destroy() took time, so I tried
// using the setTimeout function to delay updating the chart
// (didn't work, but I left it in the code, anyway.)
setTimeout(function() {updateChart(bigChart)}, 2000);
//updateChart(bigChart);
});
// NOTE: The updateChart() function is actually included in "js/chartDataFunctions.js"
function updateChart(chart) {
/*
This section of the program reads the HTML elements then uses them
to make an Ajax request to sql server, and these become the
parameters for the newDataSet() function below.
*/
newDataset(chart,firstElement,newdataset,backgroundcolor,color);
}
// NOTE: The newDataSet() function is actually included in "js/chartJSFunctions.js"
// I show it here for brevity.
// It decides which axis (y or y1) to use to plot the datasets
// the dataset is pushed into the data, and chart.update() puts it in the chart object
function newDataset(chart,label,data,bgcolor='white',color='rgb(255,255,255)') {
var maxValue = Math.max(...data);
if (Number.isNaN(maxValue)) {
return;
}
if (maxValue == 0) {
return;
}
var axisID = 'y';
var ptStyle = 'circle';
//var pStyle = 'circle';
if (maxValue < 50) {
axisID = 'y1';
bgcolor = 'white';
//ptStyle = 'Star'
}
chart.data.datasets.push({
label:label,
yAxisID:axisID,
data:data,
borderColor:color,
backgroundColor:bgcolor,
//pointStyle:ptStyle
});
chart.update();
}
});
</script>
I found a work-around that solves my problem, but I still think this is a bug in ChartJS. Before calling bigChart.destroy(), I now do two things: First, reset the data object back to it's original value, and second, reset the config object back to it's original value, THEN call bigChart.destroy().
I think the destroy() method should handle that for me, but in my case, for whatever reason, it doesn't.
So, what I have is a work-around, not really a solution, but I'll take it.
I am using Primefaces 6 for my companys charting needs, which relies on jQplot.
For a project, I am trying to overlay a line chart on a stacked bar chart with negative values, to obtain something like this:
The problem is that when I try to add a linechartseries to the same model as the two barchartseries , the linechart becomes a part of the stack when setting setStacked(true); on the model, because Primefaces seems to not allow individual disabling of stacking on series, only per model. So I end up with this when rendering the chart with
<p:chart type="bar" model="#{backingBean.cartesianChartModel}"/>
After some investigation I have notoced that jQplot is capable of disabling Stacking on individual series by passing disableStack : true in the JS options, so the question is if it's posssible to override this in some way on the rendered page,either via PF or via some JS hack? I feel that using the extender only apples to the entire model?
Related issues: Disable individual stacking
By pouring through the documentation I found a solution to the problem, if not the question:
It seems that Primefaces allows for individual series to be excempt from the stack in the series creation in this version, by passing
LineChartSeries.setDisableStack(true);
Simple as that.
I guess it may be possible. I used the extender functionality for some jqPlot hacks in the past.
In my case, for example, I had a Donut Chart defined with an extender function as follows:
private void createDonutModel() {
donutModel = new DonutChartModel();
donutModel.setLegendPosition("s");
donutModel.setLegendPlacement(LegendPlacement.OUTSIDE);
donutModel.setSliceMargin(4);
donutModel.setDataFormat("value");
donutModel.setShadow(false);
donutModel.setExtender("donutExtender");
donutModel.setSeriesColors("B81C40, FFA600, 79B54A");
}
The corresponding javascript was doing some changes to the jqPlot:
/**
* Customized jqPlot JQuery layout of the Donut Chart for Status Dashboard.
*/
function donutExtender() {
this.cfg.seriesDefaults = {
// make this a donut chart.
renderer:$.jqplot.DonutRenderer,
rendererOptions:{
thickness: 26,
ringMargin: 0,
fill: true,
padding: 0,
sliceMargin: 4,
// Pies and donuts can start at any arbitrary angle.
startAngle: -90,
showDataLabels: false,
// By default, data labels show the percentage of the donut/pie.
// You can show the data 'value' or data 'label' instead, or 'percent'
dataLabels: 'value',
shadow: false
}
}
this.cfg.gridPadding = {
top: 0, right: 0, bottom: 0, left: 0
}
this.cfg.legend = {
show: false
}
this.cfg.grid = { drawBorder: false,
shadow: false,
background: "transparent"
};
}
So you may try something like this in your case ?
Leave the extension configuration of your series empty, except for the one you are interested in...
function chartExtender() {
this.cfg.series = [
{ //...
},
{ // ...
},
{
disableStack: true
}
]
}
Worth having a shot ...
I am a beginner in html and javascript so your help will be much appreciated.
I am creating a page that accepts values x and y from a form and enters it into a graph.
The graph is working perfectly on the same web page but i would like to have the graph in an entirely different page after I click the submit button. How can i carry forward these variables to a different page and display the same graph? (I would like to avoid JQuery, php etc altogether as this is for a school project)
Here's my code:
function someFunction(){
var xScore = parseFloat(x)
var yScore = parseFloat(y)
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['x', 'y'],
[ xScore , yScore],
]);
var options = {
title: 'Sample Graph',
hAxis: {title: 'x value', minValue: 1, maxValue: 9},
vAxis: {title: 'y value', minValue: 1, maxValue: 9},
legend: 'none'
};
var chart = new google.visualization.ScatterChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
This chart loads when i place following the div in the body.
<div id="chart_div" style="width: 900px; height: 900px;">
I would like to place this div in the body of another html page but would like to carry forward the x and y values generated on this web page (they are generated when the form is submitted).
Use window.localStorage
Put the following literal object in some js file that you will refer to from the current and the next page (or just paste the object wherever necessary).
var xyStore =
{
GetX: function () {
return JSON.parse(localStorage.getItem("x"));
},
GetY: function () {
return JSON.parse(localStorage.getItem("y"));
},
SetX: function (x) {
localStorage.setItem("x", JSON.stringify(x));
},
SetY: function (y) {
localStorage.setItem("y", JSON.stringify(y));
},
};
use in current page:
xyStore.SetX(12);
use in next page:
alert(xyStore.GetX()); // alerts: 12
EDIT
Following #RokoC.Buljan comment, this functionality can be expanded as follows:
Get: function (name) {
return JSON.parse(localStorage.getItem(name));
},
Set: function (name, val) {
localStorage.setItem(name, JSON.stringify(val));
}
Use a simple form on the first page that is configured with method="GET" and action="/page2.html". The input fields within the form should also have a name attribute filled in, for example <input name="x">. Then when you press the submit button in your form, it will pass along the field names and values as a querystring to the second page. For example /graph.html?x=123&y=456.
On this second page you want to read these values, so you need to parse the querystring which is located in window.location.search. There are various ways of doing this, but no built-in feature (it will always be a string, for example x=123&y=456 that you need to parse). I will leave that open as you mention it is a school project and that last bit would be the next challenge ;)
(localStorage as described by #remdevtec is also an option of course.)
I'm using the following json to generate my PieChart, but it generates a blank chart with no error.
The following block is my code to generate the chart:
$.post('/admin/relatorios/getVendasCidadeChart', {
dt_inicio: $("#datepicker-from").val(),
dt_fim: $("#datepicker-to").val()
}).done(function(donutData){
var dados = new google.visualization.DataTable(donutData);
var options = {
title: "Vendas por Cidade",
width: "100%",
height: "100%",
};
var chart = new google.visualization.PieChart(document.getElementById('vendas-cidade'));
chart.draw(dados, options);
});
The json output by the post request:
{"cols":[{"id":"","label":"Loja","type":"string"},{"id":"","label":"Valor(R$)","type":"number"}],"rows":[{"c":[{"v":"Loja Shopping"},{"v":"8620.00"}]},{"c":[{"v":"Loja Centro"},{"v":"10240.00"}]}]}
Chart generated:
http://i.stack.imgur.com/Dfhe4.png
Update 1
Fiddle reproducing the problem:
https://jsfiddle.net/thatmesg/1/
I had similar "issue" but that wasn't an issue. All my pie slices were just returning 0 which may happen and it's logically correct.
Add sliceVisibilityThreshold: 0 to your options variable. It should display the legend even if there will be no data. If you'll see the legend then we can assume that Google Chart library works as expected. If not... then we'll investigate further.
var options = {
legend: { position: 'top', maxLines: 6 },
sliceVisibilityThreshold: 0
};
EDIT:
I see that you're using wrong type of brackets:
That's how your JSON should look like. If you generate it using PHP, don't forget to use json_encode.
var data = google.visualization.arrayToDataTable([["Category","Hours"],["Compliance \/ Policy ($0.00)",0],["Harrassment ($0.00)",0],["Productivity ($0.00)",0],["Skills Gap ($0.00)",0],["Values Behaviour ($0.00)",0]]);
Also I see that yous JSON contains "keys". You should do something like (remove keys and pass just values to the JSON):
$final['json'] = json_encode(array_values($pie_hours));
The issue was with my values... The column "Valor" is defined as number, but the values was coming as strings with quotes...
Just removed the quotes from my JSON and it worked
{"cols":[{"id":"","label":"Loja","pattern":"","type":"string"},{"id":"","label":"Valor(R$)","pattern":"","type":"number"}],"rows":[{"c":[{"v":"Loja Shopping","f":null},{"v":8620.00,"f":null}]},{"c":[{"v":"Loja Centro","f":null},{"v":10240.00,"f":null}]}]}
Updated fiddle
Im rendering graph by morris js. Im using data from mysql database by JSON.
Everything works fine, but I would like to add one more feature to the graph. (change dot color if there is something in buy action).
My JSON:
[{"longdate":"2014-08-20
18:20:01","price":"1620","action":"buy"},{"longdate":"2014-08-20
18:40:01","price":"1640","action":""},{"longdate":"2014-08-20
19:00:01","price":"1620","action":""}]
So I would like to change dot color for values with buy action.
My code for graph:
$.getJSON('results.json', function(day_data) {
Morris.Line({
element: 'graph',
data: day_data,
xkey: 'longdate',
ykeys: ['price'],
labels: ['Cena'],
lineColors: lineColor,
pointSize: 0,
hoverCallback: function(index, options, content) {
var date = "<b><font color='black'>Data: "+day_data[index]['longdate']+"</font></b><br>";
var param1 = "<font color='"+lineColor[0]+"'>Cena - "+day_data[index]['price']+"</font><br>";
return date+param1;
},
xLabelFormat : function (x) {
return changeDateFormat(x);
}
/*My TRIAL
if(action == 'buy'){
pointSize: 4,
lineColors: green,
}
*/
});
});
So my code doesnt work, how can I make this working?
Judging from the documentation for the line chart, this library is not capable of doing what you are asking "out of the box" (meaning, without modification).
You could always fork the github project and add this capability. If you are not interested or able to do that, then you'll probably need to choose a different charting library.