在隐藏的引导选项卡窗格上重绘Google图表

时间:2014-05-29 11:33:35

标签: javascript jquery twitter-bootstrap google-visualization

我在我的网络应用中使用谷歌散点图,并在文档就绪时加载。我最近做了一些重组,并尝试在不同的引导选项卡中划分页面内容。

如果我在活动选项卡窗格上加载图表,它可以正常工作,图表的宽度,高度参数是正确的。但是,如果我将图形移动到隐藏选项卡,则图形无法正确加载函数传递的参数(特别是宽度和高度)。目前我正在调用文档中的图表。

这是我的代码

HTML

<div class="layout layout-stack-sm layout-main-right">
    <div class="col-sm-3 layout-sidebar">
        <div class="col-sm-12">
            <ul id="myTab" class="nav nav-layout-sidebar nav-stacked">
                <li class="active">
                    <a href="#quick-stats" data-toggle="tab">
                        <i class="fa fa-th"></i>
                        &nbsp;&nbsp;Quick Stats
                    </a>
                </li>
                <li>
                    <a href="#engagement-graph" data-toggle="tab">
                        <i class="fa fa-bar-chart-o"></i>
                        &nbsp;&nbsp;Engagement graph
                    </a>
                </li>
            </ul>
        </div>
    </div>  <!-- layout sidebar -->

    <div class="col-sm-9 layout-main">
        <div class="tab-content stacked-content">
            <div class="tab-pane fade in active" id="quick-stats">
                Some content   
            </div>
            <div class="tab-pane fade" id="engagement-graph">
                <div id="myMoodPage">
                <div class="graph" id="graph">
                </div>
            </div>
        </div>
    </div>
</div>

在文档就绪上加载图表

<script type="text/javascript" src="https://www.google.com/jsapi">
</script>
<script type="text/javascript">
    google.load("visualization", "1", {packages: ["corechart"]});

    $(document).ready(function () {
        $('#myMoodPage').myMood({
            graphDataUrl: '<?php echo $this->url('my_mood/mood_graph_data'); ?>',
            graphData: <?php echo $graphData; ?>,
            titles: ['<?php echo $this->translate('time'); ?>', '<?php echo $this->translate('mood'); ?>', '<?php echo $this->translate('avg'); ?>']
        });
    });

    function updatePageCharts() {
        $('#myMoodPage').myMood('loadGraph');
    }

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

</script>

这是我的主要JS文件,我在这里定义图形参数和其他东西(这个问题实际上并不相关,但只是为了完整性我发布了整个函数)

(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 += "<br>" + 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 += "<br>" + 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种不同情况下的屏幕截图

在活动标签页面上加载 enter image description here

加载隐藏的标签页 enter image description here

2 个答案:

答案 0 :(得分:9)

在隐藏div中绘制图表(通常是实现哪些选项卡界面)会破坏Visualization API中的维度检测算法,这就是为什么图表在除了在开头打开的选项卡之外的任何选项卡中绘制时都会绘制得很奇怪。

您只能加载API一次;随后对Visualization API google.load的调用将被忽略,这就是为什么你的&#34; shown.bs.tab&#34;事件处理程序不能做你想做的事。

由于延迟选项卡初始化直到图表绘制后并不是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加载jsapi的脚本标记后,drawChart对象就会存在,这将在执行此代码之前,因此您将始终在此处触发google函数。但是,让{{1}}对象可用并不意味着加载了Visualization API。

答案 1 :(得分:2)

只是因为其他人正在寻找更简单的解决方案。

另一种方法是实现以下内容:

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

这将导致图表重新绘制,就好像浏览器已调整大小一样。