简体   繁体   中英

Load a Google Chart from an asynchronous AJAX response

In the old Google Chart API it was possible to use PHP to render a chart, there was even a wrapper to do it: http://code.google.com/p/gchartphp/

But with the new Chart API http://code.google.com/apis/chart/ which produces much fancier chart, but loads only with javascript in the browser.

The effect I am trying to achieve is submitting a multiple choice form to the server via AJAX, have the PHP update the DB serverside, then return the updated chart.

In the old API way I could have done this. But in the new way I would be returning javascript to the browser and appending it to the document in order to render the chart. It wouldn't execute because of this. I believe I could eval() this javascript but that is bad form isn't it because someone could do some nasty stuff with that couldn't they - eval()ing a serverside response?

How can I overcome this? Is there a PHP wrapper to help this? Or is there another why which I have overlooked?

Many thanks

The way I solved this issue was pretty obvious in the end. I just needed to step outside my thoughts and approach it from a different angle.

I was trying to all the leg work of processing the database information AND creating the Google Chart (a Pie Chart in this case) on the server side, using PHP. Then return that as the AJAX response.

Actually all I needed to do was return the data needed to create the Pie Chart (along with some extra meta data such as target element id). I did this as JSON. Then by picking through the Google Charts documentation I was able to find out how to trigger the API using client side javascript to load the returned JSON data. Then let the Google code render the chart client side - so I basically shifted all the rendering responsibility to the client browser. This is where the old and new chart APIs differ.

It took a lot of trial and error and plenty of help from Firebug. One big tripping point which is worth mentioning is that you need to load all the Google JSAPI before you receive the JSON response - so as the initial page renders - and one thing you must do with:

google.load("visualization", "1", {packages:["corechart"]});

is make sure it loads as the page is rendering. If you call this after the page has completely rendered then the page will reload.

Hope this helps someone.

Thanks to andyg1 and Ascendant , I was able to make it work like this (using PrototypeJS rather than jQuery but the idea is the same). Since this is not at all obvious, I'm going to show all the steps.

The Ajax endpoint just returns json and looks like this (a .NET MVC template). Note that I found I had to quote everything which Google's documentation does not suggest is necessary:

<% 
Response.Headers.Add("Content-type", "text/json");
Response.AddHeader("Content-type", "application/json");
%>
{
  "cols": [
    {"id": "col_1", "label": "Date", "type": "string"},
    {"id": "col_2", "label": "Score", "type": "number"}
  ],
  "rows": [
    <%
    int index = 0;
    foreach(KeyValuePair<string, double> item in Model.Performance ) { %>
      {"c":[{"v":"<%= item.Key %>"}, {"v":<%= item.Value %>}]}<%= (index == Model.Performance.Count - 1) ? "" : "," %>
      <% index++; %>
      <% 
    } 
    %>
  ]
}

Then the master page contained this:

<script type="text/javascript" src="https://www.google.com/jsapi"></script> 
<script type="text/javascript" src="/js/myJavascriptFile.js" />

Then in myJavascriptFile.js (note the last line of the initialize method is google.setOnLoadCallback which calls a method in my class not drawChart):

google.load('visualization', '1', {'packages':['corechart']});
var colors = {'blue': '#369', 'red': '#c22', 'green': '#283', 'yellow': '#c91'};

var MyClass = Class.create({

  initialize: function() {
    ... 
    google.setOnLoadCallback(this.getTeamCharts);
  },

  getTeamCharts: function () {
    $$(".chart-wrapper").each(function (div) {
      var chartData = div.getData();
      var parameters = {
        ...
      };

      new Ajax.Request('/endpoints/TeamChart.aspx', {
        method: 'get',
        parameters: parameters,
        onSuccess: function(transport) {
            var jsonData = transport.responseJSON;
            var data = new google.visualization.DataTable(jsonData);
            var chartColor = colors[parameters.TeamColor];
            var chartDivId = 'chart_div_' + parameters.TeamIdAsString;

            // Set chart options
            var options = {
              'chartArea': {'left':'15%','top':'15%','width':'80%','height':'70%'},
              'legend': {'position': 'none'},
              'lineWidth': 3,
              'width': 262, 
              'height': 180,
              'colors': [chartColor]
            };

            // Instantiate and draw our chart, passing in some options.
            var chart = new google.visualization.LineChart(document.getElementById(chartDivId));
            chart.draw(data, options);
        }
      });
    });
  }

});

document.observe("dom:loaded", function () {
  var thing = new MyClass();
});

I'm sure it could be further improved but it works!

I've handled situations like this in the past using a hidden field that's returned as part of the request with the relevant data in whatever format you want. No javascript is returned. On the ajax's success callback, you'd grab and handle the data from that input.

EDIT:

basically, if you're directly getting via ajax html to update the page and you don't want to change how that functions , put as part of the ajax response a hidden input

<input type="hidden" name="yourhiddeninputname" id="yourhiddeninputname" 
   value="whatever|data|you|want,blah,blah,blah" /> 

Then, once the html is injected into your page , you would do something like a

var yourdata = document.getElementById('yourhiddeninputname').value;
// do stuff with your data here.

Is that the best option? I'm not sure. It really depends upon how you're handing your ajax. I made my original comments coming from an ASP.NET background, where, often, you are counting on certain class instances to generate html for you and you are kinda abstracted from the actual html (and other things) being generated. There are certainly other ways you could handle this, especially if you have full control as to how the AJAX response is rendered and handled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM