Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I'm using amcharts to build graph on a visualforce page. The amcharts's input is JSON. So I parse my object to JSON string using JSON.serializePretty. Using this approach, the graph is built fine and fast as long as data size is small. But the VF page will be loaded more and more slowly if I increase the data size. Finally, the following error will occur because of the appearance of large JSON string in VF page.

Response size exceeded 15MB organization limit

How could I fix this problem? Is there anyway to pass large string from controller to VF page more efficiently? Any advice would be appreciated.

My code

This is a part of the controller:

private List<ChartRecord> chartData;

public ChartController(){
    getAllData();
    //JSON string created here.
    dataJson = JSON.serializePretty(chartData);
}

// Wrapper class
public class ChartRecord {
    public Integer rowIndex { get; set; }
    public Decimal score { get; set; }
    public ChartRecord (Integer rowIndex, Decimal score) {
        this.rowIndex = rowIndex;
        this.score = score/100;
    }

And then the JSON string is passed to amcharts.

AmCharts.ready(function () {
var data = {!dataJson};
....
}

The JSON string shown in page source:

AmCharts.ready(function () {
    var data = [ {
  "score" : 0.996920641888553,
  "rowIndex" : 1
}, {
  "score" : 1.9421192769522631,
  "rowIndex" : 2
}, {
  "score" : 2.8873179120159732,
  "rowIndex" : 3
}, ...
share|improve this question
    
I think you need to exclude your chartData variable from the view state by adding the transient keyword to it - salesforce.com/us/developer/docs/apexcode/Content/… See if this fixes your problem. –  Bachovski Jul 30 at 6:06
    
@Bachovski I think weather transient or not it will count against the response limit (but if transient, then not against the viewstate limit of 135k) –  Uwe Heim Jul 30 at 6:58
    
@Bachovski Thank you for the advice. I tried transient before but nothing changed. –  lw29 Jul 30 at 7:16

2 Answers 2

The 15 MB is a hard limit. There are several possible workarounds.

First, you should consider using JavaScript remoting to retrieve the data asynchronously. This will allow your page to load quickly and not show a simple white screen while waiting for the data to load.

Secondly, you may need to paginate your data to keep under the 15MB limit. Again, remoting will work well here, because you can just call the function iteratively to get all of your data.

Thirdly, I'd suggest that you not use serializePretty, as this adds a lot of extra whitespace and so on-- it's designed to make the JSON look pretty, but this cost will come in the form of extra bytes. JavaScript doesn't need the JSON to look pretty, but humans find it easier to read. You'll be able to squeeze more performance out of your page using JSON.serialize instead.

share|improve this answer
    
Thank you for your reply. I'm working on JavaScript remoting right now. I'll come back with result. :) –  lw29 Jul 30 at 7:18
    
Thank you for your help. I solved the problem and finally got rid of JSON. :) –  lw29 Jul 31 at 5:43
up vote 0 down vote accepted

I will post what I did to solve the problem here.

Thanks to @sfdcfox. I looked into Javascript remoting and came up with this solution. Finally I could replace the large JSON string in my visualforce page with very small amount of code and improve performance.

My solution:

Javascript remoting and amcharts validateData function.

Controller:

public static List<ChartRecord> chartData; 
    // Wrapper class
public class ChartRecord {
    public Integer rowIndex { get; set; }
    public Decimal score { get; set; }
    public ChartRecord (Integer rowIndex, Decimal score) {
        this.rowIndex = rowIndex;
        this.score = score/100;
    }
}
@RemoteAction
public static List<ChartRecord> getChartDataSet(){
    if (ChartController.chartData == null)
        getChartData(); 
    return ChartController.chartData;
}
private static void getChartData(){
    //prepare the data
}

Amcharts:

var chart;
AmCharts.ready(function () {
//create your chart here 
//insert the mock dataProvider - everything you like

// WRITE                             
chart.write("chart");

//this is the remoting method 
//call it after the empty chart is built
generateData();
});

Javascript remoting and amcharts validateData:

function generateData(){
var dataProvider = []; //new data obtained via remoting
Visualforce.remoting.Manager.invokeAction(
    '{!$RemoteAction.ChartController.getChartDataSet}',        
    function(result, event) {                
        if(event.type === 'exception') {
           //Exception handling
        } else if (event.status) {
            //put all the result in dataProvider
            for (n = 0; n< result.length; ++n) { 
                dataProvider.push({rowIndex:result[n].rowIndex, score:result[n].score});
            }
            //switch chart.dataProvider(mock) to the remoting data
            chart.dataProvider = dataProvider;
            //rewrite the chart
            chart.validateData();
            $("#loader").fadeOut("slow");//I'll explain this line later
        } else {
        }
    },
    {buffer: false, escape: true, timeout: 30000 }
);
}

Because javascript remoting's processed asynchronously, sorting by row index is required if I want my chart to be displayed being ordered by row index. So I decided to get the result only once instead of getting data iteratively. (I tried "iteratively getting data and sorting" but didn't check whether the performance is better or worse.)

With all the codes mentioned above, instead of a blank page, a visualforce page(with a not-yet-have-any-data chart) will be immediately shown while the chart data is being downloaded. But empty chart might confuse and worry users so I added a loading image over the graph to indicate that the graph is being generated.

<div id="loaderWrapper" >
<div id="gainchart" ></div><br />
<div id="loader"></div> <!--loading image-->

This line in generateData() hides the loading image after the graph is rerendered.

$("#loader").fadeOut("slow"); 

Thank you for your help!

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.