function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
arghhhhharghhhhh 

Drawing google chart on page load always empty

Hi All,

 

I had a page which was working perfectly rendering 3 pageblock sections and a google chart section.  I modified this code to now show a "now loading" screen while it fetches the elements and this has now broken the google charts portion for some reason.  Charts always shows "no data" now.

 

My guess is because the script tag used to pull this data in from the controller is now run at a time when my function that pulls this data together in the backend is not yet ready.. not sure.

 

Here is what is intended:

1. on page load a "please wait while fetching info.." pops up and a single controller method is called pulling all relevant data together.

2. once the method is complete a pageblocktable is populated and the google charts data is drawn.

 

That's it.  I've tried several workarounds to this but all to no avail.  I've posted the portions of the vf which matter for now.  The controller has always worked so I have not posted that yet.

 

Thanks in advance to all who help!

 

...
<apex:actionstatus id="pleasewait">
            <apex:facet name="start">
                <div class="waitingSearchDiv" id="el_loading" style="background-color: #fbfbfb;
                       height: 100%;opacity:0.65;width:100%;"> 
                    <div class="waitingHolder" style="top: 74.2px; width: 91px;">
                        <img class="waitingImage" src="/img/loading.gif" title="Please Wait..." />
                        <span class="waitingDescription">Loading...</span>
                    </div>
                </div>
            </apex:facet>
        </apex:actionstatus>    	
    	
    	<apex:actionFunction action="{!getFinAccts}" name="initialLoad" reRender="finAccountsTable, finAcctAnalytics" oncomplete="google.setOnLoadCallback(drawChart);" status="pleasewait" />
    	<!--Load the AJAX API-->   
    	<script type="text/javascript" src="https://www.google.com/jsapi"></script>    	    		             
		<script type="text/javascript">        	
			// Load the Visualization API and the piechart package.
	           google.load('visualization', '1.0', {'packages':['corechart']});
	
	           // Set a callback to run when the Google Visualization API is loaded.
	           google.setOnLoadCallback(drawChart);
	       
	           // Callback that creates and populates a data table,
	           // instantiates the pie chart, passes in the data and
	           // draws it.
	           function drawChart() {
	       
	               // Create the data table.
	               var data = new google.visualization.DataTable();
	               data.addColumn('string', 'Account Type');
	               data.addColumn('number', '# of Accounts');
	               console.debug('DataTable: {!dataTable}');
	               {!dataTable}                
	       
	               // Set chart options
	               var options = {'title':'{!Account.Name}',
	                              'width':400,
	                              'height':300};
	       
	               // Instantiate and draw our chart, passing in some options.
	               var chart = new google.visualization.PieChart(document.getElementById('chart'));
	               chart.draw(data, options);
	           }            
	    </script>
    	
    	<script type="text/javascript">    	
        	window.setTimeout(initialLoad, 100);        	        	            
    	</script>
...
        <!-- Financial Account Analytics -->
        <apex:outputPanel id="finAcctAnalytics" layout="block">
	    	<!--Div that will hold the pie chart-->
	        <div id="chart" layout="block" style="overflow:auto;" />	        	        		        	            				              	        
		</apex:outputPanel>                  		  		        

 

Best Answer chosen by Admin (Salesforce Developers) 
bob_buzzardbob_buzzard

So the javascript appears in the page after the re-rendering but isn't accessible?  That's pretty strange.  When I was trying to do the dashboard refresh, alerts were the one thing that worked as expected.

 

Could you approach this a different way and rather than using rerendering, simply execute the actionfunction and allow the entire page to refresh?  You can still use the conditional rendering, but all the javascript will be available as soon as the page has loaded.

All Answers

bob_buzzardbob_buzzard

In this section of javascript&colon;

 

 google.load('visualization', '1.0', {'packages':['corechart']});

// Set a callback to run when the Google Visualization API is loaded. google.setOnLoadCallback(drawChart);

 

You are scheduling the drawChart method to be invoked once the page is fully loaded.  I'd expect this to happen before your actionfunction is invoked, resulting in an empty chart.  

 

In your actionfunction:

 

<apex:actionFunction action="{!getFinAccts}" name="initialLoad" reRender="finAccountsTable, finAcctAnalytics" oncomplete="google.setOnLoadCallback(drawChart);" status="pleasewait" />

