I have a web page (written with Google Apps Script) that on initial load, calls the server, first to get a group of drop down options to create filters, then calls again to initialize a timeline chart. I've had this page for a long time, and it works just as intended.
What I am working on now is saving some of the data to sessionStorage, so that it doesn't have to call the server every time, in case a user navigates away from the page. This is the first time I'm trying to do this, so I'm still learning about it.
My first step was to save the drop down items. Once I got that to work, I would try to do the same thing with the chart data.
I've left some original code in, but commented out.
$(function() {
//google.script.run.withSuccessHandler(buildDropDowns).buildDropDowns();
//google.script.run.withSuccessHandler(getChartData).getChartData("-", "-", "-", "-", "-", "-", "-");
testSession();
});
function testSession() {
if(sessionStorage.getItem("drop") == null) {
google.script.run.withSuccessHandler(buildDropDowns).buildDropDowns();
} else {
buildDropDowns(sessionStorage.getItem("drop"));
}
if(sessionStorage.getItem("chart") == null) {
google.script.run.withSuccessHandler(getChartData).getChartData("-", "-", "-", "-", "-", "-", "-");
} else {
getChartData(sessionStorage.getItem("chart"));
}
}
function buildDropDowns(JSONObj) {
sessionStorage.setItem("drop", JSONObj);
var dropDownObj = JSON.parse(JSONObj);
// do a bunch of things with the dropDownObj
}
function getChartData(JSONObj) {
sessionStorage.setItem("chart", JSONObj);
var dataObj = JSON.parse(JSONObj);
var rawChartData = dataObj.chartData;
var count = dataObj.count;
var colors = dataObj.colors;
const dateIndices = [3,4];
const chartData = rawChartData.map(function (row) {
return row.map(function (col, index) {
return (dateIndices.indexOf(index) === -1) ? col : new Date(col);
});
});
if(chartData.length == 1) {
document.getElementById("chartDiv").innerHTML = "<b>No projects meet your criteria</b>";
} else {
buildChart(chartData, count, colors);
}
}
google.charts.load('current', {'packages':['timeline']});
function buildChart(chartData, count, colors) {
var container = document.getElementById('chartDiv');
//*** EDIT - THIS APPEARS TO BE PROBLEM LINE ***
var chart = new google.visualization.Timeline(container);
//Is there something about visualizations that are different on a reload?
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Project' });
dataTable.addColumn({ type: 'string', id: 'dummy bar label' });
dataTable.addColumn({ type: 'string', role: 'tooltip', p: {'html':true}});
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addColumn({ type: 'number', id: 'sortValue' });
dataTable.addColumn({ type: 'string', id: 'gate' });
dataTable.addRows(chartData);
var options = {
height: Math.min(800, (chartData.length*45) 60),
width:"100%",
};
var view = new google.visualization.DataView(dataTable);
view.setColumns([0, 1, 2, 3, 4]); // exclude column 5 (sequence)
chart.draw(view, options);
}
The dropdown section does do what I expected it to. The first time opening the page, it calls the server to get the data, and saves the JSONObj to sessionStorage. When either refreshing the page, or navigating away and then back again, I can see that rather than going to the server to get those dropdown items, it gets JSONObj from the sessionStorage, and processes it correctly. Success!!
So I tried the same thing with the chart data. And again, when it first loads the page, it goes to the server to get the data, and saves JSONObj to sessionStorage. But when I refresh the tab (or navigate away and back), it does not correctly process JSONObj from sessionStorage. I am getting an 'undefined' error, that I can't track down.
Using console.log(), it appears that the JSONObj is the same when it is originally sent from the server as it is when it is retrieved from sessionData. But clearly, there's something about it that the rest of the code doesn't like. It process the original correctly, but errors out on the sessionData copy.
Is this just something stupid that I'm missing (again, this is the first time I'm trying to do things with sessionData)? Or is there something in the way sessionData stores the JSONObj that changes it in some way, so that it can't be processed the same way when retrieved? Why is it working for the dropDown process, but not the chartData process?
Any nudges in the right direction would be appreciated.
CodePudding user response:
Thank you to @RonM for pointing me in the right direction. What I've now learned is that, previously, the visualization package was given time to load, while the server was being called to capture the data. Once I took the server out of the flow on subsequent loads, the data was trying to write to the timeline, before the timeline had a chance to complete loading.
I've made a couple updates to the code based on this, to make sure the chart package loads before it tries to process the data from sessionData.
$(function() {
//google.script.run.withSuccessHandler(buildDropDowns).buildDropDowns();
//google.script.run.withSuccessHandler(getChartData).getChartData("-", "-", "-", "-", "-", "-", "-");
//testSession(); //COMMENT OUT NOW
google.charts.load('current', {'packages':['timeline'], callback: testSession }); // ADDED IN ITS PLACE
});
function testSession() {
if(sessionStorage.getItem("drop") == null) {
google.script.run.withSuccessHandler(buildDropDowns).buildDropDowns();
} else {
buildDropDowns(sessionStorage.getItem("drop"));
}
if(sessionStorage.getItem("chart") == null) {
google.script.run.withSuccessHandler(getChartData).getChartData("-", "-", "-", "-", "-", "-", "-");
} else {
getChartData(sessionStorage.getItem("chart"));
}
}
function buildDropDowns(JSONObj) {
sessionStorage.setItem("drop", JSONObj);
var dropDownObj = JSON.parse(JSONObj);
// do a bunch of things with the dropDownObj
}
function getChartData(JSONObj) {
sessionStorage.setItem("chart", JSONObj);
var dataObj = JSON.parse(JSONObj);
var rawChartData = dataObj.chartData;
var count = dataObj.count;
var colors = dataObj.colors;
const dateIndices = [3,4];
const chartData = rawChartData.map(function (row) {
return row.map(function (col, index) {
return (dateIndices.indexOf(index) === -1) ? col : new Date(col);
});
});
if(chartData.length == 1) {
document.getElementById("chartDiv").innerHTML = "<b>No projects meet your criteria</b>";
} else {
buildChart(chartData, count, colors);
}
}
//google.charts.load('current', {'packages':['timeline']}); //COMMENT OUT NOW
function buildChart(chartData, count, colors) {
var container = document.getElementById('chartDiv');
//*** EDIT - THIS APPEARS TO BE PROBLEM LINE ***
var chart = new google.visualization.Timeline(container);
//Is there something about visualizations that are different on a reload?
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Project' });
dataTable.addColumn({ type: 'string', id: 'dummy bar label' });
dataTable.addColumn({ type: 'string', role: 'tooltip', p: {'html':true}});
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addColumn({ type: 'number', id: 'sortValue' });
dataTable.addColumn({ type: 'string', id: 'gate' });
dataTable.addRows(chartData);
var options = {
height: Math.min(800, (chartData.length*45) 60),
width:"100%",
};
var view = new google.visualization.DataView(dataTable);
view.setColumns([0, 1, 2, 3, 4]); // exclude column 5 (sequence)
chart.draw(view, options);
}
Now that I am loading the visualization package at page load, and only then calling either the server to get the data if needed, or displaying the data if it's already in sessionData, things seem to be working as I wanted them to.