How Visualforce Charting Works

Creating a chart with Visualforce requires the following:
  1. Write an Apex method that queries for, calculates, and wraps your chart data to send to the browser.
  2. Define your chart using the Visualforce charting components.

When the page containing the chart loads, the chart data is bound to a chart component, and the JavaScript that draws the chart is generated. When the JavaScript executes, the chart is drawn in the browser.

A Simple Charting Example

A Visualforce chart requires that you create a chart container component, which encloses at least one data series component. You can optionally add additional series components, chart axes, as well as labeling components such as a legend, chart labels, and tooltips for data points. For example, here is a simple pie chart and the markup which creates it:
A simple pie chart
<apex:page controller="PieChartController" title="Pie Chart">
    <apex:chart height="350" width="450" data="{!pieData}">
        <apex:pieSeries dataField="data" labelField="name"/>
        <apex:legend position="right"/>
    </apex:chart>
</apex:page>

The <apex:chart> component defines the chart container, and binds the component to the data source, the getPieData() controller method. The <apex:pieSeries> describes the label and data fields to access in the returned data, to label and size each data point.

Here is the associated controller:

public class PieChartController {
    public List<PieWedgeData> getPieData() {
        List<PieWedgeData> data = new List<PieWedgeData>();
        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));
        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;
        }
    }
}
This controller is deliberately simple; you normally issue one or more SOQL queries to collect your data. These are the important points illustrated by the example:
  • The getPieData() method returns a List of simple objects, an inner class PieWedgeData used as a wrapper. Each element in the list is used to create a data point.
  • The PieWedgeData class is just a set of properties, and is essentially used as a name=value store.
  • The chart series component <apex:pieSeries> defines which properties from the PieWedgeData class to use to determine each point in the series. In this simple example there's no mystery, but in charts with multiple series and axes this convention allows the efficient return of the entire data set in one List object.

Providing Chart Data

A Visualforce chart binds to the source of its data through the data attribute on the <apex:chart> component. Data can be provided three different ways:

Providing Chart Data via a Controller Method

On the server side, write a controller method that returns a List of objects, which can be your own Apex wrapper objects as in A Simple Charting Example, sObjects, or AggregateResult objects. The method is evaluated server-side, and the results serialized to JSON. On the client, these results are used directly by <apex:chart>, with no further opportunity for processing.

To illustrate this technique with sObjects, here is a simple controller that returns a list of Opportunities, and a bar chart for their amounts:

public class OppsController {
    
    // Get a set of Opportunities 
    
    public ApexPages.StandardSetController setCon {
        get {
            if(setCon == null) {
                setCon = new ApexPages.StandardSetController(Database.getQueryLocator(
                      [SELECT name, type, amount, closedate FROM Opportunity]));
                setCon.setPageSize(5);
            }
            return setCon;
        }
        set;
    }
    
    public List<Opportunity> getOpportunities() {
         return (List<Opportunity>) setCon.getRecords();
    }
}
<apex:page controller="OppsController">
    <apex:chart data="{!Opportunities}" width="600" height="400">
        <apex:axis type="Category" position="left" fields="Name" title="Opportunities"/>
        <apex:axis type="Numeric" position="bottom" fields="Amount" title="Amount"/>
        <apex:barSeries orientation="horizontal" axis="bottom" xField="Name" yField="Amount"/>
    </apex:chart>
    <apex:dataTable value="{!Opportunities}" var="opp">
        <apex:column headerValue="Opportunity" value="{!opp.name}"/>
        <apex:column headerValue="Amount" value="{!opp.amount}"/>
    </apex:dataTable>
</apex:page>

A horizontal bar chart from a List of sObjects
There are two important things to notice about this example:
  • The Visualforce chart components access the data attributes from a List of Opportunity sObjects the same way as from the simple Data object used in A Simple Charting Example.
  • The object field names used as data attributes are case-sensitive in JavaScript while field names in Apex and Visualforce are case-insensitive. You must be careful to use the precise field name in the fields, xField, and yField attributes of axes and data series components, or your chart will silently fail.

Providing Chart Data Using a JavaScript Function

Instead of feeding data directly into the chart, you can provide the <apex:chart> component with the name of a JavaScript function that provides the data. The actual JavaScript function is defined in or linked from your Visualforce page. This function has the opportunity to manipulate the results before passing it to <apex:chart>, or to perform other user interface or page updates.

The JavaScript function must take a callback function as a parameter, and invoke the callback with the function's data result object. The simplest working JavaScript function looks like this:

<apex:page>
    <script>
    function getRemoteData(callback) {
       PieChartController.getRemotePieData(function(result, event) {
           if(event.status && result && result.constructor === Array) {
               callback(result);
           }
       });
    }
    </script>

    <apex:chart data="getRemoteData" ...></apex:chart>
</apex:page>

To support this chart, add the following controller method to the PieChartController class defined in A Simple Charting Example:

@RemoteAction
public static List<PieWedgeData> getRemotePieData() {
    List<PieWedgeData> data = new List<PieWedgeData>();
    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));
    return data;
}

See JavaScript Remoting for Apex Controllers for more information about using JavaScript remoting in Visualforce.

Providing Chart Data via a JavaScript Array

You can use Visualforce charting with non-Salesforce data sources by building a JavaScript array, in your own JavaScript code in your page, and providing the name of that array to <apex:chart>.

<apex:page>
    <script>
    // Build the chart data array in JavaScript
    var dataArray = new Array();
    dataArray.push({'data1':33,'data2':66,'data3':80,'name':'Jan'});
    dataArray.push({'data1':33,'data2':66,'data3':80,'name':'Feb'});
    // ...
    </script>

    <apex:chart data="dataArray" ...></apex:chart>
</apex:page>

When using this technique, if your data is coming from another source you may not need any server-side Apex code at all.

Chart Data Format

Chart data provided by an Apex method should be a List of uniform objects. These objects may be simple wrappers, sObjects, or AggregateResult objects. Every object in the List must contain all fields referenced in the <apex:chart> component hierarchy that is bound to that data source. If all fields are not provided, a client-side JavaScript error is thrown, which you can view in a JavaScript console such as Firebug.

Chart data provided by JavaScript methods should be a JavaScript array of arrays. See Providing Chart Data via a JavaScript Array for details.

© Copyright 2000–2012 salesforce.com, inc. All rights reserved.
Various trademarks held by their respective owners.