From 067754f0f6100b0b2a4bc627dbafdaf3955c8102 Mon Sep 17 00:00:00 2001 From: Adrien Dieudonne Date: Wed, 19 Sep 2018 11:42:06 +0200 Subject: [PATCH] [REF] web: Use graph renderer to render pie_chart widget Before this rev., the pie_chart widget was rendered from scratch with nv.d3 and we had duplicated code. In comparison mode, two widgets were instancied and it was a bit difficult to synchronize the size of these svg graphs. nv.d3 graphs are attached when the dom is ready because it needs to calculate the available space. UI bugs can appear if the dom changes... .update() must be executed on the graph to avoid this as we already do in graph renderer. So now, the rendering of the pie_chart widget is done by the graph renderer. As comparaison mode is supported by the rendering, only one widget needs to be instancied. Task ID: 1835644 --- addons/web/__manifest__.py | 1 + addons/web/static/src/js/widgets/pie_chart.js | 202 +++++------------- addons/web/static/src/xml/pie_chart.xml | 10 + 3 files changed, 60 insertions(+), 153 deletions(-) create mode 100644 addons/web/static/src/xml/pie_chart.xml diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index bbfe3a134a0..e14aa45c82e 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -25,6 +25,7 @@ This module provides the core of the Odoo Web Client. "static/src/xml/base.xml", "static/src/xml/kanban.xml", "static/src/xml/menu.xml", + "static/src/xml/pie_chart.xml", "static/src/xml/rainbow_man.xml", "static/src/xml/report.xml", "static/src/xml/web_calendar.xml", diff --git a/addons/web/static/src/js/widgets/pie_chart.js b/addons/web/static/src/js/widgets/pie_chart.js index def6147fb05..85e4a8121ec 100644 --- a/addons/web/static/src/js/widgets/pie_chart.js +++ b/addons/web/static/src/js/widgets/pie_chart.js @@ -4,187 +4,83 @@ odoo.define('web.PieChart', function (require) { /** * This widget render a Pie Chart. It is used in the dashboard view. */ -var ajax = require('web.ajax'); -var config = require('web.config'); + +var core = require('web.core'); +var Domain = require('web.Domain'); +var viewRegistry = require('web.view_registry'); var Widget = require('web.Widget'); var widgetRegistry = require('web.widget_registry'); -var GROUPABLE_TYPES = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime']; +var qweb = core.qweb; var PieChart = Widget.extend({ className: 'o_pie_chart', - cssLibs: [ - '/web/static/lib/nvd3/nv.d3.css' - ], - jsLibs: [ - '/web/static/lib/nvd3/d3.v3.js', - '/web/static/lib/nvd3/nv.d3.js', - '/web/static/src/js/libs/nvd3.js' - ], + xmlDependencies: ['/web/static/src/xml/pie_chart.xml'], /** - * override - * + * @override * @param {Widget} parent * @param {Object} record - * @param {Object} node + * @param {Object} node node from arch */ init: function (parent, record, node) { - this._super(parent); + this._super.apply(this, arguments); - this.record = record; - this.model = record.model; + var modifiers = node.attrs.modifiers; + var domain = record.domain.concat( + Domain.prototype.stringToArray(modifiers.domain || '[]')); + var arch = qweb.render('web.PieChart', { + modifiers: modifiers, + }); - if (node.attrs.modifiers) { - this.measure = node.attrs.modifiers.measure || ''; - this.measureField = this.record.fields[this.measure]; - this.groupBy = node.attrs.modifiers.groupby.split(':')[0] || ''; - this.interval = node.attrs.modifiers.groupby.split(':')[1]; + this.subViewParams = { + context: record.context, + domain: domain, + groupBy: [], + modelName: record.model, + withControlPanel: false, + isEmbedded: true, + mode: 'pie', + title: modifiers.title || modifiers.measure || '', + }; - if (!_.contains(Object.keys(this.record.fields), this.groupBy)) { - return; - } - - this.groupByField = this.record.fields[this.groupBy]; - this.groupByType = this.groupByField.type; - this.title = node.attrs.modifiers.title || this.measure || ''; - this.trueLabel = node.attrs.modifiers.trueLabel || 'True'; - this.falseLabel = node.attrs.modifiers.falseLabel || 'False'; - } + this.viewInfo = { + arch: arch, + fields: record.fields, + viewFields: record.fieldsInfo.dashboard, + }; }, /** - * override + * Instantiates the pie chart view and starts the graph controller. + * + * @override */ willStart: function () { var self = this; - this.data = []; + var def1 = this._super.apply(this, arguments); - var query = { - model: this.model, - method: 'read_group', - domain: [], - groupBy: [this.groupBy + (this.interval ? ':' + this.interval : '')], - fields: [this.measure], - lazy: false, - }; - - // Handle case where measure attribute is missing. - query.fields = this.measure ? [this.measure] : []; - - return $.when(this._rpc(query), ajax.loadLibs(this)).then( - function (result) { - if (result) { - if (_.contains(GROUPABLE_TYPES, self.groupByType)) { - for (var i = 0; i < result.length; i++) { - var value = result[i][self.measure]; - var label = undefined; - var interval = self.interval ? ':' + self.interval : ''; - var groupby = self.groupBy + interval; - - switch (self.groupByType) { - case 'boolean': - label = ['False', 'True'][i]; - break; - case 'many2one': - label = result[i][groupby][1]; - break; - default: - label = result[i][groupby]; - } - - self.data.push({ - 'label': label, - 'value': value, - }); - } - } - } - }); - }, - - /** - * This method ensure that Pie Chart is attached to DOM before being rendered. - * - * If we don't do that, Pie Chart can be rendered without being aware of the - * size of its container, it is then rendered with a size of 0 0 and remains - * invisible (even with a good lens). - */ - on_attach_callback: function() { - this._render(); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - /** - * @private - */ - _render: function () { - // Note: The rendering of this widget is aynchronous as NVD3 does a - // setTimeout(0) before executing the callback given to addGraph. - var self = this; - if(!this.data || !_.isArray(this.data)) { - return; - } - - var $label = this._renderLabel(this.title); - this.$el.empty(); - $label.appendTo(this.$el); - $('').appendTo(this.$el); - - var legend_right = config.device.size_class > config.device.SIZES.XS; - var legendPosition = legend_right ? 'right' : 'top'; - var color = d3.scale.category10().range(); - - nv.addGraph(function () { - self.chart = nv.models.pieChart() - .x(function(d) { return d.label }) - .y(function(d) { return d.value }) - .legendPosition(legendPosition) - .labelType('percent') - .showLabels(true) - .showLegend(true) - .color(color); - - self.chart.legend.rightAlign(false); - self.chart.legend.align(true); - self.chart.legend.expanded(true); - - d3.select(self.$('svg')[0]) - .datum(self.data) - .transition().duration(0) - .call(self.chart); - self.chart.update(); + var SubView = viewRegistry.get('graph'); + var subView = new SubView(this.viewInfo, this.subViewParams); + var def2 = subView.getController(this).then(function (controller) { + self.controller = controller; + return self.controller.appendTo(document.createDocumentFragment()); }); + return $.when(def1, def2); }, /** - * @private - * @param {any} value - * @param {any} field - * @returns {string} + * @override */ - _getNumberedValue: function (value, field) { - var id = value[0]; - var name = value[1]; - this.numbering[field] = this.numbering[field] || {}; - this.numbering[field][name] = this.numbering[field][name] || {}; - var numbers = this.numbering[field][name]; - numbers[id] = numbers[id] || _.size(numbers) + 1; - return name + (numbers[id] > 1 ? " (" + numbers[id] + ")" : ""); + start: function () { + this.$el.append(this.controller.$el); + return this._super.apply(this, arguments); }, - /** - * Renders a pie chart's label. + /** + * Call `on_attach_callback` for each subview * - * @private - * @param {String} title - * @returns {jQueryElement} + * @override */ - _renderLabel: function (title) { - var $result = $('