Перерисовать диаграммы Google на скрытой панели вкладок

Я использую диаграмму рассеяния Google в своем веб-приложении, и она загружается на готовом документе. Недавно я сделал некоторую реструктуризацию и попытался разделить содержимое страницы на разных вкладках начальной загрузки.

Если я загружаю график на панели активных вкладок, он работает нормально, а параметры ширины и высоты графика верны. Однако, если я переношу график на скрытую вкладку, график не загружается должным образом с параметрами, переданными функции (особенно шириной и высотой). На данный момент я нахожу график готового документа.

Вот мой код

HTML

<div><code>
 <div>
 <div>
 <ul id="myTab">
 <li>
 <a href="#quick-stats" data-toggle="tab">
 
   Quick Stats
 </a>
 </li>
 <li>
 <a href="#engagement-graph" data-toggle="tab">
 
   Engagement graph
 </a>
 </li>
 </ul>
 </div>
 </div> <!-- layout sidebar -->
 <div>
 <div>
 <div id="quick-stats">
 Some content 
 </div>
 <div id="engagement-graph">
 <div id="myMoodPage">
 <div id="graph">
 </div>
 </div>
 </div>
 </div>
</div>
</code>
<p>Загрузка графика в готовом документе</p>

<p>Это мой основной JS файл, где я определяю параметры графика и другие вещи (которые на самом деле не актуальны для этого вопроса, но только для полноты я публикую всю функцию)</p>
<pre class="prettyprint linenums">(function ($) {
 $.widget("ui.myMood", {
 options: {
 graphDataUrl: "",
 graphData: {},
 titles: ["time", "mood", "avg"]
 },
 _create: function () {
 var self = this;
 this.periodSelect = this.element.find('select[name="period"]');
 this.questionSelect = this.element.find('select[name="question"]');
 this.graphContainer = this.element.find(".statistics");
 this.atStart = this.element.find("#atStart .value");
 this.highest = this.element.find("#highest .value");
 this.lowest = this.element.find("#lowest .value");
 this.current = this.element.find("#current .value");
 this.last30 = this.element.find("#last30 .value");
 this.last30Trend = this.element.find("#last30");
 this.week = this.element.find("#week .value");
 this.weekTrend = this.element.find("#week");
 this.graphId = "graph";
 this.selectedFilterName = this.options.titles[2];
 this.periodSelect.change(function () {
 self.loadGraph()
 });
 this.questionSelect.change(function () {
 self.loadGraph();
 self.selectedFilterName = self.questionSelect.find("option:selected").text()
 });
 this.setGraphData(this.options.graphData);
 return this
 },
 loadGraph: function () {
 var self = this;
 var data = {
 period: self.periodSelect.val(),
 question: self.questionSelect.val()
 };
 var success = function (data) {
 self.setGraphData(data)
 };
 $.ajax({
 type: "POST",
 url: self.options.graphDataUrl,
 data: data,
 success: success
 })
 },
 setGraphData: function (data) {
 var self = this;
 this.atStart.text(data.atStart);
 this.highest.text(data.highest);
 this.lowest.text(data.lowest);
 this.current.text(data.current);
 this.last30.text(data.last30.val);
 this.last30Trend.find(".up, .down").hide();
 this.last30Trend.removeClass("incr");
 this.last30Trend.removeClass("decr");
 if (Number(data.last30.trend) > 0) {
 this.last30Trend.find(".up").show();
 this.last30Trend.addClass("incr")
 }
 if (Number(data.last30.trend) < 0) {
 this.last30Trend.find(".down").show();
 this.last30Trend.addClass("decr")
 }
 this.week.text(data.week.val);
 this.weekTrend.find(".up, .down").hide();
 this.weekTrend.removeClass("incr");
 this.weekTrend.removeClass("decr");
 if (Number(data.week.trend) > 0) {
 this.weekTrend.find(".up").show();
 this.weekTrend.addClass("incr")
 }
 if (Number(data.week.trend) < 0) {
 this.weekTrend.find(".down").show();
 this.weekTrend.addClass("decr")
 }
 var moodData = data.mood;
 var dataArray = new Array();
 for (i in moodData) {
 if (moodData[i].avg == null) {
 moodData[i].avg = undefined
 }
 if (moodData[i].mood == null) {
 moodData[i].mood = undefined
 }
 if (moodData[i].team == null) {
 moodData[i].team = undefined
 }
 var moodText = '<div style="border-radius: 2px; padding: 10px; color: #ffffff; font-weight: bold; background-color: #9c3b8a;">';
 if (moodData[i].event != null) {
 moodText += moodData[i].event + " "
 }
 moodText += moodData[i].mood;
 if (moodData[i].comment) {
 moodText += "" + moodData[i].comment
 }
 moodText += "</div>";
 var avgText = '<div style="border-radius: 2px; padding: 10px; color: #ffffff; font-weight: bold; background-color: #15426c;">';
 if (moodData[i].event != null) {
 avgText += moodData[i].event + " "
 }
 avgText += moodData[i].avg;
 if (moodData[i].publicComment) {
 avgText += "" + moodData[i].publicComment
 }
 avgText += "</div>";
 var row = [new Date(moodData[i].time * 1000), Number(moodData[i].mood), moodText, Number(moodData[i].avg), avgText, false];
 dataArray.push(row)
 }
 if (google) {
 drawChart()
 } else {
 google.setOnLoadCallback(drawChart)
 }
 function drawChart() {
 var dataTable = new google.visualization.DataTable();
 dataTable.addColumn("date", self.options.titles[0]);
 dataTable.addColumn("number", self.options.titles[1]);
 dataTable.addColumn({
 type: "string",
 role: "tooltip",
 p: {
 html: true
 }
 });
 dataTable.addColumn("number", self.selectedFilterName);
 dataTable.addColumn({
 type: "string",
 role: "tooltip",
 p: {
 html: true
 }
 });
 dataTable.addColumn({
 type: "boolean",
 role: "certainty"
 });
 dataTable.addRows(dataArray);
 var dataView = new google.visualization.DataView(dataTable);
 var chart = new google.visualization.ScatterChart(document.getElementById(self.graphId));
 var options = {
 legend: {
 position: "bottom"
 },
 interpolateNulls: true,
 chartArea: {
 left: 25,
 top: 25,
 width: '92%',
 height: '80%'
 },
 backgroundColor: "#ffffff",
 vAxis: {
 minValue: 0,
 maxValue: 6,
 viewWindow: {
 min: 0,
 max: 6
 }
 },
 tooltip: {
 isHtml: true
 },
 series: {
 0: {
 color: "#9c3b8a",
 lineWidth: 2,
 pointSize: 6
 },
 1: {
 color: "#15426c",
 lineWidth: 1,
 pointSize: 3
 }
 }
 };
 chart.draw(dataView, options)
 }
 }
 })
})(jQuery);

