I am having trouble nailing down the best method for passing data from a Java backing/managed bean to a jQuery/Javascript component such as Highcharts, so that my web app produces/displays data in a dynamic, real time way. I am pretty solid on my Java side of things but I have a pretty limited knowledge of JavaScript/jQuery which is obviously where I am falling down. So far as I have read, the best way is to Ajaxify a hidden field on my web app and pass a JSON object or string? in to it and then pass that value into my JS component.
Firstly this seems a bit labour intensive as I would have an Ajax call to update the JSON data and then a setInterval call to re-read the data into the JS component? I was hoping that I could pass the data straight into the JS component...
Either way could someone just firm up my knowledge and tell me a good tried and tested method, if different from above... Guides/Demo's would also be massively appreciated!!
I also use Primeface's 2.2.1 if this will affect a suggested methodology?
Cheers
Ally
UPDATE:
Code is below but I just want to describe what I am trying to achieve quickly: Effectively I am trying to implement a dynamic Highcharts chart, using a simple count++ function from my backing bean. Obviously further down the line I will be using a real-time feed to provide this data but at the moment I am just trying to get Highcharts to work based of changing information from my JSF backing bean.
Here is my simple count function and conversion to JSON (I am not sure if the JSON method is really necessary since only 1 value(int) is being passed, but I would like to retain this method as I am sure I will be using more extensively on other parts of the web app):
public class TestBean {
private int output;
public TestBean() {
output = 1;
}
public int getOutput() {
return output;
}
public void setOutput(int output) {
this.output = output;
}
public void update() {
setOutput(getOutput() + 1);
}
public void prepareChartDate() {
// Produce you JSON string (I use Gson here)
RequestContext reqCtx = RequestContext.getCurrentInstance();
reqCtx.addCallbackParam("chartData", new Gson().toJson(output));
}
}
Highcharts External JS File, again its worth noting that I have maintained the series function at the bottom of the chart to build/populate the graph before I start appending values obtained from the JSF Beans:
Highcharts.setOptions({
global: {
useUTC: false
}
});
var chart;
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
backgroundColor: "#F8F0DB",
renderTo: 'containerHigh',
defaultSeriesType: 'area',
margin: 10,
marginLeft:30,
marginBottom:17,
zoomType: 'y',
events: {
load: updateChartData
}
},
title: {
text: 'Feed Flow Rate '
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: ''
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function() {
return '<b>'+ this.series.name +'</b><br/>'+
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) +'<br/>'+
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
plotOptions: {
area: {
fillColor: {
linearGradient: [0, 0, 0, 100],
stops: [
[0, Highcharts.getOptions().colors[0]],
[1, 'rgba(2,0,0,0)']
]
},
lineWidth: 1,
marker: {
enabled: false,
states: {
hover: {
enabled: true,
radius: 5
}
}
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
}
}
},
series: [{
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i++) {
data.push({
x: time + i * 10000,
y: 50 * Math.random()
});
}
return data;
})()
}]
});
});
And the inline JS Function held on my XHTML page:
This works:
function updateChartData(xhr, status, args) {
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime()
y = 50 * Math.random();
series.addPoint([x, y], true, true);
}, 1000)
//parse it, process it and load it into the chart
}
but when I try to pass my bean value:
function updateChartData(xhr, status, args) {
var jsonString = args.chartData
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime()
y = jsonString
series.addPoint([x, y], true, true);
}, 1000)
//parse it, process it and load it into the chart
}
That doesnt work...
Also I've been trying both remoteCommand and Poll to get the chart to work, neither of which I have been successful with:
<h:form>
<p:commandButton value="update" action="#{testBean.update}" update="beanvalue"/>
<h:outputText value="#{testBean.output}" id="beanvalue"/> <br />
<h:outputText value="#{testBean.output}" id="chartValue"/> <br />
<p:commandButton value="Load" type="button" onclick="fetchChartData();"/>
<p:remoteCommand name="fetchChartData"
action="#{testBean.prepareChartDate()}"
oncomplete="updateChartTest(xhr, status, args);"/>
</h:form>
As I've said before Bhesh, your help is massively appreciated and anymore would be great!
Cheers
Ally
If you are using PrimeFaces 2.2.1 then it should be pretty easy to acheive what you are trying to do.
PF has a component p:remoteCommand
with which you can Ajaxicaly invoke a managed-bean method.
<h:form id="frmIrsChartData">
<p:remoteCommand name="fetchChartData"
action="#{chartManagedBean.prepareChartData()}"
oncomplete="updateChartData(xhr, status, args);"/>
</h:form>
And in the method prepareChartData()
you produce your JSON string and use RequestContext
provided by PF to send it back to client:
public void prepareChartDate() {
// Produce you JSON string (I use Gson here)
RequestContext reqCtx = RequestContext.getCurrentInstance();
reqCtx.addCallbackParam("chartData", jsonString);
}
And in the Javascript callback function updateChartData(xhr, status, args)
you can process the JSON response and load into the chart:
function updateChartData(xhr, status, args) {
var jsonResponse = args.chartData;
//parse it, process it and load it into the chart
}
And to periodically update the chart just use the Javascript setInterval
or setTimeout
and pass the name
of the remoteCommand
which is actually a Javascript function.
setInterval("fetchChartData()", 5000);
That's how I do it.
PF also has another component p:poll
which makes it easier to implement live charts.
Update:
You got it wrong. On
What you need to do is on document-ready render the chart first and then start you poll.
$(document).ready(function() {
chart = new Highcharts.Chart({
//....
});
// start your poll here
});
The p:poll
callback function updateChartData(xhr, status, args)
needs be global so that the poll can call it on oncomplete
event.
function updateChartData(xhr, status, args) {
var jsonResponse = args.chartData;
//parse it, process it and load it into the chart
//you will load data into the same chart created in document-ready
}
And when you are using poll you do not need the setTimeout
or setInterval
.
Update 2:
First of all, in updateChartData
function you are supposed to update the chart
that you created:
function updateChartData(xhr, status, args) {
var series = chart.series[0]; // <--- no more this instead global variable "chart"
var newData = args.chartData;
var x = (new Date()).getTime()
var y = eval('(' + newData + ')');
series.addPoint([x, y], true, true);
}
Next, updateChartData
is callback function so don't call it yourself, it is called by the remote-command every time the request is complete.
And, give the remote-command a separate form of it's own:
<h:form>
<p:remoteCommand name="fetchChartData"
action="#{testBean.prepareChartDate()}"
oncomplete="updateChartData(xhr, status, args);"/>
</h:form>
Note in oncomplete="updateChartTest(xhr, status, args);"
in your code the callback function should be updateChartTest
instead of updateChartTest
.
Trigger the ajaxical polling after the chart is done loading:
events: {
load: function() {
setInterval("fetchChartData()", 5000); //update the chart every 5 seconds
}
}
For testing only, trying returning a random value from you managed-bean:
public void prepareChartDate() {
// Produce you JSON string (I use Gson here)
RequestContext reqCtx = RequestContext.getCurrentInstance();
reqCtx.addCallbackParam("chartData", new Random().nextInt(500));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With