<apex:page controller="PieChartRemoteController"> <script> function retrieveChartData(callback) { var year = document.getElementById('theYear').value; Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.PieChartRemoteController.getRemotePieData}', year, function(result, event) { if(event.status && result && (result.constructor === Array)) { callback(result); RemotingPieChart.show(); } else if (event.type === 'exception') { document.getElementById("remoteResponseErrors").innerHTML = event.message + '<br/>' + event.where; } else { document.getElementById("remoteResponseErrors").innerHTML = event.message; } }, { escape: true } ); } function refreshRemoteChart() { var statusElement = document.getElementById('statusDisplay'); statusElement.innerHTML = "loading..."; retrieveChartData(function(statusElement){ return function(data){ RemotingPieChart.reload(data); statusElement.innerHTML = ''; }; }(statusElement) ); } </script> <apex:pageBlock title="Charts"> <apex:pageBlockSection title="Visualforce Charting + JavaScript Remoting"> <apex:chart height="350" width="450" data="retrieveChartData" name="RemotingPieChart" hidden="true"> <apex:pieSeries dataField="data" labelField="name"/> <apex:legend position="right"/> </apex:chart> <div> <select id="theYear" onChange="refreshRemoteChart();"> <option value="2013">2013</option> <option value="2012">2012</option> <option value="2011">2011</option> <option value="2010">2010</option> </select> <span id="statusDisplay"></span> <span id="remoteResponseErrors"></span> </div> </apex:pageBlockSection> </apex:pageBlock> </apex:page>
This markup attaches a chart component to its data source by setting the chart’s data attribute to the name of a JavaScript function, retrieveChartData, which returns the data. The name of the function is provided as a string.
A static HTML <select> menu displays the years available to chart. The menu is not associated with a form element of any kind, and its value is never submitted directly back to the controller. Instead, the <select> menu’s onChange attribute calls a JavaScript function, refreshRemoteChart(), whenever the menu changes. There are two additional static HTML elements: two <span> tags with IDs. The <span> tags are empty when the page loads, and are updated via JavaScript to display status and error messages when necessary.
This diagram illustrates these links between the different components
of the page:
The sequence for the initial loading of the chart is simple: the <apex:chart> named RemotePieChart calls retrieveChartData() to get its initial data, and retrieveChartData() calls RemotePieChart.show() when it has the data. And, the chart appears.
Updates are more complicated. When a new year is chosen from the theYear menu, the menu’s onChange event fires, which calls the refreshRemoteChart() function. refreshRemoteChart() in turn calls the retrieveChartData() function, and when the @RemoteAction returns new data, retrieveChartData() (via the callback provided by refreshRemoteChart()) calls RemotePieChart.reload(). And, the chart updates.
Here are a couple of other items to note:
public class PieChartRemoteController { // The year to be charted public String chartYear { get { if (chartYear == Null) chartYear = '2013'; return chartYear; } set; } // Years available to be charted, for <apex:selectList> public static List<SelectOption> getChartYearOptions() { List<SelectOption> years = new List<SelectOption>(); years.add(new SelectOption('2013','2013')); years.add(new SelectOption('2012','2012')); years.add(new SelectOption('2011','2011')); years.add(new SelectOption('2010','2010')); return years; } public List<PieWedgeData> getPieData() { // Visualforce expressions can't pass parameters, so get from property return PieChartRemoteController.generatePieData(this.chartYear); } @RemoteAction public static List<PieWedgeData> getRemotePieData(String year) { // Remoting calls can send parameters with the call return PieChartRemoteController.generatePieData(year); } // Private data "generator" private static List<PieWedgeData> generatePieData(String year) { List<PieWedgeData> data = new List<PieWedgeData>(); if(year.equals('2013')) { // These numbers are absolute quantities, not percentages // The chart component will calculate the percentages data.add(new PieWedgeData('Jan', 30)); data.add(new PieWedgeData('Feb', 15)); data.add(new PieWedgeData('Mar', 10)); data.add(new PieWedgeData('Apr', 20)); data.add(new PieWedgeData('May', 20)); data.add(new PieWedgeData('Jun', 5)); } else { data.add(new PieWedgeData('Jan', 20)); data.add(new PieWedgeData('Feb', 35)); data.add(new PieWedgeData('Mar', 30)); data.add(new PieWedgeData('Apr', 40)); data.add(new PieWedgeData('May', 5)); data.add(new PieWedgeData('Jun', 10)); } return data; } // Wrapper class public class PieWedgeData { public String name { get; set; } public Integer data { get; set; } public PieWedgeData(String name, Integer data) { this.name = name; this.data = data; } } }