7

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

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
Ally
  • 1,476
  • 3
  • 19
  • 30

1 Answers1

16

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));
}
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
  • Yep I am using p:poll to update the value in my web app, I just wasnt sure how to pass this into a JS Function namely Highcharts! I'll give this a go and let you know! Thank you. – Ally Oct 28 '11 at 14:29
  • Any chance you could show the structure or give an example of you JSON data and how you load that into HighCharts? I have what you've written above but its the loading into highcharts I am still struggling on... I am not sure if its what I am trying to pass in or how i am trying to load it in! – Ally Oct 28 '11 at 15:51
  • well its pretty much as you have posted above except im using a poll: ... I've also added an alert to my script so every 10 secs I see my string update. At the moment I am only trying to pass in my 'Y' axis value string, keeping the x axis as: var x = (new Date()).getTime(), but as I said it my method of trying to pass my values into the Highcharts JS file which i think is completely wrong! – Ally Oct 28 '11 at 16:00
  • @Ally: So are you receiving the JSON properly in your `fetchChartData();`? If yes then please post your JS code (Highcharts one) and not in comment, update your original post. – Bhesh Gurung Oct 28 '11 at 16:07
  • I've added the highcharts code that add's the point to the chart! As I mention I am simply trying to visualise incrementing a number at the mo! – Ally Oct 28 '11 at 16:15
  • Bhesh I am still having troubles with this, you've been great thus far but my main problem is I am struggling to 'parse it, process it and load it into the chart' the args.chartData information. Everytime I add this value into my Charts JS, either the charts fails to initialise or the chart doest update and remains a static chart! So what I need to know is how did you parse & load the args.chartData into your charts js file. Thanks – Ally Nov 02 '11 at 11:15
  • @Ally: Can you post you whole JS code, specially that creates the chart and one that sends ajax request and updates the chart with response data. – Bhesh Gurung Nov 02 '11 at 13:07
  • I have updated my original post with my current code... thanks for the help and hopefully we can crack it soon! Cheers – Ally Nov 02 '11 at 14:49
  • You sir are a hero!!! excellent stuff you were a massive help Bhesh.. thanks alot you made my day :) – Ally Nov 02 '11 at 17:00