Я попытался перезагрузить график, как это, но это не сработало

$("a[href='#engagement-graph']").on('shown.bs.tab', function (e) {
 google.load('visualization', '1', {
 packages: ['corechart'],
 callback: drawChart
 });
 });

Снимок экрана в 2 разных ситуациях

Загрузка на активной вкладке

Загрузка на скрытую вкладку

2 ответа

Чертежные диаграммы внутри скрытых div (как правило, реализуются как интерфейсы вкладок) разрывают алгоритмы определения размеров в API визуализации, поэтому диаграмма рисуется странно, когда она отображается на любой вкладке, отличной от открытой в начале вкладки.

Вы можете загрузить API только один раз; последующие вызовы google.load для API визуализации будут проигнорированы, поэтому ваш обработчик событий "показан .bs.tab" не делает то, что вы хотите.

Поскольку задержка инициализации вкладки до тех пор, пока рисование диаграмм не является действительно жизнеспособным вариантом с Bootstrap, я бы предложил заменить этот код:

google.load("visualization", "1", {packages: ["corechart"]});
$("a[href='#engagement-graph']").on('shown.bs.tab', function (e) {
 google.load('visualization', '1', {
 packages: ['corechart'],
 callback: drawChart
 });
});

с этим:

function init () {
 if (/* boolean expression teting if chart tab is open */) {
 drawChart();
 }
 else {
 $("a[href='#engagement-graph']").one('shown.bs.tab', drawChart);
 }
}
google.load("visualization", "1", {packages: ["corechart"], callback: init});

Это должно сделать диаграмму, если ее вкладка контейнера открыта, и создать одноразовый обработчик событий, который рисует диаграмму при первом открытии вкладки, если вкладка не открыта.

Вам нужно поместить этот код внутри функции setGraphData, чтобы избежать состояния гонки при загрузке данных, и поэтому функция drawChart будет в области видимости. Также удалите эти строки:

if (google) {
 drawChart()
} else {
 google.setOnLoadCallback(drawChart)
}

Объект google существует, как только тег script для загрузок jsapi, который будет перед тем, как этот код будет выполнен, поэтому вы всегда будете запускать функцию drawChart здесь. Однако наличие доступного объекта google не означает, что загружен API визуализации.


Просто помогите другим людям найти более простое решение.

Альтернативой может быть следующее:

$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
 var ev = document.createEvent('Event');
 ev.initEvent('resize', true, true);
 window.dispatchEvent(ev);
 });

Это заставит диаграмму повторно рисовать, как если бы браузер был изменен.

licensed under cc by-sa 3.0 with attribution.