when the ajax request completes you insert another callback for when the page has loaded, but by that time its too late, as the page has already loaded and been rendered.

 

Try removing the  

 // Set a callback to run when the Google Visualization API is loaded.
 google.setOnLoadCallback(drawChart);

lines and changing the actionfunction oncomplete attribute to:

 

oncomplete="drawChart();"

 

arghhhhharghhhhh

Thanks bob,

 

tried removing the google.setonLoad... and adding the drawChart(); to on complete but it' still showing up with no data. 

 

Is it possible that my {!dataTable} variable in the controller is empty when it is initially loaded on the page and so is always blank?

 

Just can't undersatnd why, thought for sure what you suggested would do the trick as it made perfect sense.

bob_buzzardbob_buzzard

That's highly likely to be the last piece in the jigsaw.  The getter will execute when the page is rendered, rather than when the javascript is executed.   One way around this may be to only render the drawChart function once your asynch processing has completed and execute it at that point.

 

I wrote a blog post a while ago that contains an example of this.  I'll see if I can locate it.

bob_buzzardbob_buzzard

Here it is:

 

http://bobbuzzard.blogspot.com/2011/06/automatic-dashboard-refresh.html

 

Its not the clearest example, as I was demonstrating something else.  The basic premise is that the canRefresh javascript is only rendered (and then executed) once other activity has completed (in this case running down a timer to that action).

arghhhhharghhhhh

Thanks Bob your example helped and now I have a added the nessecary changes although now the reRender isn't rendering the google chart section.  

 

The value is not always false as I've confirmed in the backend it's true.  Am I doing something wrong with how I'm reRendering?  

Thanks again for all the help, hopefully this will be the last :\

 

...
		<apex:actionFunction action="{!getFinAccts}" name="initialLoad" reRender="finAcctAnalytics, finAccountsTable" oncomplete="drawChart();" status="pleasewait" />    	
    	    	    	
    	<script type="text/javascript">    	
        	window.setTimeout(initialLoad, 100);     			     	        	     		      	        	          
    	</script>
		
		...
		
		 <!-- Financial Account Analytics -->
        <apex:outputPanel id="finAcctAnalytics" layout="block" rendered="{!chartReady}" >
        	<!--Load the AJAX API-->   
    	<script type="text/javascript" src="https://www.google.com/jsapi"></script>    	    		             
		<script type="text/javascript">        	
			// Load the Visualization API and the piechart package.
	        google.load('visualization', '1.0', {'packages':['corechart']});
	
	        // Set a callback to run when the Google Visualization API is loaded.
	        //google.setOnLoadCallback(drawChart);
	       
	        // Callback that creates and populates a data table,
	        // instantiates the pie chart, passes in the data and
	        // draws it.
	        function drawChart() {
	       
	        	// Create the data table.
	            var data = new google.visualization.DataTable();
	            data.addColumn('string', 'Account Type');
	            data.addColumn('number', '# of Accounts');
	            console.debug('DataTable: {!dataTable}');
	            {!dataTable}                
	       
	            // Set chart options
	            var options = {'title':'{!Account.Name}',
	            	'width':400,
	                'height':300};
	       
				// Instantiate and draw our chart, passing in some options.
	            var chart = new google.visualization.PieChart(document.getElementById('chart'));
	            chart.draw(data, options);
			}
			
			drawChart();
			console.debug('test');            
	    </script>
	    	<!--Div that will hold the pie chart-->	    		    	
	        <div id="chart" layout="block" style="overflow:auto;" />	        	        	        	        		        	            				              	       
		</apex:outputPanel>
		...

 

bob_buzzardbob_buzzard

I think that finAcctAnalytics will not be rerendered as its not there in the first place - this is explained more at another blog post of mine - Visualforce rerendering woes:

 

http://bobbuzzard.blogspot.com/2011/02/visualforce-re-rendering-woes.html

 

Also, I think the oncomplete may now be problematic, as I'm not sure about the race condition of the drawchart function being rendered and executed. In fact, you shouldn't need that now as the rerendered finAcctAnalytics calls the function after defining it.

arghhhhharghhhhh

Thank's again Bob,  they are definitely 'woes' :)

 

