<link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../iron-ajax/iron-ajax.html"> <link rel="import" href="../google-apis/google-legacy-loader.html"> <!-- `google-chart` encapsulates Google Charts as a web component, allowing you to easily visualize data. From simple line charts to complex hierarchical tree maps, the chart element provides a number of ready-to-use chart types. <google-chart type='pie' options='{"title": "Distribution of days in 2001Q1"}' cols='[{"label":"Month", "type":"string"}, {"label":"Days", "type":"number"}]' rows='[["Jan", 31],["Feb", 28],["Mar", 31]]'> </google-chart> Height and width are specified as style attributes: google-chart { height: 300px; width: 50em; } Data can be provided in one of three ways: - Via the `cols` and `rows` attributes: cols='[{"label":"Mth", "type":"string"}, {"label":"Days", "type":"number"}]' rows='[["Jan", 31],["Feb", 28],["Mar", 31]]' - Via the `data` attribute, passing in the data directly: data='[["Month", "Days"], ["Jan", 31], ["Feb", 28], ["Mar", 31]]' - Via the `data` attribute, passing in the URL to a resource containing the data, in JSON format: data='http://example.com/chart-data.json' @demo --> <dom-module id="google-chart"> <link rel="import" type="css" href="google-chart.css"> <template> <iron-ajax id="ajax" handle-as="json" url="{{data}}" on-response="_externalDataLoaded"></iron-ajax> <div id="chartdiv"></div> <google-legacy-loader on-api-load="_readyForAction"></google-legacy-loader> </template> </dom-module> <script> (function() { "use strict"; Polymer({ is: 'google-chart', /** * Fired when the graph is displayed. * * @event google-chart-render */ /** * Fired when the user makes a selection in the chart. * * @event google-chart-select * @param {object} detail * @param {array} detail.selection The user-defined selection. */ properties: { /** * Sets the type of the chart. * * Should be one of: * - `area`, `bar`, `bubble`, `candlestick`, `column`, `combo`, `geo`, * `histogram`, `line`, `pie`, `scatter`, `stepped-area`, `treemap` * * See <a href="https://google-developers.appspot.com/chart/interactive/docs/gallery">Google Visualization API reference (Chart Gallery)</a> for details. * */ type: { type: String, value: 'column', observer: '_typeChanged' }, /** * Sets the options for the chart. * * Example: * <pre>{ * title: "Chart title goes here", * hAxis: {title: "Categories"}, * vAxis: {title: "Values", minValue: 0, maxValue: 2}, * legend: "none" * };</pre> * See <a href="https://google-developers.appspot.com/chart/interactive/docs/gallery">Google Visualization API reference (Chart Gallery)</a> * for the options available to each chart type. * */ options: { type: Object, value: function() { return {}; } }, /** * Sets the data columns for this object. * * When specifying data with `cols` you must also specify `rows`, and * not specify `data`. * * Example: * <pre>[{label: "Categories", type: "string"}, * {label: "Value", type: "number"}]</pre> * See <a href="https://google-developers.appspot.com/chart/interactive/docs/reference#DataTable_addColumn">Google Visualization API reference (addColumn)</a> * for column definition format. * * @attribute cols * @type array */ cols: { type: Array, value: function() { return []; } }, /** * Sets the data rows for this object. * * When specifying data with `rows` you must also specify `cols`, and * not specify `data`. * * Example: * <pre>[["Category 1", 1.0], * ["Category 2", 1.1]]</pre> * See <a href="https://google-developers.appspot.com/chart/interactive/docs/reference#addrow">Google Visualization API reference (addRow)</a> * for row format. * * @attribute rows * @type array */ rows: { type: Array, value: function() { return []; } }, /** * Sets the entire dataset for this object. * Can be used to provide the data directly, or to provide a URL from * which to request the data. * * The data format can be a two-dimensional array or the DataTable format * expected by Google Charts. * See <a href="https://google-developers.appspot.com/chart/interactive/docs/reference#DataTable">Google Visualization API reference (DataTable constructor)</a> * for data table format details. * * When specifying data with `data` you must not specify `cols` or `rows`. * * Example: * <pre>[["Categories", "Value"], * ["Category 1", 1.0], * ["Category 2", 1.1]]</pre> * * @attribute data * @type array, object, or string */ data: { type: Object, // or array, or object value: function() { return []; } }, /** * Selected datapoint(s) in the map. * * An array of objects, each with a numeric row and/or column property. * `row` and `column` are the zero-based row or column number of an item * in the data table to select. * * To select a whole column, set row to null; * to select a whole row, set column to null. * * Example: * <pre> * [{row:0,column:1}, {row:1, column:null}] * </pre> * * @attribute selection * @type array */ selection: { type: Array, value: function() { return []; }, observer: '_selectionChanged' }, }, observers: [ '_loadData(rows, cols, data)' ], _packages: null, _chartObject: null, _isReady: false, _canDraw: false, _dataTable: null, _chartTypes: null, _readyForAction: function(e, detail, sender) { this._loadPackageByChartType(); google.load("visualization", "1", { packages: this._packages[this.type], callback: function() { this._isReady = true; this._loadChartTypes(); this._loadData(); }.bind(this) }); }, _typeChanged: function() { // Invalidate current chart object. this._chartObject = null; this._loadData(); }, _selectionChanged: function() { if (this._chartObject && this.setSelection) { this._chartObject.setSelection(this.selection); } }, /** * Draws the chart. * * Called automatically on first load and whenever one of the attributes * changes. Can be called manually to handle e.g. page resizes. * * @method drawChart * @return {Object} Returns null. */ drawChart: function() { if (this._canDraw) { if (!this.options) { this.options = {}; } if (!this._chartObject) { var chartClass = this._chartTypes[this.type]; if (chartClass) { this._chartObject = new chartClass(this.$.chartdiv); } } if (this._chartObject) { google.visualization.events.addOneTimeListener(this._chartObject, 'ready', function() { this.fire('google-chart-render'); }.bind(this)); google.visualization.events.addListener(this._chartObject, 'select', function() { this.selection = this._chartObject.getSelection(); this.fire('google-chart-select', { selection: this._chartObject.getSelection() }); }.bind(this)); this._chartObject.draw(this._dataTable, this.options); if (this._chartObject.setSelection){ this._chartObject.setSelection(this.selection); } } else { this.$.chartdiv.innerHTML = 'Undefined chart type'; } } return null; }, /** * Returns the chart serialized as an image URI. * * Call this after the chart is drawn (google-chart-render event). * * @return {string} Returns image URI. */ getImageURI: function() { return this._chartObject.getImageURI(); }, _loadChartTypes: function() { this._chartTypes = { 'area': google.visualization.AreaChart, 'bar': google.visualization.BarChart, 'bubble': google.visualization.BubbleChart, 'candlestick': google.visualization.CandlestickChart, 'column': google.visualization.ColumnChart, 'combo': google.visualization.ComboChart, 'geo': google.visualization.GeoChart, 'histogram': google.visualization.Histogram, 'line': google.visualization.LineChart, 'pie': google.visualization.PieChart, 'scatter': google.visualization.ScatterChart, 'stepped-area': google.visualization.SteppedAreaChart, 'table': google.visualization.Table, 'gauge': google.visualization.Gauge, 'treemap': google.visualization.TreeMap }; }, _loadPackageByChartType: function() { this._packages = { 'area': 'corechart', 'bar': 'corechart', 'bubble': 'corechart', 'candlestick': 'corechart', 'column': 'corechart', 'combo': 'corechart', 'geo': 'corechart', 'histogram': 'corechart', 'line': 'corechart', 'pie': 'corechart', 'scatter': 'corechart', 'stepped-area': 'corechart', 'table': 'table', 'gauge': 'gauge', 'treemap': 'treemap' }; }, _loadData: function() { this._canDraw = false; if (this._isReady) { if (typeof this.data == 'string' || this.data instanceof String) { // Load data asynchronously, from external URL. this.$.ajax.generateRequest(); } else { var dataTable = this._createDataTable(); this._canDraw = true; if (dataTable) { this._dataTable = dataTable; this.drawChart(); } } } }, _externalDataLoaded: function(e) { var dataTable = this._createDataTable(e.detail.response); this._canDraw = true; this._dataTable = dataTable; this.drawChart(); }, _createDataTable: function(data) { var dataTable = null; // If a data object was not passed to this function, default to the // chart's data attribute. Passing a data object is necessary for // cases when the data attribute is a URL pointing to an external // data source. if (!data) { data = this.data; } if (!data) data = []; if (this.rows && this.rows.length > 0 && this.cols && this.cols.length > 0) { // Create the data table from cols and rows. dataTable = new google.visualization.DataTable(); dataTable.cols = this.cols; for (var i = 0; i < this.cols.length; i++) { dataTable.addColumn(this.cols[i]); } dataTable.addRows(this.rows); } else { // Create dataTable from the passed data or the data attribute. // Data can be in the form of raw DataTable data or a two // dimensional array. if (data.rows && data.cols) { dataTable = new google.visualization.DataTable(data); } else if (data.length > 0) { dataTable = google.visualization.arrayToDataTable(data); } } return dataTable; } }); })(); </script>