+ Start a Discussion
Baird_SBaird_S 

Best practice for multiple Googlecharts on VF page

Is there a way to insert separate Googlecharts as components into a visualforce page, something like using apex:include, so that I can develop each Googlechart as its separate page?  I've tried using apex:include, but each chart simply renders on top of the last one.

 

Or is it necessary to include the visualization, etc. for each chart in the page header, and then call them separately in the body separated by <div> tags?

 

I hope that's not the only approach ... I'm trying to build a single VF page with ten or so charts embedded, and don't want have a humongous mess in the header.

 

Thanks for your advice,

Baird

Best Answer chosen by Admin (Salesforce Developers) 
Baird_SBaird_S

Starz, thanks a ton for your suggestions.  They pointed me right to the solution, which I found with help from BritishBoyinDC as well.  Here's the gist of it:

 

Each one of my components was replacing the same <div id="visualization"> in this section:

 

        // Create and draw the visualization.
        var ac = new google.visualization.ComboChart(document.getElementById('visualization'));
        ac.draw(data, { ... etc.

 

What I needed to do was to create three <div>s with id "visualization1," "visualization2," and so on.   Thus:

 

    
    <div id="visualization2" style="width: 600px; height: 400px;">
     <c:compare_rev_sources ></c:compare_rev_sources>
      </div> 
      
    <div id="visualization3" style="width: 600px; height: 400px;">
     <c:compare_mmbrs_dnrs />
      </div> 

 And then each component needs to insert the chart in a specific div:

 

        // Create and draw the visualization.
        var ac = new google.visualization.ComboChart(document.getElementById('visualization3'));
        ac.draw(data, { ... etc.

 

The way Starz handled it was slightly different.  He includes the line:

function drawTable() { ...

 which creates the table itself, within the <table><tr><td></td></tr></table> structure.  BritishBoyinDC points out that this is a simpler structure.  Using the <div> tags allows for more formatting and manipulation.

 

Hope this helps someone else as they find their way through this,

Baird

 

All Answers

Starz26Starz26

Sure....I have 3 across and 10 down in Google charts.

 

Here is a sample component to use:

 

<apex:component >
<apex:attribute name="jsondata" description="This is the chart data" type="string" required="true" />
    <apex:attribute name="barformat" description="Apply barformat to this column" type="string" />
    <apex:attribute name="barwidth" description="Apply barformat to this column" type="integer" default="90"  />
    <apex:attribute name="decimalformat1" description="Apply decimalformat to this column" type="string" />
    <apex:attribute name="decimalformat2" description="Apply decimalformat to this column" type="string" />
    <apex:attribute name="showRowNumber" description="Show Row Number" type="Boolean" default="true"  />
    <apex:outputPanel id="chart_div">
        <script type="text/javascript" src="http://www.google.com/jsapi"></script>
        <script type="text/javascript">
    
 google.load("visualization", "1", {packages: ["table" ]});
 google.setOnLoadCallback(drawTable); 
 
 function drawTable() {

) );
var data = new google.visualization.DataTable(  { {!jsondata} }  );
   

  var barformatter = "{!barformat}";
 
  if ( barformatter != "" ) { // Apply formatter to this column
    var formatter = new google.visualization.TableBarFormat({width: {!barwidth}});
    formatter.format(data, barformatter); 
  }
  
  var decimalformatter1 = "{!decimalformat1}";
 
  if ( decimalformatter1 != "" ) { 
    decimalformatter1 = parseInt(decimalformatter1);
    var formatter = new google.visualization.TableNumberFormat(
        {prefix: '$'});
    formatter.format(data, decimalformatter1); 
  }


  var decimalformatter2 = "{!decimalformat2}";
  if ( decimalformatter2 != "" ) { 
   decimalformatter2 = parseInt(decimalformatter2);
    var formatter = new google.visualization.TableNumberFormat(
        {prefix: '$'});
    formatter.format(data, decimalformatter2); 
  } 
  
  var table = new google.visualization.Table(  
    document.getElementById('{!$Component.chart_div}'));

   table.draw(data, {allowHtml: true, showRowNumber: {!showRowNumber}});
   
 
 }
</script>
    </apex:outputPanel>
</apex:component>

 and a snippet from the page:

 

<table>
    <tr>
        <td>
            <p id="tblTitle" class="title" > <b>Bottom Accounts by Growth Last 3 Months</b></p>
            <c:GoogleBarChart jsondata="{!growthByAccountBottom3}"  isStacked="true" title="" legend="bottom" decimalFormat1="1" decimalformat2="2" decimalFormat3="3" decimalFormat4="4" width="450" height="500" ShowChart="true"/>
        </td>
        <td>
            <p id="tblTitle" class="title"> <b>Bottom Accounts by Growth Last 6 Months</b></p>
            <c:GoogleBarChart jsondata="{!growthByAccountBottom6}"  isStacked="true" title="" legend="bottom" decimalFormat1="1" decimalformat2="2" decimalFormat3="3" decimalFormat4="4" width="450" height="500" ShowChart="true"/>

        </td>
        <td>
            <p id="tblTitle" class="title"> <b>Bottom Accounts by Growth Last 12 Months</b></p>
            <c:GoogleBarChart jsondata="{!growthByAccountBottom12}"  isStacked="true" title="" legend="bottom" decimalFormat1="1" decimalformat2="2" decimalFormat3="3" decimalFormat4="4" width="450" height="500" ShowChart="true"/>

        </td>
    </tr>
</table>

 

Just placed in a table and use css to format

 

 

 

Baird_SBaird_S

Thanks, Starz.  It looks like what you're doing is more complex than my application.  Let me give it a shot based on the code you sent.  Baird

Baird_SBaird_S

I've got the formatting, and have inserted the components.  But when I put multiple components into the table format, they still render one on top of the other ... or, perhaps, only the last one renders.  I wonder if my problem is in the script that I include in each component.  Right now, my components look like this:

 

<script type="text/javascript">
google.load('visualization', '1', {packages: ['corechart']});
</script>
<script type="text/javascript">
function drawVisualization() {
// Create and populate the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Month'); // Implicit domain label col.
data.addColumn('number', 'Revenue'); // Implicit series 1 data col.
data.addColumn({type:'boolean',role:'certainty'}); // certainty col.
data.addRows([
['Your Organization',111371, false],
['Lowest in Group', 11298, true],
['Average for Group', 491853, true],
['Highest in Group', 1741692, true]
]);

// Create and draw the visualization.
var ac = new google.visualization.ComboChart(document.getElementById('visualization'));
ac.draw(data, {
title : 'How does your revenue compare to others in your comparison group?',
width: 600,
height: 400,
legend: {position: "none"},
seriesType: "bars",
series: {5: {type: "line"}}
});
}

google.setOnLoadCallback(drawVisualization);
</script>

 

Am I including elements in the component that should be in the page header instead, or only referenced once?

Starz26Starz26
How is you VF page formatted? Example of how you have it in the table?
Baird_SBaird_S
    <table>
    <tr>
    <td>
    
    <c:compare_rev_sources />
    </td>
    <td>
    <c:compare_revenue />
    </td>
    </tr>
    </table>

 I'm trying to keep it simple, not even using a controller, just to get the layout straight.

 

Above the table  I have another graph, not in a component.  I haven't been able to figure out which parts go in the component and which parts need to be in the HTML header.  The whole page looks like this:

 

<apex:page >
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>
      Google Visualization API Sample
    </title>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {packages: ['corechart']});
    </script>
    <script type="text/javascript">
      function drawVisualization() {
        // Create and populate the data table.
      var data = new google.visualization.DataTable();
      data.addColumn('string', 'Month'); // Implicit domain label col.
      data.addColumn('number', 'Revenue'); // Implicit series 1 data col.
      data.addColumn({type:'boolean',role:'certainty'}); // certainty col.
      data.addRows([
          ['Your Organization',111371,  false],
          ['Lowest in Group',  11298, true],
          ['Average for Group',  491853, true],
          ['Highest in Group', 1741692,  true]
      ]);
      
        // Create and draw the visualization.
        var ac = new google.visualization.ComboChart(document.getElementById('visualization'));
        ac.draw(data, {
          title : 'How does your revenue compare to others in your comparison group?',
          width: 600,
          height: 400,
          legend: {position: "none"},
          seriesType: "bars",
          series: {5: {type: "line"}}
        });
      }
      

      google.setOnLoadCallback(drawVisualization);
    </script>
  </head>
  <body style="font-family: Arial;border: 0 none;">
    <div id="visualization" style="width: 600px; height: 400px;"></div>
    <table>
    <tr>
    <td>
    
    <c:compare_rev_sources />
    </td>
    <td>
    <c:compare_revenue />
    </td>
    </tr>
    </table>
    
    
  </body>
</html>

<p></p>


</apex:page>

 On this page, I only see the last component rendered.  If I remove one, the previous one appears.

 

Here's the code I'm using for one of my components:

 

<apex:component >
  <apex:outputPanel id="chart_div">
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {packages: ['corechart']});
    </script>
    <script type="text/javascript">
      function drawVisualization() {
        // Create and populate the data table.
      var data = new google.visualization.DataTable();
      data.addColumn('string', 'Source'); // Implicit domain label col.
      data.addColumn('number', 'Your organization'); // Implicit series 1 data col.
      data.addColumn({type:'boolean',role:'certainty'}); // certainty col.
      data.addColumn('number', 'Average for your comparison group');
      data.addRows([
          ['Dues',17, false, 9],
          ['Indiv Donations',  17, false, 15],
          ['Events',  15, false, 15],
          ['Foundations', 35, false, 32],
          ['Corporations', 0, false, 5],
          ['Federal', 0, false, 9],
          ['State', 6, false, 6],
          ['Other Govts', 2, false, 3],
          ['Other Sources', 2, false, 6]
      ]);
      
        // Create and draw the visualization.
        var ac = new google.visualization.ComboChart(document.getElementById('visualization'));
        ac.draw(data, {
          title : 'How does your distribution of revenue compare to others in your sample? (Values are percentages of total revenue.)',
          width: 600,
          height: 400,
          vAxis: {title: "Percent'"},
          legend: {position: "right"},
          seriesType: "bars",
        });
      }
      

      google.setOnLoadCallback(drawVisualization);
    </script>
</apex:outputPanel>
</apex:component>

 Thanks for your help with this.

 

 

Starz26Starz26

Your element to palce the chart in needs to be in the component.....

 

document.getElementById('visualization'

 

If you look at mine the put the chart in the ID chart_div and I have that in the component not in the VF page

Baird_SBaird_S

Starz, thanks a ton for your suggestions.  They pointed me right to the solution, which I found with help from BritishBoyinDC as well.  Here's the gist of it:

 

Each one of my components was replacing the same <div id="visualization"> in this section:

 

        // Create and draw the visualization.
        var ac = new google.visualization.ComboChart(document.getElementById('visualization'));
        ac.draw(data, { ... etc.

 

What I needed to do was to create three <div>s with id "visualization1," "visualization2," and so on.   Thus:

 

    
    <div id="visualization2" style="width: 600px; height: 400px;">
     <c:compare_rev_sources ></c:compare_rev_sources>
      </div> 
      
    <div id="visualization3" style="width: 600px; height: 400px;">
     <c:compare_mmbrs_dnrs />
      </div> 

 And then each component needs to insert the chart in a specific div:

 

        // Create and draw the visualization.
        var ac = new google.visualization.ComboChart(document.getElementById('visualization3'));
        ac.draw(data, { ... etc.

 

The way Starz handled it was slightly different.  He includes the line:

function drawTable() { ...

 which creates the table itself, within the <table><tr><td></td></tr></table> structure.  BritishBoyinDC points out that this is a simpler structure.  Using the <div> tags allows for more formatting and manipulation.

 

Hope this helps someone else as they find their way through this,

Baird

 

This was selected as the best answer
Baird_SBaird_S

After this exchange, I still could not get multiple charts to post on one page using a structure like this:

 

<apex:component>
<head>
...
var data ... etc.
var ac = new google.visualization.ComboChart(document.getElementById('visualization'));
        ac.draw(data, { ... etc.
...
</head>
<body>
<div id="visualization></div>
</body>
...
etc.
</apex:component>

 

I was attempting to reuse the component on a visualforce page, thus:

 

<apex:page >
<c:compare_mmbrs_dnrs titletext="This title inserted from VF page" other_attributes ... etc.></c:compare_mmbrs_dnrs >

<c:compare_mmbrs_dnrs titletext="This is the second of the compare members components" other_attributes ... etc.>
</c:compare_mmbrs_dnrs>
</apex:page>

 The problem, I eventually figured out, was that the second time I called the component, when it looked for the "visualization" div, it actually found the visualization div from the first time I had called the component, and overwrote that.  This I verified.

 

BritishBoyinDC was able to dig into the page source code to see that Salesforce could actually differentiate between the two divs in the separate instances of the component.  He came up with this fix, which uses a "

getElementById('{!$Component.theChart}')

to specify the div within this particular component, and 

 <apex:outputPanel style="width: 600px; height: 400px;" layout="block" id="theChart">

to create a div where the component will render.  Here's the entire component:

 

<apex:component >
<apex:attribute name="titletext" type="String" description="TitleText for this graph"/>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {packages: ['corechart']});
    </script>
     <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {packages: ['corechart']});
    </script>
    <script type="text/javascript">
      function drawVisualization() {
        // Create and populate the data table.
      var data = new google.visualization.DataTable();
      data.addColumn('string', 'Source'); // Implicit domain label col.
      data.addColumn('number', 'Your organization'); // Implicit series 1 data col.
      data.addColumn({type:'boolean',role:'certainty'}); // certainty col.
      data.addColumn('number', 'Average for your comparison group'); 
      data.addRows([
          ['Members',375, false, 864],
          ['Donors',  447, false, 591]
      ]);
      
        // Create and draw the visualization.
       var ac = new google.visualization.ComboChart(document.getElementById('{!$Component.theChart}'));
         ac.draw(data, {
          title : '{!titletext}',
          width: 600,
          height: 400,
          vAxis: {title: "Number of Members or Donors'"},
          legend: {position: "right"},
          seriesType: "bars",
        });
      }
      

      google.setOnLoadCallback(drawVisualization);
    </script>
   <apex:outputPanel style="width: 600px; height: 400px;" layout="block" id="theChart"></apex:outputPanel>
</apex:component>

 Baird

Starz26Starz26

Or as in my code above,

 

simply place the chart div inside the component......