Yup I noticed there's really no need for the onComplete and got rid of it and fixed the rerender issue by wrapping the googlechart outputPanel in another parent outputPanel which did the trick.  Only thing I'm having trouble with now is actually drawing the chart.  For some reason even though the rendered="{!chartRender}" value is true nothing is displayed but the JS is now showing up in the dom.  this is driving me batty but I couldn't have gotten here without your help so thank you.  Seems as though it's almost there fortunately :)

 

I threw a console.debug in there and it's being printed in the dom but seems like it's never executed.  I tried adding the onComplete back in to the actionFunction but still no luck.  ' added code for reference.

 

...
<apex:actionFunction action="{!getFinAccts}" name="initialLoad" reRender="finAccountsTable, finAcctAnalytics" oncomplete="drawChart();" status="pleasewait" />

<script type="text/javascript">    	
window.setTimeout(initialLoad, 100);     			     	        	     		      	        	          
</script>
...
        <!-- Financial Account Analytics -->
        <apex:outputPanel id="finAcctAnalytics" >

        	<apex:outputPanel layout="block" rendered="{!chartReady}">
	        	<!--Load the AJAX API-->   
	    		<script type="text/javascript" src="https://www.google.com/jsapi"></script>    	    		             
				<script type="text/javascript">        	
					// Load the Visualization API and the piechart package.
			        google.load('visualization', '1.0', {'packages':['corechart']});
			
			        // Set a callback to run when the Google Visualization API is loaded.
			        //google.setOnLoadCallback(drawChart);
			       
			        // Callback that creates and populates a data table,
			        // instantiates the pie chart, passes in the data and
			        // draws it.
			        function drawChart() {
			       
			        	// Create the data table.
			            var data = new google.visualization.DataTable();
			            data.addColumn('string', 'Account Type');
			            data.addColumn('number', '# of Accounts');
			            console.debug('DataTable: {!dataTable}');
			            {!dataTable}                
			       
			            // Set chart options
			            var options = {'title':'{!Account.Name}',
			            	'width':400,
			                'height':300};
			       
						// Instantiate and draw our chart, passing in some options.
			            var chart = new google.visualization.PieChart(document.getElementById('chart'));
			            chart.draw(data, options);
					}			            
		    	</script>		    	
	        </apex:outputPanel>
	        <!--Div that will hold the pie chart-->	    		    	
			<div id="chart" layout="block" style="overflow:auto;" />
	        	        	        	        		        	            				              	       
		</apex:outputPanel>	
...

 

 

As a side, I was initially having trouble with my chartReady setter with it sticking to false even though I had set it to true.

 

Initially I had it defined as:

boolean chartReady {get; set {this.chartReady = false;} }

 

then later in my class I say ... this.chartReady = true;  but it was always false.  I then changed the setter to:

boolean chartReady {get; set;}

 

and set th value to false in the constructor and that did the trick.  I found that really strange.

bob_buzzardbob_buzzard

Have you checked the javascript console to see if there are any errors?

arghhhhharghhhhh

yup, no errors.  So it's all there but just not executing.  I'm continually playing with it to see what's going on

bob_buzzardbob_buzzard

Bummer.  Alerts in there to check that the drawCharts is available and executing?

arghhhhharghhhhh

yeah they don't even fire :| 

 

and when I try to manually execute drawChart(); it says it can't find the method.  so looks like the rerender approach works for getting over the javascript issue and adds the js dynamically to the page when needed but doesn't execute it so its' hack time it seems like :(

bob_buzzardbob_buzzard

So the javascript appears in the page after the re-rendering but isn't accessible?  That's pretty strange.  When I was trying to do the dashboard refresh, alerts were the one thing that worked as expected.

 

Could you approach this a different way and rather than using rerendering, simply execute the actionfunction and allow the entire page to refresh?  You can still use the conditional rendering, but all the javascript will be available as soon as the page has loaded.

This was selected as the best answer
arghhhhharghhhhh
<apex:page standardController="Account" extensions="FinAccountsSectionController" action="{!getFinAccts}">
...
 <!-- Financial Account Analytics -->        
        <apex:outputPanel id="finAcctAnalytics" >        	        		        		
        	<apex:outputPanel layout="block" >
              <!--Load the AJAX API-->   	   
				<script type="text/javascript" src="https://www.google.com/jsapi"></script>               		    	    		             
				<script type="text/javascript">				        
					// Load the Visualization API and the piechart package.
			        google.load('visualization', '1.0', {'packages':['corechart']});
			
			        // Set a callback to run when the Google Visualization API is loaded.
			        google.setOnLoadCallback(drawChart);
			       
			        // Callback that creates and populates a data table,
			        // instantiates the pie chart, passes in the data and
			        // draws it.
			        function drawChart() {
			       
			        	// Create the data table.
			            var data = new google.visualization.DataTable();
			            data.addColumn('string', 'Account Type');
			            data.addColumn('number', '# of Accounts');			            			            			            
			            {!dataTable}                
			       
			            // Set chart options
			            var options = {'title':'{!Account.Name}',
			            	'width':400,
			                'height':300};
			       
						// Instantiate and draw our chart, passing in some options.
			            var chart = new google.visualization.PieChart(document.getElementById('chart'));
			            chart.draw(data, options);
					}																							          
		    	</script>		    	        	
        		<!--Div that will hold the pie chart-->	    		    	
				<div id="chart" layout="block" style="overflow:auto;" />			    	    	
	        </apex:outputPanel>	        	         	        	        	        		        	            				              	       
		</apex:outputPanel>		
...

 

Not sure why the alerts were firing in your blog post and nothing was firing on my end, maybe previous apex version allowed for that functionality and now it's plugged? not sure why that would be though.

 

I went ahead and added the getFinAccntList as an action method in the apex:page section and uncommented the google.setOnLoad... call so that by the time the js is read the list is already populated.  Would have loved to get the 'cleaner' solution to work but this works as well.  The actionFunction method is now deprecated as a result :\

 

Thanks for all your help and I hope I can return the favour in the future!

SandeepVSandeepV

Hi arghhhhh

 

is it possible for you to post the entire code or send it to me by Private message.

 

I am facing a similar issue , so wanted to refer to your code.

 

Thanks 

 

SandeepV

Sreesh GundaSreesh Gunda
Hi. I am trying to use above code, getting similar issue. Unable to fire the SOQL in Apex Class. Could someone please help.

Class:
global with sharing class GanttTasks1 {
@RemoteAction
global static HiTech__Project_Tasks__c[] loadrecords() {
  return [Select id,  HiTech__ProjectId__c,HiTech__Start_Date__c, HiTech__End_Date__c, HiTech__Task_Title__c, HiTech__Task_Status__c
                   ,HiTech__Percentage__c, HiTech__Duration_Days__c, HiTech__Gantt_Bar_line__c, HiTech__Dependent_on_Task__c
                     ,HiTech__Task_Id__c 
                                                   from HiTech__Project_Tasks__c];
}
       
}


Page:
   <apex:page controller="GanttTasks1" sidebar="false"> 
    <apex:includeScript id="a" value="https://www.google.com/jsapi" />
    <apex:sectionHeader title="Google Charts + Javascript Remoting" subtitle="Project Tasks Gantt"/>
    <div id="chart_div" />
   
    <script type="text/javascript">
     
        google.charts.load('current', {'packages':['gantt']});
        google.setOnLoadCallback(drawChart);
        function drawChart() {        
         GanttTasks1.loadrecords(function(result, event){  
                    
                    
                     var data = new google.visualization.DataTable();
                      data.addColumn('string', 'Task ID');
                  data.addColumn('string', 'Task Name');
                  data.addColumn('date', 'Start Date');
                  data.addColumn('date', 'End Date');
                  data.addColumn('number', 'Duration');
                  data.addColumn('number', 'Percent Complete');
                  data.addColumn('string', 'Dependencies'); 
                    
                     for (var i = 0; i < result.length; i++) {
                    var r = result[i];
                    data.addRow([r.HiTech__Task_Id__c, r.HiTech__Task_Title__c, new Date(r.HiTech__Start_Date__c), new Date(r.HiTech__End_Date__c),
                                 r.HiTech__Duration_Days__c,r.HiTech__Percentage__c,r.HiTech__Dependent_on_Task__c]);
                      }
                    var chart = new google.visualization.Gantt(document.getElementById('chart_div'));
                    chart.draw(data, options);
              }, {escape:true});
          }
    </script>
</apex:page>